tarantool16 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,47 @@
1
+ require 'tarantool16/errors'
2
+ require 'tarantool16/response'
3
+
4
+ module Tarantool16
5
+ module Connection
6
+ class Option
7
+ attr :sync, :error, :data
8
+ def initialize(sync, err, data)
9
+ @sync = sync
10
+ @error = err
11
+ @data = data
12
+ end
13
+
14
+ def ok?
15
+ !@error
16
+ end
17
+
18
+ def raise_if_error!
19
+ raise @error if @error
20
+ end
21
+
22
+ def self.ok(sync, code, data)
23
+ if code == 0
24
+ new(sync, nil, data)
25
+ else
26
+ new(sync, ::Tarantool16::DBError.with_code_message(code, data), nil)
27
+ end
28
+ end
29
+
30
+ def self.error(sync, err, message = nil)
31
+ if err.is_a? Class
32
+ err = err.new message
33
+ end
34
+ new(sync, err, nil)
35
+ end
36
+
37
+ def inspect
38
+ s = @sync ? " sync=#{sync}" : ""
39
+ if ok?
40
+ "<Option#{s} data=#{@data.inspect}>"
41
+ else
42
+ "<Option#{s} error=#{@error.inspect}>"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,94 @@
1
+ module Tarantool16
2
+ IPROTO_CODE = 0x00
3
+ IPROTO_SYNC = 0x01
4
+ IPROTO_SPACE_ID = 0x10
5
+ IPROTO_INDEX_ID = 0x11
6
+ IPROTO_LIMIT = 0x12
7
+ IPROTO_OFFSET = 0x13
8
+ IPROTO_ITERATOR = 0x14
9
+ IPROTO_KEY = 0x20
10
+ IPROTO_TUPLE = 0x21
11
+ IPROTO_FUNCTION_NAME = 0x22
12
+ IPROTO_USER_NAME = 0x23
13
+ IPROTO_DATA = 0x30
14
+ IPROTO_ERROR = 0x31
15
+
16
+ IPROTO_GREETING_SIZE = 128
17
+
18
+ REQUEST_TYPE_OK = 0
19
+ REQUEST_TYPE_PING = 64
20
+ REQUEST_TYPE_SELECT = 1
21
+ REQUEST_TYPE_INSERT = 2
22
+ REQUEST_TYPE_REPLACE = 3
23
+ REQUEST_TYPE_UPDATE = 4
24
+ REQUEST_TYPE_DELETE = 5
25
+ REQUEST_TYPE_CALL = 6
26
+ REQUEST_TYPE_AUTHENTICATE = 7
27
+ REQUEST_TYPE_ERROR = 1 << 15
28
+
29
+
30
+ SPACE_SCHEMA = 272
31
+ SPACE_SPACE = 280
32
+ SPACE_INDEX = 288
33
+ SPACE_FUNC = 296
34
+ SPACE_USER = 304
35
+ SPACE_PRIV = 312
36
+ SPACE_CLUSTER = 320
37
+
38
+ INDEX_SPACE_PRIMARY = 0
39
+ INDEX_SPACE_NAME = 2
40
+ INDEX_INDEX_PRIMARY = 0
41
+ INDEX_INDEX_NAME = 2
42
+
43
+ ITERATOR_EQ = 0
44
+ ITERATOR_REQ = 1
45
+ ITERATOR_ALL = 2
46
+ ITERATOR_LT = 3
47
+ ITERATOR_LE = 4
48
+ ITERATOR_GE = 5
49
+ ITERATOR_GT = 6
50
+ ITERATOR_BITS_ALL_SET = 7
51
+ ITERATOR_BITS_ANY_SET = 8
52
+ ITERATOR_BITS_ALL_NOT_SET = 9
53
+ ITERATOR_RTREE_OVERLAPS = 10
54
+ ITERATOR_RTREE_NEIGHBOR = 11
55
+
56
+ Iterators = {}
57
+ [
58
+ [ITERATOR_EQ, %w[eq ==]],
59
+ [ITERATOR_REQ, %w[req rev ==<]],
60
+ [ITERATOR_ALL, %w[all *]],
61
+ [ITERATOR_LT, %w[< lt]],
62
+ [ITERATOR_LE, %w[<= le]],
63
+ [ITERATOR_GE, %w[>= ge]],
64
+ [ITERATOR_GT, %w[> gt]],
65
+ [ITERATOR_BITS_ALL_SET, %w[ball &=]],
66
+ [ITERATOR_BITS_ANY_SET, %w[bany &]],
67
+ [ITERATOR_BITS_ALL_NOT_SET, %w[bnotany !&]],
68
+ [ITERATOR_RTREE_OVERLAPS, %w[roverlaps here &&]],
69
+ [ITERATOR_RTREE_NEIGHBOR, %w[rneighbor near <->]],
70
+ ].each do |it, names|
71
+ names.each do |name|
72
+ Iterators[it] = it
73
+ Iterators[name] = it
74
+ Iterators[name.to_sym] = it
75
+ end
76
+ end
77
+ Iterators[nil] = ITERATOR_EQ
78
+ def self.iter(iter)
79
+ unless it = Iterators[iter]
80
+ raise "Unknown iterator #{iter.inspect}"
81
+ end
82
+ it
83
+ end
84
+
85
+ # Default value for socket timeout (seconds)
86
+ SOCKET_TIMEOUT = nil
87
+ # Default maximum number of attempts to reconnect
88
+ RECONNECT_MAX_ATTEMPTS = 10
89
+ # Default delay between attempts to reconnect (seconds)
90
+ RECONNECT_DELAY = 0.1
91
+ # Number of reattempts in case of server
92
+ # return completion_status == 1 (try again)
93
+ RETRY_MAX_ATTEMPTS = 10
94
+ end
@@ -0,0 +1,198 @@
1
+ require_relative 'schema'
2
+ module Tarantool16
3
+ class DB
4
+ attr :conn
5
+
6
+ def initialize(host, opts = {})
7
+ @host = host
8
+ @opts = opts.dup
9
+ @future = nil
10
+ @spaces = nil
11
+ @defined_fields = {}
12
+ _fill_standard_spaces
13
+ @conn = self.class::Connection.new(@host, @opts)
14
+ end
15
+
16
+ def define_fields(sid, fields)
17
+ sid = sid.to_s if sid.is_a?(Symbol)
18
+ @defined_fields[sid] = fields
19
+ if @spaces && (sp = @spaces[sid])
20
+ if sp.sid && sp.name && !sp.name.empty?
21
+ rf1 = @defined_fields[sp.sid]
22
+ rf2 = @defined_fields[sp.name]
23
+ if rf1 && rf2 && rf1 != rf2
24
+ raise "Misconfigured defined fields for #{sp.name_sid}"
25
+ end
26
+ end
27
+ sp.fields = fields
28
+ end
29
+ end
30
+
31
+ def _fill_standard_spaces
32
+ rf = @defined_fields
33
+ rf[SPACE_INDEX] =
34
+ [%w{sid num}, %w{iid num}, %w{name str},
35
+ %w{type str}, %w{unique num}, %w{part_count num},
36
+ {name: 'parts', type: [:num, :str], tail: true}]
37
+ end
38
+
39
+ def _synchronized
40
+ raise "Override #_synchronized"
41
+ end
42
+
43
+ UNDEF = Object.new.freeze
44
+ def _with_space(name, cb)
45
+ future = @future || _space_future
46
+ future.then_blk do |r|
47
+ unless r.ok?
48
+ cb.call r
49
+ else
50
+ sps = r.data
51
+ sp = sps[name]
52
+ if sp.nil? && Symbol == name
53
+ sp = sps[name.to_s]
54
+ sps[name] = sp unless sp.nil?
55
+ end
56
+ if sp.nil?
57
+ cb.call Option.error(SchemaError, "space #{name} not found")
58
+ else
59
+ yield sp
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def _space_future
66
+ _synchronized do
67
+ return @future if @future
68
+ future = @future = self.class::SchemaFuture.new
69
+ fill_indexes = nil
70
+ spaces = nil
71
+ fill_spaces = lambda do|r|
72
+ unless r.ok?
73
+ future.set r
74
+ _synchronized do
75
+ @future = nil
76
+ end
77
+ else
78
+ _synchronized do
79
+ _fill_spaces(r.data)
80
+ spaces = @spaces
81
+ _select(SPACE_INDEX, 0, [], 0, 2**30, :all, false, fill_indexes)
82
+ end
83
+ end
84
+ end
85
+ fill_indexes = lambda do |r|
86
+ unless r.ok?
87
+ future.set r
88
+ _synchronized do
89
+ @future = nil
90
+ @spaces = nil
91
+ end
92
+ else
93
+ _synchronized do
94
+ _fill_indices(spaces, r.data)
95
+ future.set Option.ok(spaces)
96
+ end
97
+ end
98
+ end
99
+ _select(SPACE_SPACE, 0, [], 0, 2**30, :all, false, fill_spaces)
100
+ return future
101
+ end
102
+ end
103
+
104
+ def _fill_spaces(rows)
105
+ @spaces = {}
106
+ rows.each do |row|
107
+ fields = @defined_fields[row[0]] || @defined_fields[row[2]] || row[6]
108
+ sp = SchemaSpace.new(row[0], row[2], fields)
109
+ @spaces[row[0]] = sp
110
+ @spaces[sp.name] = sp
111
+ @spaces[sp.name.to_sym] = sp
112
+ end
113
+ end
114
+
115
+ def _fill_indices(spaces, rows)
116
+ rows.
117
+ map{|row| [row[0], [row[2], row[1], row[3], 6.step(row.size-1, 2).map{|i| row[i]}]]}.
118
+ group_by{|sid, _| sid}.
119
+ each do |sid, inds|
120
+ sp = spaces[sid]
121
+ sp.indices = inds.map{|_sid, ind| ind}
122
+ end
123
+ end
124
+
125
+ def _insert(sno, tuple, need_hash, cb)
126
+ if !need_hash && sno.is_a?(Integer) && tuple.is_a?(Array)
127
+ return conn._insert(sno, tuple, cb)
128
+ end
129
+ _with_space(sno, cb) do |sp|
130
+ _tuple = tuple.is_a?(Hash) ? sp.map_tuple(tuple) : tuple
131
+ _cb = need_hash ? sp.wrap_cb(cb) : cb
132
+ conn._insert(sp.sid, _tuple, _cb)
133
+ end
134
+ end
135
+
136
+ def _replace(sno, tuple, need_hash, cb)
137
+ if !need_hash && sno.is_a?(Integer) && tuple.is_a?(Array)
138
+ return conn._replace(sno, tuple, cb)
139
+ end
140
+ _with_space(sno, cb) do |sp|
141
+ _tuple = tuple.is_a?(Hash) ? sp.map_tuple(tuple) : tuple
142
+ _cb = need_hash ? sp.wrap_cb(cb) : cb
143
+ conn.insert(sp.sid, _tuple, _cb)
144
+ end
145
+ end
146
+
147
+ def _delete(sno, ino, key, need_hash, cb)
148
+ ino = 0 if ino.nil? && key.is_a?(Array)
149
+ if !need_hash && sno.is_a?(Integer) && ino.is_a?(Integer) && key.is_a?(Array)
150
+ return conn._delete(sno, ino, key, cb)
151
+ end
152
+ _with_space(sno, cb) do |sp|
153
+ sp.get_ino(ino, key, ITERATOR_EQ, cb) do |_ino, _key|
154
+ _cb = need_hash ? sp.wrap_cb(cb) : cb
155
+ conn._delete(sp.sid, _ino, _key, _cb)
156
+ end
157
+ end
158
+ end
159
+
160
+ def _select(sno, ino, key, offset, limit, iterator, need_hash, cb)
161
+ key = [] if key.nil?
162
+ ino = 0 if ino.nil? && key.is_a?(Array)
163
+ iterator = ::Tarantool16.iter(iterator) unless iterator.is_a?(Integer)
164
+ if sno.is_a?(Integer) && ino.is_a?(Integer) && (key.is_a?(Array) || key.nil?)
165
+ return conn._select(sno, ino, key, offset, limit, iterator, cb)
166
+ end
167
+ _with_space(sno, cb) do |sp|
168
+ sp.get_ino(ino, key, iterator, cb) do |_ino, _key|
169
+ _cb = need_hash ? sp.wrap_cb(cb) : cb
170
+ conn._select(sp.sid, _ino, _key, offset, limit, iterator, _cb)
171
+ end
172
+ end
173
+ end
174
+
175
+ def _update(sno, ino, key, ops, need_hash, cb)
176
+ ino = 0 if ino.nil? && key.is_a?(Array)
177
+ ops_good = ops.is_a?(Array) && ops.all?{|a| ops[1].is_a?(Integer)}
178
+ if sno.is_a?(Integer) && ino.is_a?(Integer) && key.is_a?(Array) && ops_good
179
+ return conn._update(sno, ino, key, ops, cb)
180
+ end
181
+ _with_space(sno, cb) do |sp|
182
+ sp.get_ino(ino, key, ITERATOR_EQ, cb) do |_ino, _key|
183
+ _ops = ops_good ? ops : sp.map_ops(ops)
184
+ _cb = need_hash ? sp.wrap_cb(cb) : cb
185
+ conn._update(sp.sid, _ino, _key, _ops, _cb)
186
+ end
187
+ end
188
+ end
189
+
190
+ def _call(name, args, cb)
191
+ conn._call(name, args, cb)
192
+ end
193
+
194
+ def _ping(cb)
195
+ conn._ping(cb)
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,118 @@
1
+ require_relative 'db'
2
+ require_relative 'connection/dumb'
3
+
4
+ module Tarantool16
5
+ class DumbDB < DB
6
+ Connection = Tarantool16::Connection::Dumb
7
+
8
+ RETURN_OR_RAISE = lambda{|r|
9
+ raise r.error unless r.ok?
10
+ r.data
11
+ }
12
+ RETURN_ONE_OR_RAISE = lambda{|r|
13
+ raise r.error unless r.ok?
14
+ r.data[0]
15
+ }
16
+ HUGE_LIMIT = 2**30
17
+
18
+ def select(sno, key, opts={})
19
+ ino = opts[:index]
20
+ offset = opts[:offset] || 0
21
+ limit = opts[:limit] || 2**30
22
+ iterator = opts[:iterator]
23
+ need_hash = opts[:hash]
24
+ key = case key
25
+ when nil
26
+ []
27
+ when Array
28
+ key
29
+ when Hash
30
+ need_hash = true
31
+ key
32
+ else
33
+ [key]
34
+ end
35
+ _select(sno, ino, key, offset, limit, iterator, need_hash, RETURN_OR_RAISE)
36
+ end
37
+
38
+ def get(sno, key, opts={})
39
+ ino = opts[:index]
40
+ iterator = opts[:iterator]
41
+ need_hash = opts[:hash]
42
+ key = case key
43
+ when nil
44
+ []
45
+ when Array
46
+ key
47
+ when Hash
48
+ need_hash = true
49
+ key
50
+ else
51
+ [key]
52
+ end
53
+ _select(sno, ino, key, 0, 1, iterator, need_hash, RETURN_ONE_OR_RAISE)
54
+ end
55
+
56
+ def insert(sno, tuple, opts = {})
57
+ need_hash = opts[:hash] || tuple.is_a?(Hash)
58
+ _insert(sno, tuple, need_hash, RETURN_OR_RAISE)
59
+ end
60
+
61
+ def replace(sno, tuple, opts = {})
62
+ need_hash = opts[:hash] || tuple.is_a?(Hash)
63
+ _replace(sno, tuple, need_hash, RETURN_OR_RAISE)
64
+ end
65
+
66
+ def delete(sno, key, opts = {})
67
+ ino = opts[:index]
68
+ need_hash = opts[:hash] || tkey.is_a?(Hash)
69
+ _delete(sno, ino, key, need_hash, RETURN_OR_RAISE)
70
+ end
71
+
72
+ def update(sno, key, ops, opts = {})
73
+ ino = opts[:index]
74
+ need_hash = opts[:hash] || key.is_a?(Hash)
75
+ _update(sno, ino, key, ops, need_hash, RETURN_OR_RAISE)
76
+ end
77
+
78
+ def _synchronized
79
+ yield
80
+ end
81
+
82
+ class SchemaFuture
83
+ UNDEF = Object.new.freeze
84
+ def initialize
85
+ @r = UNDEF
86
+ @cb = nil
87
+ end
88
+ def then(cb)
89
+ unless @r.equal? UNDEF
90
+ return cb.call(@r)
91
+ end
92
+ if @cb
93
+ raise "Blocking future accepts only 1 callback"
94
+ end
95
+ @cb = cb
96
+ end
97
+
98
+ def then_blk
99
+ unless @r.equal? UNDEF
100
+ return yield @r
101
+ end
102
+ if @cb
103
+ raise "Blocking future accepts only 1 callback"
104
+ end
105
+ @cb = lambda{|r| yield r}
106
+ end
107
+
108
+ def set(r)
109
+ @r = r
110
+ if cb = @cb
111
+ @cb = nil
112
+ cb.call(r)
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+ end