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,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