line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package MR::Tarantool::Box; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=pod |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
MR::Tarantool::Box - A driver for an efficient Tarantool/Box NoSQL in-memory storage. |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 SYNOPSIS |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my $box = MR::Tarantool::Box->new({ |
12
|
|
|
|
|
|
|
servers => "127.0.0.1:33013", |
13
|
|
|
|
|
|
|
name => "My Box", # mostly used for debug purposes |
14
|
|
|
|
|
|
|
spaces => [ { |
15
|
|
|
|
|
|
|
indexes => [ { |
16
|
|
|
|
|
|
|
index_name => 'idx1', |
17
|
|
|
|
|
|
|
keys => [0], |
18
|
|
|
|
|
|
|
}, { |
19
|
|
|
|
|
|
|
index_name => 'idx2', |
20
|
|
|
|
|
|
|
keys => [1,2], |
21
|
|
|
|
|
|
|
}, ], |
22
|
|
|
|
|
|
|
space => 1, # space id, as set in Tarantool/Box config |
23
|
|
|
|
|
|
|
name => "primary", # self-descriptive space-id |
24
|
|
|
|
|
|
|
format => "QqLlSsCc&$", # pack()-compatible, Qq must be supported by perl itself, |
25
|
|
|
|
|
|
|
# & stands for byte-string, $ stands for utf8 string. |
26
|
|
|
|
|
|
|
default_index => 'idx1', |
27
|
|
|
|
|
|
|
fields => [qw/ id f2 field3 f4 f5 f6 f7 f8 misc_string /], # turn each tuple into hash, field names according to format |
28
|
|
|
|
|
|
|
}, { |
29
|
|
|
|
|
|
|
#... |
30
|
|
|
|
|
|
|
} ], |
31
|
|
|
|
|
|
|
default_space => "primary", |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
timeout => 1.0, # seconds |
34
|
|
|
|
|
|
|
retry => 3, |
35
|
|
|
|
|
|
|
debug => 9, # output to STDERR some debugging info |
36
|
|
|
|
|
|
|
raise => 0, # dont raise an exception in case of error |
37
|
|
|
|
|
|
|
}); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my $bool = $box->Insert(1, 2,3, 4,5,6,7,8,"asdf") or die $box->ErrorStr; |
40
|
|
|
|
|
|
|
my $bool = $box->Insert(2, 2,4, 4,5,6,7,8,"asdf",{space => "primary"}) or die $box->ErrorStr; |
41
|
|
|
|
|
|
|
my $tuple = $box->Insert(3, 3,3, 4,5,6,7,8,"asdf",{want_inserted_tuple => 1}) or die $box->ErrorStr; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# Select by single-field key |
44
|
|
|
|
|
|
|
my $tuple = $box->Select(1); # scalar context - scalar result: $tuple |
45
|
|
|
|
|
|
|
my @tuples = $box->Select(1,2,3); # list context - list result: ($tuple, $tuple, ...) |
46
|
|
|
|
|
|
|
my $tuples = $box->Select([1,2,3],{space => "primary", use_index => "idx1"}); # arrayref result: [$tuple, $tuple, ...] |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Select by multi-field key |
49
|
|
|
|
|
|
|
my $tuples = $box->Select([[2,3]],{use_index => "idx2"}); # by full key |
50
|
|
|
|
|
|
|
my $tuples = $box->Select([[2]] ,{use_index => "idx2"}); # by partial key |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my $bool = $box->UpdateMulti(1,[ f4 => add => 3 ]); |
53
|
|
|
|
|
|
|
my $bool = $box->UpdateMulti(2,[ f4 => add => 3 ],{space => "primary"}); |
54
|
|
|
|
|
|
|
my $tuple = $box->UpdateMulti(3,[ f4 => add => 3 ],{want_updated_tuple => 1}); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
my $bool = $box->Delete(1); |
57
|
|
|
|
|
|
|
my $tuple = $box->Delete(2, {want_deleted_tuple => 1}); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=head1 DESCRIPTION |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head2 METHODS |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=cut |
64
|
|
|
|
|
|
|
|
65
|
1
|
|
|
1
|
|
2148
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
35
|
|
66
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
33
|
|
67
|
1
|
|
|
1
|
|
5
|
use Scalar::Util qw/looks_like_number/; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
60
|
|
68
|
1
|
|
|
1
|
|
1873
|
use List::MoreUtils qw/each_arrayref zip/; |
|
1
|
|
|
|
|
3811
|
|
|
1
|
|
|
|
|
243
|
|
69
|
1
|
|
|
1
|
|
16
|
use Time::HiRes qw/sleep/; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
13
|
|
70
|
1
|
|
|
1
|
|
9003
|
use Encode; |
|
1
|
|
|
|
|
24107
|
|
|
1
|
|
|
|
|
122
|
|
71
|
|
|
|
|
|
|
|
72
|
1
|
|
|
1
|
|
13
|
use MR::IProto (); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
26
|
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
use constant { |
75
|
1
|
|
|
|
|
216
|
WANT_RESULT => 1, |
76
|
|
|
|
|
|
|
INSERT_ADD => 2, |
77
|
|
|
|
|
|
|
INSERT_REPLACE => 4, |
78
|
1
|
|
|
1
|
|
69
|
}; |
|
1
|
|
|
|
|
3
|
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub IPROTOCLASS () { 'MR::IProto' } |
82
|
|
|
|
|
|
|
|
83
|
1
|
|
|
1
|
|
7
|
use vars qw/$VERSION %ERRORS/; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
73
|
|
84
|
|
|
|
|
|
|
$VERSION = 0.0.24; |
85
|
|
|
|
|
|
|
|
86
|
1
|
|
|
1
|
|
19730
|
BEGIN { *confess = \&MR::IProto::confess } |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
%ERRORS = ( |
89
|
|
|
|
|
|
|
0x00000000 => q{OK}, |
90
|
|
|
|
|
|
|
0x00000100 => q{Non master connection, but it should be}, |
91
|
|
|
|
|
|
|
0x00000200 => q{Illegal parametrs}, |
92
|
|
|
|
|
|
|
0x00000300 => q{Uid not from this storage range}, |
93
|
|
|
|
|
|
|
0x00000400 => q{Tuple is marked as read-only}, |
94
|
|
|
|
|
|
|
0x00000500 => q{Tuple isn't locked}, |
95
|
|
|
|
|
|
|
0x00000600 => q{Tuple is locked}, |
96
|
|
|
|
|
|
|
0x00000700 => q{Failed to allocate memory}, |
97
|
|
|
|
|
|
|
0x00000800 => q{Bad integrity}, |
98
|
|
|
|
|
|
|
0x00000a00 => q{Unsupported command}, |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
0x00000b00 => q{Can't do select}, |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
0x00001800 => q{Can't register new user}, |
103
|
|
|
|
|
|
|
0x00001a00 => q{Can't generate alert id}, |
104
|
|
|
|
|
|
|
0x00001b00 => q{Can't del node}, |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
0x00001c00 => q{User isn't registered}, |
107
|
|
|
|
|
|
|
0x00001d00 => q{Syntax error in query}, |
108
|
|
|
|
|
|
|
0x00001e00 => q{Unknown field}, |
109
|
|
|
|
|
|
|
0x00001f00 => q{Number value is out of range}, |
110
|
|
|
|
|
|
|
0x00002000 => q{Insert already existing object}, |
111
|
|
|
|
|
|
|
0x00002200 => q{Can not order result}, |
112
|
|
|
|
|
|
|
0x00002300 => q{Multiple update/delete forbidden}, |
113
|
|
|
|
|
|
|
0x00002400 => q{Nothing affected}, |
114
|
|
|
|
|
|
|
0x00002500 => q{Primary key update forbidden}, |
115
|
|
|
|
|
|
|
0x00002600 => q{Incorrect protocol version}, |
116
|
|
|
|
|
|
|
0x00002700 => q{WAL failed}, |
117
|
|
|
|
|
|
|
0x00003000 => q{Procedure return type is not supported in the binary protocol}, |
118
|
|
|
|
|
|
|
0x00003100 => q{Tuple doesn't exist}, |
119
|
|
|
|
|
|
|
0x00003200 => q{Procedure is not defined}, |
120
|
|
|
|
|
|
|
0x00003300 => q{Lua error}, |
121
|
|
|
|
|
|
|
0x00003400 => q{Space is disabled}, |
122
|
|
|
|
|
|
|
0x00003500 => q{No such index in space}, |
123
|
|
|
|
|
|
|
0x00003600 => q{Field was not found in the tuple}, |
124
|
|
|
|
|
|
|
0x00003700 => q{Tuple already exists}, |
125
|
|
|
|
|
|
|
0x00003800 => q{Duplicate key exists in a unique index}, |
126
|
|
|
|
|
|
|
0x00003900 => q{Space does not exists}, |
127
|
|
|
|
|
|
|
); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=pod |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=head3 new |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
my $box = $class->new(\%args); |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
%args: |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=over |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item B => [ \%space, ... ] |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
%space: |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=over |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item B => $space_id_uint32 |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Space id as set in Tarantool/Box config. |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=item B => $space_name_string |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
Self-descriptive space id, which will be mapped into C. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item B => $format_string |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
C-compatible tuple format string, allowed formats: C, |
158
|
|
|
|
|
|
|
where C<&> stands for bytestring, C<$> stands for L string. C usable only if perl supports |
159
|
|
|
|
|
|
|
int64 itself. Tuples' fields are packed/unpacked according to this C. |
160
|
|
|
|
|
|
|
C<< * >> at the end of C enables L. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=item B => B<$coderef> |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Specify a callback to turn each tuple into a good-looking hash. |
165
|
|
|
|
|
|
|
It receives C id and resultset as arguments. No return value needed. |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
$coderef = sub { |
168
|
|
|
|
|
|
|
my ($space_id, $resultset) = @_; |
169
|
|
|
|
|
|
|
$_ = { FieldName1 => $_->[0], FieldName2 => $_->[1], ... } for @$resultset; |
170
|
|
|
|
|
|
|
}; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=item B => B<$arrayref> |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
Specify an arrayref of fields names according to C to turn each |
175
|
|
|
|
|
|
|
tuple into a good-looking hash. Names must begin with C<< [A-Za-z] >>. |
176
|
|
|
|
|
|
|
If L enabled, last field will be used to fold tailing fields. |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=item B => B<$arrayref> |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
Specify an arrayref of fields names according to C<< (xxx)* >> to turn |
181
|
|
|
|
|
|
|
tailing fields into a good-looking array of hashes. |
182
|
|
|
|
|
|
|
Names must begin with C<< [A-Za-z] >>. |
183
|
|
|
|
|
|
|
Works with L enabled only. |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=item B => [ \%index, ... ] |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
%index: |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=over |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=item B => $index_id_uint32 |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
Index id as set in Tarantool/Box config within current C. |
195
|
|
|
|
|
|
|
If not set, order position in C is theated as C. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item B => $index_name_string |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
Self-descriptive index id, which will be mapped into C. |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item B => [ $field_no_uint32, ... ] |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
Properly ordered arrayref of fields' numbers which are indexed. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=back |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item B => $default_index_name_string_or_id_uint32 |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Index C or C to be used by default for the current C in B |
210
|
|
|
|
|
|
|
Must be set if there are more than one C<\%index>es. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=item B => $primary_key_name_string_or_id_uint32 |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Index C or C to be used by default for the current C in B operations. |
215
|
|
|
|
|
|
|
It is set to C by default. |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=back |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=item B => $default_space_name_string_or_id_uint32 |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Space C or C to be used by default. Must be set if there are |
222
|
|
|
|
|
|
|
more than one C<\%space>s. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=item B => $timeout_fractional_seconds_float || 23 |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
A common timeout for network operations. |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=item B => $select_timeout_fractional_seconds_float || 2 |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Select queries timeout for network operations. See L. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=item B => $retry_int || 1 |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
A common retries number for network operations. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item B => $select_retry_int || 3 |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
Select queries retries number for network operations. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Sometimes we need short timeout for select's and long timeout for B update's, |
241
|
|
|
|
|
|
|
because in case of timeout we B. For the same |
242
|
|
|
|
|
|
|
reason we B update operation. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
So increasing C and setting C<< retry => 1 >> for updates lowers possibility of |
245
|
|
|
|
|
|
|
such situations (but, of course, does not exclude them at all), and guarantees that |
246
|
|
|
|
|
|
|
we dont do the same more then once. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=item B => $soft_retry_int || 3 |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
A common retries number for Tarantool/Box B (these marked by 1 in the |
251
|
|
|
|
|
|
|
lowest byte of C). In that case we B that the B
|
252
|
|
|
|
|
|
|
declined> by Tarantool/Box for some reason (a tuple was locked for another update, for |
253
|
|
|
|
|
|
|
example), and we B try it again. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
This is also limited by C/C |
256
|
|
|
|
|
|
|
(depending on query type). |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=item B => $retry_delay_fractional_seconds_float || 1 |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
Specify a delay between retries for network operations. |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=item B => $raise_bool || 1 |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
Should we raise an exceptions? If so, exceptions are raised when no more retries left and |
265
|
|
|
|
|
|
|
all tries failed (with timeout, fatal, or temporary error). |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=item B => $debug_level_int || 0 |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Debug level, 0 - print nothing, 9 - print everything |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item B => $name |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
A string used for self-description. Mainly used for debugging purposes. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=back |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=cut |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
sub _make_unpack_format { |
280
|
0
|
|
|
0
|
|
|
my ($ns,$prefix) = @_; |
281
|
0
|
|
|
|
|
|
$ns->{format} =~ s/\s+//g; |
282
|
0
|
0
|
|
|
|
|
confess "${prefix} bad format `$ns->{format}'" unless $ns->{format} =~ m/^[\$\&lLsScCqQ]*(?:\([\$\&lLsScCqQ]+\)\*|\*)?$/; |
283
|
0
|
0
|
|
|
|
|
$ns->{long_tuple} = 1 if $ns->{format} =~ s/\*$//; |
284
|
0
|
|
|
|
|
|
$ns->{long_format} = ''; |
285
|
0
|
|
|
|
|
|
my @f_long; |
286
|
0
|
0
|
|
|
|
|
if ($ns->{long_tuple}) { |
287
|
0
|
|
|
|
|
|
$ns->{format} =~ s/( \( [^\)]* \) | . )$//x; |
288
|
0
|
|
|
|
|
|
$ns->{long_format} = $1; |
289
|
0
|
|
|
|
|
|
$ns->{long_format} =~ s/[()]*//g; |
290
|
0
|
|
|
|
|
|
@f_long = split //, $ns->{long_format}; |
291
|
0
|
0
|
|
|
|
|
$ns->{long_byfield_unpack_format} = [ map { m/[\&\$]/ ? 'w/a*' : "x$_" } @f_long ]; |
|
0
|
|
|
|
|
|
|
292
|
0
|
0
|
|
|
|
|
$ns->{long_field_format} = [ map { m/[\&\$]/ ? 'a*' : $_ } @f_long ]; |
|
0
|
|
|
|
|
|
|
293
|
0
|
|
|
|
|
|
$ns->{long_utf8_fields} = [ grep { $f_long[$_] eq '$' } 0..$#f_long ]; |
|
0
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
} |
295
|
0
|
|
|
|
|
|
my @f = split //, $ns->{format}; |
296
|
0
|
0
|
|
|
|
|
$ns->{byfield_unpack_format} = [ map { m/[\&\$]/ ? 'w/a*' : "x$_" } @f ]; |
|
0
|
|
|
|
|
|
|
297
|
0
|
0
|
|
|
|
|
$ns->{field_format} = [ map { m/[\&\$]/ ? 'a*' : $_ } @f ]; |
|
0
|
|
|
|
|
|
|
298
|
0
|
|
|
|
|
|
$ns->{unpack_format} = join('', @{$ns->{byfield_unpack_format}}); |
|
0
|
|
|
|
|
|
|
299
|
0
|
0
|
|
|
|
|
$ns->{unpack_format} .= '('.join('', @{$ns->{long_byfield_unpack_format}}).')*' if $ns->{long_tuple}; |
|
0
|
|
|
|
|
|
|
300
|
0
|
|
|
|
|
|
$ns->{string_keys} = { map { $_ => 1 } grep { $f[$_] =~ m/[\&\$]/ } 0..$#f }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
301
|
0
|
|
|
|
|
|
$ns->{utf8_fields} = { map { $_ => $_ } grep { $f[$_] eq '$' } 0..$#f }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
sub new { |
305
|
0
|
|
|
0
|
1
|
|
my ($class, $arg) = @_; |
306
|
0
|
|
|
|
|
|
my $self; |
307
|
|
|
|
|
|
|
|
308
|
0
|
|
|
|
|
|
$arg = { %$arg }; |
309
|
0
|
|
0
|
|
|
|
$self->{name} = $arg->{name} || ref$class || $class; |
310
|
0
|
|
0
|
|
|
|
$self->{timeout} = $arg->{timeout} || 23; |
311
|
0
|
|
0
|
|
|
|
$self->{retry} = $arg->{retry} || 1; |
312
|
0
|
|
0
|
|
|
|
$self->{retry_delay} = $arg->{retry_delay} || 1; |
313
|
0
|
|
0
|
|
|
|
$self->{select_retry} = $arg->{select_retry} || 3; |
314
|
0
|
|
0
|
|
|
|
$self->{softretry} = $arg->{soft_retry} || $arg->{softretry} || 3; |
315
|
0
|
|
0
|
|
|
|
$self->{debug} = $arg->{'debug'} || 0; |
316
|
0
|
|
0
|
|
|
|
$self->{ipdebug} = $arg->{'ipdebug'} || 0; |
317
|
0
|
|
|
|
|
|
$self->{raise} = 1; |
318
|
0
|
0
|
|
|
|
|
$self->{raise} = $arg->{raise} if exists $arg->{raise}; |
319
|
0
|
|
0
|
|
|
|
$self->{select_timeout} = $arg->{select_timeout} || $self->{timeout}; |
320
|
0
|
|
0
|
|
|
|
$self->{iprotoclass} = $arg->{iprotoclass} || $class->IPROTOCLASS; |
321
|
0
|
|
|
|
|
|
$self->{_last_error} = 0; |
322
|
0
|
|
|
|
|
|
$self->{_last_error_msg} = ''; |
323
|
|
|
|
|
|
|
|
324
|
0
|
0
|
|
|
|
|
$self->{hashify} = $arg->{'hashify'} if exists $arg->{'hashify'}; |
325
|
0
|
|
|
|
|
|
$self->{default_raw} = $arg->{default_raw}; |
326
|
0
|
0
|
0
|
|
|
|
$self->{default_raw} = 1 if !defined$self->{default_raw} and defined $self->{hashify} and !$self->{hashify}; |
|
|
|
0
|
|
|
|
|
327
|
|
|
|
|
|
|
|
328
|
0
|
|
0
|
|
|
|
$arg->{spaces} = $arg->{namespaces} = [@{ $arg->{spaces} ||= $arg->{namespaces} || confess "no spaces given" }]; |
|
0
|
|
0
|
|
|
|
|
329
|
0
|
0
|
|
|
|
|
confess "no spaces given" unless @{$arg->{spaces}}; |
|
0
|
|
|
|
|
|
|
330
|
0
|
|
|
|
|
|
my %namespaces; |
331
|
0
|
|
|
|
|
|
for my $ns (@{$arg->{spaces}}) { |
|
0
|
|
|
|
|
|
|
332
|
0
|
|
|
|
|
|
$ns = { %$ns }; |
333
|
0
|
0
|
|
|
|
|
my $namespace = defined $ns->{space} ? $ns->{space} : $ns->{namespace}; |
334
|
0
|
|
|
|
|
|
$ns->{space} = $ns->{namespace} = $namespace; |
335
|
0
|
0
|
|
|
|
|
confess "space[?] `space' not set" unless defined $namespace; |
336
|
0
|
0
|
0
|
|
|
|
confess "space[$namespace] already defined" if $namespaces{$namespace} or $ns->{name}&&$namespaces{$ns->{name}}; |
|
|
|
0
|
|
|
|
|
337
|
0
|
0
|
0
|
|
|
|
confess "space[$namespace] no indexes defined" unless $ns->{indexes} && @{$ns->{indexes}}; |
|
0
|
|
|
|
|
|
|
338
|
0
|
|
|
|
|
|
$namespaces{$namespace} = $ns; |
339
|
0
|
0
|
|
|
|
|
$namespaces{$ns->{name}} = $ns if $ns->{name}; |
340
|
|
|
|
|
|
|
|
341
|
0
|
|
|
|
|
|
_make_unpack_format($ns,"space[$namespace]"); |
342
|
|
|
|
|
|
|
|
343
|
0
|
0
|
|
|
|
|
$ns->{append_for_unpack} = '' unless defined $ns->{append_for_unpack}; |
344
|
0
|
|
|
|
|
|
$ns->{check_keys} = {}; |
345
|
|
|
|
|
|
|
|
346
|
0
|
|
|
|
|
|
my $inames = $ns->{index_names} = {}; |
347
|
0
|
|
|
|
|
|
my $i = -1; |
348
|
0
|
|
|
|
|
|
for my $index (@{$ns->{indexes}}) { |
|
0
|
|
|
|
|
|
|
349
|
0
|
|
|
|
|
|
++$i; |
350
|
0
|
0
|
|
|
|
|
confess "space[$namespace]index[($i)] no name given" unless length $index->{index_name}; |
351
|
0
|
|
|
|
|
|
my $index_name = $index->{index_name}; |
352
|
0
|
0
|
0
|
|
|
|
confess "space[$namespace]index[$index_name($i)] no indexes defined" unless $index->{keys} && @{$index->{keys}}; |
|
0
|
|
|
|
|
|
|
353
|
0
|
0
|
0
|
|
|
|
confess "space[$namespace]index[$index_name($i)] already defined" if $inames->{$index_name} || $inames->{$i}; |
354
|
0
|
0
|
|
|
|
|
$index->{id} = $i unless defined $index->{id}; |
355
|
0
|
|
|
|
|
|
$inames->{$i} = $inames->{$index_name} = $index; |
356
|
0
|
|
0
|
|
|
|
int $_ == $_ and $_ >= 0 and $_ < @{$ns->{field_format}} or confess "space[$namespace]index[$index_name] bad key `$_'" for @{$ns->{keys}}; |
|
0
|
|
0
|
|
|
|
|
|
0
|
|
0
|
|
|
|
|
357
|
0
|
|
|
|
|
|
$ns->{check_keys}->{$_} = int !! $ns->{string_keys}->{$_} for @{$index->{keys}}; |
|
0
|
|
|
|
|
|
|
358
|
0
|
|
0
|
|
|
|
$index->{string_keys} ||= $ns->{string_keys}; |
359
|
|
|
|
|
|
|
} |
360
|
0
|
0
|
|
|
|
|
if( @{$ns->{indexes}} > 1 ) { |
|
0
|
|
|
|
|
|
|
361
|
0
|
0
|
|
|
|
|
confess "space[$namespace] default_index not given" unless defined $ns->{default_index}; |
362
|
0
|
0
|
|
|
|
|
confess "space[$namespace] default_index $ns->{default_index} does not exist" unless $inames->{$ns->{default_index}}; |
363
|
0
|
0
|
|
|
|
|
$ns->{primary_key_index} = $ns->{default_index} unless defined $ns->{primary_key_index}; |
364
|
0
|
0
|
|
|
|
|
confess "space[$namespace] primary_key_index $ns->{primary_key_index} does not exist" unless $inames->{$ns->{primary_key_index}}; |
365
|
|
|
|
|
|
|
} else { |
366
|
0
|
|
0
|
|
|
|
$ns->{default_index} ||= 0; |
367
|
0
|
|
0
|
|
|
|
$ns->{primary_key_index} ||= 0; |
368
|
|
|
|
|
|
|
} |
369
|
0
|
|
0
|
|
|
|
$ns->{fields} ||= $arg->{default_fields}; |
370
|
0
|
|
0
|
|
|
|
$ns->{long_fields} ||= $arg->{default_long_fields}; |
371
|
0
|
0
|
|
|
|
|
if($ns->{fields}) { |
372
|
0
|
0
|
|
|
|
|
confess "space[$namespace] fields must be ARRAYREF" unless ref $ns->{fields} eq 'ARRAY'; |
373
|
0
|
0
|
|
|
|
|
confess "space[$namespace] fields number must match format" if @{$ns->{fields}} != int(!!$ns->{long_tuple})+@{$ns->{field_format}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
374
|
0
|
|
0
|
|
|
|
m/^[A-Za-z]/ or confess "space[$namespace] fields names must begin with [A-Za-z]: bad name $_" for @{$ns->{fields}}; |
|
0
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
|
$ns->{fields_hash} = { map { $ns->{fields}->[$_] => $_ } 0..$#{$ns->{fields}} }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
} |
377
|
0
|
0
|
|
|
|
|
if($ns->{long_fields}) { |
378
|
0
|
0
|
|
|
|
|
confess "space[$namespace] long_fields must be ARRAYREF" unless ref $ns->{long_fields} eq 'ARRAY'; |
379
|
0
|
0
|
|
|
|
|
confess "space[$namespace] long_fields number must match format" if @{$ns->{long_fields}} != @{$ns->{long_field_format}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
380
|
0
|
|
0
|
|
|
|
m/^[A-Za-z]/ or confess "space[$namespace] long_fields names must begin with [A-Za-z]: bad name $_" for @{$ns->{long_fields}}; |
|
0
|
|
|
|
|
|
|
381
|
0
|
|
|
|
|
|
$ns->{long_fields_hash} = { map { $ns->{long_fields}->[$_] => $_ } 0..$#{$ns->{long_fields}} }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
} |
383
|
0
|
0
|
0
|
|
|
|
$ns->{default_raw} = 1 if !defined$ns->{default_raw} and defined $ns->{hashify} and !$ns->{hashify}; |
|
|
|
0
|
|
|
|
|
384
|
|
|
|
|
|
|
} |
385
|
0
|
|
|
|
|
|
$self->{namespaces} = \%namespaces; |
386
|
0
|
0
|
|
|
|
|
if (@{$arg->{spaces}} > 1) { |
|
0
|
|
|
|
|
|
|
387
|
0
|
0
|
|
|
|
|
$arg->{default_namespace} = $arg->{default_space} if defined $arg->{default_space}; |
388
|
0
|
0
|
|
|
|
|
confess "default_space not given" unless defined $arg->{default_namespace}; |
389
|
0
|
0
|
|
|
|
|
confess "default_space $arg->{default_namespace} does not exist" unless $namespaces{$arg->{default_namespace}}; |
390
|
0
|
|
|
|
|
|
$self->{default_namespace} = $arg->{default_namespace}; |
391
|
|
|
|
|
|
|
} else { |
392
|
0
|
|
0
|
|
|
|
$self->{default_namespace} = $arg->{default_space} || $arg->{default_namespace} || $arg->{spaces}->[0]->{space}; |
393
|
0
|
0
|
|
|
|
|
confess "default_space $self->{default_namespace} does not exist" unless $namespaces{$self->{default_namespace}}; |
394
|
|
|
|
|
|
|
} |
395
|
0
|
|
|
|
|
|
bless $self, $class; |
396
|
0
|
|
|
|
|
|
$self->_connect($arg->{'servers'}); |
397
|
0
|
|
|
|
|
|
return $self; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
sub _debug { |
401
|
0
|
0
|
|
0
|
|
|
if($_[0]->{warn}) { |
402
|
0
|
|
|
|
|
|
&{$_[0]->{warn}}; |
|
0
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
} else { |
404
|
0
|
|
|
|
|
|
warn "@_[1..$#_]\n"; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
sub _connect { |
409
|
0
|
|
|
0
|
|
|
my ($self, $servers) = @_; |
410
|
0
|
|
|
|
|
|
$self->{server} = $self->{iprotoclass}->new({ |
411
|
|
|
|
|
|
|
servers => $servers, |
412
|
|
|
|
|
|
|
name => $self->{name}, |
413
|
|
|
|
|
|
|
debug => $self->{'ipdebug'}, |
414
|
|
|
|
|
|
|
dump_no_ints => 1, |
415
|
|
|
|
|
|
|
max_request_retries => 1, |
416
|
|
|
|
|
|
|
retry_delay => $self->{retry_delay}, |
417
|
|
|
|
|
|
|
}); |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=pod |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head3 Error |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
Last error code, or 'fail' for some network reason, oftenly a timeout. |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
$box->Insert(@tuple) or die sprintf "Error %X", $box->Error; # die "Error 202" |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head3 ErrorStr |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Last error code and description in a single string. |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
$box->Insert(@tuple) or die $box->ErrorStr; # die "Error 00000202: Illegal Parameters" |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=cut |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
sub ErrorStr { |
437
|
0
|
|
|
0
|
1
|
|
return $_[0]->{_last_error_msg}; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
sub Error { |
441
|
0
|
|
|
0
|
1
|
|
return $_[0]->{_last_error}; |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
sub _chat { |
445
|
0
|
|
|
0
|
|
|
my ($self, %param) = @_; |
446
|
0
|
|
|
|
|
|
my $orig_unpack = delete $param{unpack}; |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
$param{unpack} = sub { |
449
|
0
|
|
|
0
|
|
|
my $data = $_[0]; |
450
|
0
|
0
|
|
|
|
|
confess __LINE__."$self->{name}: [common]: Bad response" if length $data < 4; |
451
|
0
|
|
|
|
|
|
my ($full_code, @err_code) = unpack('LX[L]CSC', substr($data, 0, 4, '')); |
452
|
|
|
|
|
|
|
# $err_code[0] = severity: 0 -> ok, 1 -> transient, 2 -> permanent; |
453
|
|
|
|
|
|
|
# $err_code[1] = description; |
454
|
|
|
|
|
|
|
# $err_code[2] = da box project; |
455
|
0
|
|
|
|
|
|
return (\@err_code, \$data, $full_code); |
456
|
0
|
|
|
|
|
|
}; |
457
|
|
|
|
|
|
|
|
458
|
0
|
|
0
|
|
|
|
my $timeout = $param{timeout} || $self->{timeout}; |
459
|
0
|
|
0
|
|
|
|
my $retry = $param{retry} || $self->{retry}; |
460
|
0
|
|
|
|
|
|
my $soft_retry = $self->{softretry}; |
461
|
0
|
|
|
|
|
|
my $retry_count = 0; |
462
|
|
|
|
|
|
|
|
463
|
0
|
|
|
|
|
|
my $callback = delete $param{callback}; |
464
|
0
|
|
|
|
|
|
my $return_fh = delete $param{return_fh}; |
465
|
0
|
|
0
|
|
|
|
my $_cb = $callback || $return_fh; |
466
|
|
|
|
|
|
|
|
467
|
0
|
0
|
0
|
|
|
|
die "Can't use raise and callback together" if $callback && $self->{raise}; |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
my $is_retry = sub { |
470
|
0
|
|
|
0
|
|
|
my ($data) = @_; |
471
|
0
|
|
|
|
|
|
$retry_count++; |
472
|
0
|
0
|
|
|
|
|
if($data) { |
473
|
0
|
|
|
|
|
|
my ($ret_code, $data, $full_code) = @$data; |
474
|
0
|
0
|
|
|
|
|
return 0 if $ret_code->[0] == 0; |
475
|
|
|
|
|
|
|
# retry if error is soft even in case of update e.g. ROW_LOCK |
476
|
0
|
0
|
0
|
|
|
|
if ($ret_code->[0] == 1 and --$soft_retry > 0) { |
477
|
0
|
0
|
|
|
|
|
--$retry if $retry > 1; |
478
|
0
|
|
|
|
|
|
return 1; |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} |
481
|
0
|
0
|
|
|
|
|
return 1 if --$retry; |
482
|
0
|
|
|
|
|
|
return 0; |
483
|
0
|
|
|
|
|
|
}; |
484
|
|
|
|
|
|
|
|
485
|
0
|
|
|
|
|
|
my $message; |
486
|
|
|
|
|
|
|
my $process = sub { |
487
|
0
|
|
|
0
|
|
|
my ($data, $error) = @_; |
488
|
0
|
|
|
|
|
|
my $errno = $!; |
489
|
0
|
0
|
0
|
|
|
|
if (!$error && $data) { |
490
|
0
|
|
|
|
|
|
my ($ret_code, $data, $full_code) = @$data; |
491
|
|
|
|
|
|
|
|
492
|
0
|
|
|
|
|
|
$self->{_last_error} = $full_code; |
493
|
0
|
0
|
0
|
|
|
|
$self->{_last_error_msg} = $message = $ret_code->[0] == 0 ? "" : sprintf "Error %08X: %s", $full_code, $$data || $ERRORS{$full_code & 0xFFFFFF00} || 'Unknown error'; |
494
|
0
|
0
|
0
|
|
|
|
$self->_debug("$self->{name}: $message") if $ret_code->[0] != 0 && $self->{debug} >= 1; |
495
|
|
|
|
|
|
|
|
496
|
0
|
0
|
|
|
|
|
if ($ret_code->[0] == 0) { |
497
|
0
|
|
|
|
|
|
my $ret = $orig_unpack->($$data,$ret_code->[2]); |
498
|
0
|
0
|
|
|
|
|
confess __LINE__."$self->{name}: [common]: Bad response (more data left)" if length $$data > 0; |
499
|
0
|
0
|
|
|
|
|
return $ret unless $_cb; |
500
|
0
|
|
|
|
|
|
return &$_cb($ret); |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
0
|
0
|
|
|
|
|
if ($ret_code->[0] == 2) { #fatal error |
504
|
0
|
0
|
|
|
|
|
$self->_raise($message) if $self->{raise}; |
505
|
0
|
0
|
|
|
|
|
return 0 unless $_cb; |
506
|
0
|
|
|
|
|
|
return &$_cb(0, $error); |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
} else { # timeout has caused the failure if $ret->{timeout} |
509
|
0
|
|
|
|
|
|
$self->{_last_error} = 'fail'; |
510
|
0
|
|
0
|
|
|
|
$message ||= $self->{_last_error_msg} = $error; |
511
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: $message") if $self->{debug} >= 1; |
512
|
0
|
0
|
|
|
|
|
$self->_raise("$self->{name}: no success after $retry_count tries: $message\n") if $self->{raise}; |
513
|
0
|
0
|
|
|
|
|
return 0 unless $_cb; |
514
|
0
|
|
|
|
|
|
return &$_cb(0, $error); |
515
|
|
|
|
|
|
|
} |
516
|
0
|
|
|
|
|
|
}; |
517
|
|
|
|
|
|
|
|
518
|
0
|
0
|
|
|
|
|
if ($callback) { |
519
|
0
|
|
|
|
|
|
$self->{_last_error} = 0x77777777; |
520
|
0
|
|
|
|
|
|
$self->{server}->SetTimeout($timeout); |
521
|
0
|
0
|
|
|
|
|
return 1 if eval { $self->{server}->send({%param, is_retry => $is_retry, max_request_retries => $retry}, $process); 1 }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
522
|
0
|
|
|
|
|
|
return 0; |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
0
|
0
|
|
|
|
|
$param{continue} = $process if $return_fh; |
526
|
|
|
|
|
|
|
|
527
|
0
|
|
|
|
|
|
my $ret; |
528
|
0
|
|
|
|
|
|
while ($retry > 0) { |
529
|
0
|
|
|
|
|
|
$self->{_last_error} = 0x77777777; |
530
|
0
|
|
|
|
|
|
$self->{server}->SetTimeout($timeout); |
531
|
|
|
|
|
|
|
|
532
|
0
|
|
|
|
|
|
$ret = $self->{server}->Chat1(%param); |
533
|
0
|
0
|
0
|
|
|
|
return $ret->{ok} if $param{continue} && $ret->{ok}; |
534
|
0
|
0
|
|
|
|
|
last unless &$is_retry($ret->{ok}); |
535
|
0
|
|
|
|
|
|
sleep $self->{retry_delay}; |
536
|
|
|
|
|
|
|
}; |
537
|
|
|
|
|
|
|
|
538
|
0
|
0
|
0
|
|
|
|
$self->_raise("no success after $retry_count tries\n") if $self->{raise} && !$ret->{ok}; |
539
|
0
|
|
|
|
|
|
return &$process($ret->{ok}, $ret->{fail}); |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
sub _raise { |
543
|
0
|
|
|
0
|
|
|
my ($self, $msg) = @_; |
544
|
0
|
|
|
|
|
|
die "$self->{name}: $msg\n"; |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
sub _validate_param { |
548
|
0
|
|
|
0
|
|
|
my ($self, $args, @pnames) = @_; |
549
|
0
|
0
|
0
|
|
|
|
my $param = $args && @$args && ref $args->[-1] eq 'HASH' ? {%{pop @$args}} : {}; |
|
0
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
|
551
|
0
|
|
|
|
|
|
my %pnames = map { $_ => 1 } @pnames; |
|
0
|
|
|
|
|
|
|
552
|
0
|
|
|
|
|
|
$pnames{space} = 1; |
553
|
0
|
|
|
|
|
|
$pnames{namespace} = 1; |
554
|
0
|
|
|
|
|
|
$pnames{callback} = 1; |
555
|
0
|
|
|
|
|
|
foreach my $pname (keys %$param) { |
556
|
0
|
0
|
|
|
|
|
confess "$self->{name}: unknown param $pname\n" unless exists $pnames{$pname}; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
0
|
0
|
0
|
|
|
|
$param->{namespace} = $param->{space} if exists $param->{space} and defined $param->{space}; |
560
|
0
|
0
|
|
|
|
|
$param->{namespace} = $self->{default_namespace} unless defined $param->{namespace}; |
561
|
0
|
0
|
|
|
|
|
confess "$self->{name}: bad space `$param->{namespace}'" unless exists $self->{namespaces}->{$param->{namespace}}; |
562
|
|
|
|
|
|
|
|
563
|
0
|
|
|
|
|
|
my $ns = $self->{namespaces}->{$param->{namespace}}; |
564
|
0
|
0
|
|
|
|
|
$param->{use_index} = $pnames{use_index} ? $ns->{default_index} : $ns->{primary_key_index} unless defined $param->{use_index}; |
|
|
0
|
|
|
|
|
|
565
|
0
|
0
|
|
|
|
|
confess "$self->{name}: bad index `$param->{use_index}'" unless exists $ns->{index_names}->{$param->{use_index}}; |
566
|
0
|
|
|
|
|
|
$param->{index} = $ns->{index_names}->{$param->{use_index}}; |
567
|
|
|
|
|
|
|
|
568
|
0
|
0
|
|
|
|
|
if(exists $pnames{raw}) { |
569
|
0
|
0
|
|
|
|
|
$param->{raw} = $ns->{default_raw} unless defined $param->{raw}; |
570
|
0
|
0
|
|
|
|
|
$param->{raw} = $self->{default_raw} unless defined $param->{raw}; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
0
|
|
|
|
|
|
return ($param, $ns, map { $param->{$_} } @pnames); |
|
0
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=pod |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=head3 Call |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
Call a stored procedure. Returns an arrayref of the result tuple(s) upon success. |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
my $results = $box->Call('stored_procedure_name', \@procedure_params, \%options) or die $box->ErrorStr; # Call failed |
583
|
|
|
|
|
|
|
my $result_tuple = @$results && $results->[0] or warn "Call succeeded, but returned nothing"; |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=over |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
=item B<@procedure_params> |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
An array of bytestrings to be passed as is to the procecedure. |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=item B<%options> |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
=over |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
=item B |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
Format to unpack the result tuple, the same as C option for C |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=back |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=back |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=cut |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
sub Call { |
606
|
0
|
|
|
0
|
1
|
|
my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/flags raise unpack unpack_format/); |
607
|
0
|
|
|
|
|
|
my ($self, $sp_name, $tuple) = @_; |
608
|
|
|
|
|
|
|
|
609
|
0
|
|
0
|
|
|
|
my $flags = $param->{flags} || 0; |
610
|
0
|
0
|
|
|
|
|
local $self->{raise} = $param->{raise} if defined $param->{raise}; |
611
|
|
|
|
|
|
|
|
612
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: CALL($sp_name)[${\join ' ', map {join' ',unpack'(H2)*',$_} @$tuple}]") if $self->{debug} >= 4; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
613
|
0
|
0
|
|
|
|
|
confess "All fields must be defined" if grep { !defined } @$tuple; |
|
0
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
|
615
|
0
|
0
|
0
|
|
|
|
confess "Required `unpack_format` option wasn't defined" |
|
|
|
0
|
|
|
|
|
616
|
|
|
|
|
|
|
unless exists $param->{unpack} or exists $param->{unpack_format} and $param->{unpack_format}; |
617
|
|
|
|
|
|
|
|
618
|
0
|
|
|
|
|
|
my $unpack_format = $param->{unpack_format}; |
619
|
0
|
0
|
|
|
|
|
if($unpack_format) { |
620
|
0
|
0
|
|
|
|
|
$unpack_format = join '', @$unpack_format if ref $unpack_format; |
621
|
0
|
|
|
|
|
|
my $f = { format => $unpack_format }; |
622
|
0
|
|
|
|
|
|
_make_unpack_format($f, "CALL"); |
623
|
0
|
|
|
|
|
|
$unpack_format = $f->{unpack_format}; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
0
|
0
|
|
|
|
|
local $namespace->{unpack_format} = $unpack_format if $unpack_format; # XXX |
627
|
0
|
0
|
|
|
|
|
local $namespace->{append_for_unpack} = '' if $unpack_format; # shit... |
628
|
|
|
|
|
|
|
|
629
|
0
|
|
|
|
|
|
$tuple = [ map { |
630
|
0
|
|
|
|
|
|
my $x = $_; |
631
|
0
|
0
|
|
|
|
|
Encode::_utf8_off($x) if Encode::is_utf8($x,0); |
632
|
0
|
|
|
|
|
|
$x; |
633
|
|
|
|
|
|
|
} @$tuple ]; |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
$self->_chat ( |
636
|
|
|
|
|
|
|
msg => 22, |
637
|
|
|
|
|
|
|
payload => pack("L w/a* L(w/a*)*", $flags, $sp_name, scalar(@$tuple), @$tuple), |
638
|
0
|
|
|
0
|
|
|
unpack => $param->{unpack} || sub { $self->_unpack_select($namespace, "CALL", @_) }, |
639
|
0
|
|
0
|
|
|
|
callback => $param->{callback}, |
640
|
|
|
|
|
|
|
); |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=pod |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=head3 Add, Insert, Replace |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
$box->Add(@tuple) or die $box->ErrorStr; # only store a new tuple |
648
|
|
|
|
|
|
|
$box->Replace(@tuple, { space => "secondary" }); # only store an existing tuple |
649
|
|
|
|
|
|
|
$box->Insert(@tuple, { space => "main" }); # store anyway |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
Insert a C<< @tuple >> into the storage into C<$options{space}> or C space. |
652
|
|
|
|
|
|
|
All of them return C upon success. |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
All of them have the same parameters: |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
=over |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=item B<@tuple> |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
A tuple to insert. All fields must be defined. All fields will be Ced according to C (see L) |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=item B<%options> |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=over |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=item B => $space_id_uint32_or_name_string |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
Specify storage space to work on. |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=back |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=back |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
The difference between them is the behaviour concerning tuple with the same primary key: |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=over |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=item * |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
B will succeed if and only if duplicate-key tuple B |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
=item * |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
B will succeed if and only if a duplicate-key tuple B |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
=item * |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
B will succeed B. Duplicate-key tuple will be B |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=back |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=cut |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
sub Add { # store tuple if tuple identified by primary key _does_not_ exist |
695
|
0
|
0
|
0
|
0
|
1
|
|
my $param = @_ && ref $_[-1] eq 'HASH' ? pop : {}; |
696
|
0
|
|
|
|
|
|
$param->{action} = 'add'; |
697
|
0
|
|
|
|
|
|
$_[0]->Insert(@_[1..$#_], $param); |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
sub Set { # store tuple _anyway_ |
701
|
0
|
0
|
0
|
0
|
0
|
|
my $param = @_ && ref $_[-1] eq 'HASH' ? pop : {}; |
702
|
0
|
|
|
|
|
|
$param->{action} = 'set'; |
703
|
0
|
|
|
|
|
|
$_[0]->Insert(@_[1..$#_], $param); |
704
|
|
|
|
|
|
|
} |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
sub Replace { # store tuple if tuple identified by primary key _does_ exist |
707
|
0
|
0
|
0
|
0
|
1
|
|
my $param = @_ && ref $_[-1] eq 'HASH' ? pop : {}; |
708
|
0
|
|
|
|
|
|
$param->{action} = 'replace'; |
709
|
0
|
|
|
|
|
|
$_[0]->Insert(@_[1..$#_], $param); |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
sub Insert { |
713
|
0
|
|
|
0
|
1
|
|
my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/want_result want_inserted_tuple _flags action raw/); |
714
|
0
|
|
|
|
|
|
my ($self, @tuple) = @_; |
715
|
|
|
|
|
|
|
|
716
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: INSERT(NS:$namespace->{namespace},TUPLE:[@{[map {qq{`$_'}} @tuple]}])") if $self->{debug} >= 3; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
|
718
|
0
|
0
|
|
|
|
|
$param->{want_result} = $param->{want_inserted_tuple} if !defined $param->{want_result}; |
719
|
|
|
|
|
|
|
|
720
|
0
|
|
0
|
|
|
|
my $flags = $param->{_flags} || 0; |
721
|
0
|
0
|
|
|
|
|
$flags |= WANT_RESULT if $param->{want_result}; |
722
|
|
|
|
|
|
|
|
723
|
0
|
|
0
|
|
|
|
$param->{action} ||= 'set'; |
724
|
0
|
0
|
|
|
|
|
if ($param->{action} eq 'add') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
725
|
0
|
|
|
|
|
|
$flags |= INSERT_ADD; |
726
|
|
|
|
|
|
|
} elsif ($param->{action} eq 'replace') { |
727
|
0
|
|
|
|
|
|
$flags |= INSERT_REPLACE; |
728
|
|
|
|
|
|
|
} elsif ($param->{action} ne 'set') { |
729
|
0
|
|
|
|
|
|
confess "$self->{name}: Bad insert action `$param->{action}'"; |
730
|
|
|
|
|
|
|
} |
731
|
0
|
|
|
|
|
|
my $chkkey = $namespace->{check_keys}; |
732
|
0
|
|
|
|
|
|
my $fmt = $namespace->{field_format}; |
733
|
0
|
|
|
|
|
|
my $long_fmt = $namespace->{long_field_format}; |
734
|
0
|
0
|
|
|
|
|
my $chk_divisor = $namespace->{long_tuple} ? @$long_fmt : @$fmt; |
735
|
0
|
0
|
|
|
|
|
confess "Wrong fields number in tuple" if 0 != (@tuple - @$fmt) % $chk_divisor; |
736
|
0
|
|
|
|
|
|
for (0..$#tuple) { |
737
|
0
|
0
|
|
|
|
|
confess "$self->{name}: ref in tuple $_=`$tuple[$_]'" if ref $tuple[$_]; |
738
|
1
|
|
|
1
|
|
18
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5469
|
|
739
|
0
|
0
|
|
|
|
|
Encode::_utf8_off($_) if Encode::is_utf8($_,0); |
740
|
0
|
0
|
|
|
|
|
if(exists $chkkey->{$_}) { |
741
|
0
|
0
|
|
|
|
|
if($chkkey->{$_}) { |
742
|
0
|
0
|
|
|
|
|
confess "$self->{name}: undefined key $_" unless defined $tuple[$_]; |
743
|
|
|
|
|
|
|
} else { |
744
|
0
|
0
|
0
|
|
|
|
confess "$self->{name}: not numeric key $_=`$tuple[$_]'" unless looks_like_number($tuple[$_]) && int($tuple[$_]) == $tuple[$_]; |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
} |
747
|
0
|
0
|
|
|
|
|
$tuple[$_] = pack($_ < @$fmt ? $fmt->[$_] : $long_fmt->[$_ % @$long_fmt], $tuple[$_]); |
748
|
|
|
|
|
|
|
} |
749
|
|
|
|
|
|
|
|
750
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: INSERT[${\join ' ', map {join' ',unpack'(H2)*',$_} @tuple}]") if $self->{debug} >= 4; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
my $cb = sub { |
753
|
0
|
|
|
0
|
|
|
my ($r) = @_; |
754
|
|
|
|
|
|
|
|
755
|
0
|
0
|
|
|
|
|
if($param->{want_result}) { |
756
|
0
|
|
|
|
|
|
$self->_PostSelect($r, $param, $namespace); |
757
|
0
|
|
0
|
|
|
|
$r = $r && $r->[0]; |
758
|
|
|
|
|
|
|
} |
759
|
|
|
|
|
|
|
|
760
|
0
|
0
|
|
|
|
|
return $param->{callback}->($r) if $param->{callback}; |
761
|
0
|
|
|
|
|
|
return $r; |
762
|
0
|
|
|
|
|
|
}; |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
my $r = $self->_chat ( |
765
|
|
|
|
|
|
|
msg => 13, |
766
|
|
|
|
|
|
|
payload => pack("LLL (w/a*)*", $namespace->{namespace}, $flags, scalar(@tuple), @tuple), |
767
|
0
|
|
|
0
|
|
|
unpack => sub { $self->_unpack_affected($flags, $namespace, @_) }, |
768
|
0
|
0
|
0
|
|
|
|
callback => $param->{callback} && $cb, |
769
|
|
|
|
|
|
|
) or return; |
770
|
|
|
|
|
|
|
|
771
|
0
|
0
|
|
|
|
|
return 1 if $param->{callback}; |
772
|
0
|
|
|
|
|
|
return $cb->($r); |
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
sub _unpack_select { |
776
|
0
|
|
|
0
|
|
|
my ($self, $ns, $debug_prefix) = @_; |
777
|
0
|
|
0
|
|
|
|
$debug_prefix ||= "SELECT"; |
778
|
0
|
0
|
|
|
|
|
confess __LINE__."$self->{name}: [$debug_prefix]: Bad response" if length $_[3] < 4; |
779
|
0
|
|
|
|
|
|
my $result_count = unpack('L', substr($_[3], 0, 4, '')); |
780
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: [$debug_prefix]: COUNT=[$result_count];") if $self->{debug} >= 3; |
781
|
0
|
|
|
|
|
|
my (@res); |
782
|
0
|
|
|
|
|
|
my $appe = $ns->{append_for_unpack}; |
783
|
0
|
|
|
|
|
|
my $fmt = $ns->{unpack_format}; |
784
|
0
|
|
|
|
|
|
for(my $i = 0; $i < $result_count; ++$i) { |
785
|
0
|
0
|
|
|
|
|
confess __LINE__."$self->{name}: [$debug_prefix]: Bad response" if length $_[3] < 8; |
786
|
0
|
|
|
|
|
|
my ($len, $cardinality) = unpack('LL', substr($_[3], 0, 8, '')); |
787
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: [$debug_prefix]: ROW[$i]: LEN=[$len]; NFIELD=[$cardinality];") if $self->{debug} >= 4; |
788
|
0
|
0
|
|
|
|
|
confess __LINE__."$self->{name}: [$debug_prefix]: Bad response" if length $_[3] < $len; |
789
|
0
|
|
|
|
|
|
my $packed_tuple = substr($_[3], 0, $len, ''); |
790
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: [$debug_prefix]: ROW[$i]: DATA=[@{[unpack '(H2)*', $packed_tuple]}];") if $self->{debug} >= 6; |
|
0
|
|
|
|
|
|
|
791
|
0
|
|
|
|
|
|
$packed_tuple .= $appe; |
792
|
0
|
|
|
|
|
|
my @tuple = eval { unpack($fmt, $packed_tuple) }; |
|
0
|
|
|
|
|
|
|
793
|
0
|
0
|
0
|
|
|
|
confess "$self->{name}: [$debug_prefix]: ROW[$i]: can't unpack tuple [@{[unpack('(H2)*', $packed_tuple)]}]: $@" if !@tuple || $@; |
|
0
|
|
|
|
|
|
|
794
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: [$debug_prefix]: ROW[$i]: FIELDS=[@{[map { qq{`$_'} } @tuple]}];") if $self->{debug} >= 5; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
795
|
0
|
|
|
|
|
|
push @res, \@tuple; |
796
|
|
|
|
|
|
|
} |
797
|
0
|
|
|
|
|
|
return \@res; |
798
|
|
|
|
|
|
|
} |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
sub _unpack_select_multi { |
801
|
0
|
|
|
0
|
|
|
my ($self, $nss, $debug_prefix) = @_; |
802
|
0
|
|
0
|
|
|
|
$debug_prefix ||= "SMULTI"; |
803
|
0
|
|
|
|
|
|
my (@rsets); |
804
|
0
|
|
|
|
|
|
my $i = 0; |
805
|
0
|
|
|
|
|
|
for my $ns (@$nss) { |
806
|
0
|
|
|
|
|
|
push @rsets, $self->_unpack_select($ns, "${debug_prefix}[$i]", $_[3]); |
807
|
0
|
|
|
|
|
|
++$i; |
808
|
|
|
|
|
|
|
} |
809
|
0
|
|
|
|
|
|
return \@rsets; |
810
|
|
|
|
|
|
|
} |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
sub _unpack_affected { |
814
|
0
|
|
|
0
|
|
|
my ($self, $flags, $ns) = @_; |
815
|
0
|
0
|
0
|
|
|
|
($flags & WANT_RESULT) ? $self->_unpack_select($ns, "AFFECTED", $_[3]) : unpack('L', substr($_[3],0,4,''))||'0E0'; |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
sub NPRM () { 3 } |
819
|
|
|
|
|
|
|
sub _pack_keys { |
820
|
0
|
|
|
0
|
|
|
my ($self, $ns, $idx) = @_; |
821
|
|
|
|
|
|
|
|
822
|
0
|
|
|
|
|
|
my $keys = $idx->{keys}; |
823
|
0
|
|
|
|
|
|
my $strkey = $ns->{string_keys}; |
824
|
0
|
|
|
|
|
|
my $fmt = $ns->{field_format}; |
825
|
|
|
|
|
|
|
|
826
|
0
|
0
|
|
|
|
|
if (@$keys == 1) { |
827
|
0
|
|
|
|
|
|
$fmt = $fmt->[$keys->[0]]; |
828
|
0
|
|
|
|
|
|
$strkey = $strkey->{$keys->[0]}; |
829
|
0
|
|
|
|
|
|
foreach (@_[NPRM..$#_]) { |
830
|
0
|
0
|
|
|
|
|
($_) = @$_ if ref $_ eq 'ARRAY'; |
831
|
0
|
0
|
|
|
|
|
Encode::_utf8_off($_) if Encode::is_utf8($_,0); |
832
|
0
|
0
|
|
|
|
|
unless ($strkey) { |
833
|
0
|
0
|
0
|
|
|
|
confess "$self->{name}: not numeric key [$_]" unless looks_like_number($_) && int($_) == $_; |
834
|
0
|
|
|
|
|
|
$_ = pack($fmt, $_); |
835
|
|
|
|
|
|
|
} |
836
|
0
|
|
|
|
|
|
$_ = pack('L(w/a*)*', 1, $_); |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
} else { |
839
|
0
|
|
|
|
|
|
foreach my $k (@_[NPRM..$#_]) { |
840
|
0
|
0
|
0
|
|
|
|
confess "bad key [@$keys][$k][@{[ref $k eq 'ARRAY' ? (@$k) : ()]}]" unless ref $k eq 'ARRAY' and @$k and @$k <= @$keys; |
|
0
|
0
|
0
|
|
|
|
|
841
|
0
|
|
|
|
|
|
for my $i (0..$#$k) { |
842
|
0
|
0
|
|
|
|
|
unless ($strkey->{$keys->[$i]}) { |
843
|
0
|
0
|
0
|
|
|
|
confess "$self->{name}: not numeric key [$i][$k->[$i]]" unless looks_like_number($k->[$i]) && int($k->[$i]) == $k->[$i]; |
844
|
|
|
|
|
|
|
} |
845
|
0
|
0
|
|
|
|
|
Encode::_utf8_off($k->[$i]) if Encode::is_utf8($k->[$i],0); |
846
|
0
|
|
|
|
|
|
$k->[$i] = pack($fmt->[$keys->[$i]], $k->[$i]); |
847
|
|
|
|
|
|
|
} |
848
|
0
|
|
|
|
|
|
$k = pack('L(w/a*)*', scalar(@$k), @$k); |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
} |
851
|
|
|
|
|
|
|
} |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
sub _PackSelect { |
854
|
0
|
|
|
0
|
|
|
my ($self, $param, $namespace, @keys) = @_; |
855
|
0
|
0
|
|
|
|
|
return '' unless @keys; |
856
|
0
|
|
|
|
|
|
$self->_pack_keys($namespace, $param->{index}, @keys); |
857
|
0
|
|
|
|
|
|
my $format = ""; |
858
|
0
|
0
|
|
|
|
|
if ($param->{format}) { #broken |
859
|
0
|
0
|
|
|
|
|
confess "broken" if $namespace->{long_tuple}; |
860
|
0
|
|
|
|
|
|
my $f = $namespace->{byfield_unpack_format}; |
861
|
0
|
|
|
|
|
|
$param->{unpack_format} = join '', map { $f->[$_->{field}] } @{$param->{format}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
862
|
0
|
|
|
|
|
|
$format = pack 'l*', scalar @{$param->{format}}, map { |
|
0
|
|
|
|
|
|
|
863
|
0
|
|
|
|
|
|
$_ = { %$_ }; |
864
|
0
|
0
|
|
|
|
|
if($_->{full}) { |
865
|
0
|
|
|
|
|
|
$_->{offset} = 0; |
866
|
0
|
|
|
|
|
|
$_->{length} = 'max'; |
867
|
|
|
|
|
|
|
} |
868
|
0
|
0
|
|
|
|
|
$_->{length} = 0x7FFFFFFF if $_->{length} eq 'max'; |
869
|
0
|
|
|
|
|
|
@$_{qw/field offset length/} |
870
|
0
|
|
|
|
|
|
} @{$param->{format}}; |
871
|
|
|
|
|
|
|
} |
872
|
0
|
|
0
|
|
|
|
return pack("LLLL a* La*", $namespace->{namespace}, $param->{index}->{id}, $param->{offset} || 0, $param->{limit} || ($param->{default_limit_by_keys} ? scalar(@keys) : 0x7FFFFFFF), $format, scalar(@keys), join('',@keys)); |
|
|
|
0
|
|
|
|
|
873
|
|
|
|
|
|
|
} |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
sub _PostSelect { |
876
|
0
|
|
|
0
|
|
|
my ($self, $r, $param, $namespace) = @_; |
877
|
0
|
0
|
|
|
|
|
if(!$param->{raw}) { |
878
|
0
|
|
|
|
|
|
my @utf8_fields = values %{$namespace->{utf8_fields}}; |
|
0
|
|
|
|
|
|
|
879
|
0
|
|
|
|
|
|
my $long_utf8_fields = $namespace->{long_utf8_fields}; |
880
|
0
|
0
|
0
|
|
|
|
if(@utf8_fields or $long_utf8_fields && @$long_utf8_fields) { |
|
|
|
0
|
|
|
|
|
881
|
0
|
|
|
|
|
|
my $long_tuple = $namespace->{long_tuple}; |
882
|
0
|
|
|
|
|
|
for my $row (@$r) { |
883
|
0
|
|
|
|
|
|
Encode::_utf8_on($row->[$_]) for @utf8_fields; |
884
|
0
|
0
|
0
|
|
|
|
if ($long_tuple && @$long_utf8_fields) { |
885
|
0
|
|
|
|
|
|
my $i = @{$namespace->{field_format}}; |
|
0
|
|
|
|
|
|
|
886
|
0
|
|
|
|
|
|
my $n = int( (@$row-$i-1) / @$long_utf8_fields ); |
887
|
0
|
|
|
|
|
|
Encode::_utf8_on($row->[$_]) for map do{ $a=$_; map $a+$i+@$long_utf8_fields*$_, 0..$n }, @$long_utf8_fields; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
} |
889
|
|
|
|
|
|
|
} |
890
|
|
|
|
|
|
|
} |
891
|
|
|
|
|
|
|
|
892
|
0
|
|
0
|
|
|
|
my $hashify = $param->{hashify} || $namespace->{hashify} || $self->{hashify}; |
893
|
0
|
0
|
|
|
|
|
if ($hashify) { |
|
|
0
|
|
|
|
|
|
894
|
0
|
|
|
|
|
|
$hashify->($namespace->{namespace}, $r); |
895
|
|
|
|
|
|
|
} elsif( $namespace->{fields} ) { |
896
|
0
|
|
|
|
|
|
my @f = @{$namespace->{fields}}; |
|
0
|
|
|
|
|
|
|
897
|
0
|
|
|
|
|
|
my @f_long; |
898
|
|
|
|
|
|
|
my $last; |
899
|
0
|
0
|
|
|
|
|
if ($namespace->{long_tuple}) { |
900
|
0
|
|
|
|
|
|
$last = pop @f; |
901
|
0
|
0
|
|
|
|
|
@f_long = @{$namespace->{long_fields}} if $namespace->{long_fields}; |
|
0
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
} |
903
|
0
|
|
|
|
|
|
for my $row (@$r) { |
904
|
0
|
|
|
|
|
|
my $h = { zip @{$namespace->{fields}}, @{[splice(@$row,0,0+@f)]} }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
905
|
0
|
0
|
|
|
|
|
if($last) { |
906
|
0
|
0
|
|
|
|
|
$row = [ map +{ zip @f_long, @{[splice(@$row,0,0+@f_long)]} }, 0..((@$row-1)/@f_long) ] if @f_long; |
|
0
|
|
|
|
|
|
|
907
|
0
|
|
|
|
|
|
$h->{$last} = $row; |
908
|
|
|
|
|
|
|
} |
909
|
0
|
|
|
|
|
|
$row = $h; |
910
|
|
|
|
|
|
|
} |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
} |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=pod |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=head3 Select |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
Select tuple(s) from storage |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
my $key = $id; |
922
|
|
|
|
|
|
|
my $key = [ $firstname, $lastname ]; |
923
|
|
|
|
|
|
|
my @keys = ($key, ...); |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
my $tuple = $box->Select($key) or $box->Error && die $box->ErrorStr; |
926
|
|
|
|
|
|
|
my $tuple = $box->Select($key, \%options) or $box->Error && die $box->ErrorStr; |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
my @tuples = $box->Select(@keys) or $box->Error && die $box->ErrorStr; |
929
|
|
|
|
|
|
|
my @tuples = $box->Select(@keys, \%options) or $box->Error && die $box->ErrorStr; |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
my $tuples = $box->Select(\@keys) or die $box->ErrorStr; |
932
|
|
|
|
|
|
|
my $tuples = $box->Select(\@keys, \%options) or die $box->ErrorStr; |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=over |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
=item B<$key>, B<@keys>, B<\@keys> |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
Specify keys to select. All keys must be defined. |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
Contextual behaviour: |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
=over |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
=item * |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
In scalar context, you can select one C<$key>, and the resulting tuple will be returned. |
947
|
|
|
|
|
|
|
Check C<< $box->Error >> to see if there was an error or there is just no such key |
948
|
|
|
|
|
|
|
in the storage |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
=item * |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
In list context, you can select several C<@keys>, and the resulting tuples will be returned. |
953
|
|
|
|
|
|
|
Check C<< $box->Error >> to see if there was an error or there is just no such keys |
954
|
|
|
|
|
|
|
in the storage |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
=item * |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
If you select C<< \@keys >> then C<< \@tuples >> will be returned upon success. C<< @tuples >> will |
959
|
|
|
|
|
|
|
be empty if there are no such keys, and false will be returned in case of error. |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
=back |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
Other notes: |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=over |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
=item * |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
If you select using index on multiple fields each C<< $key >> should be given as a key-tuple C<< $key = [ $key_field1, $key_field2, ... ] >>. |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=back |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
=item B<%options> |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
=over |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=item B => $space_id_uint32_or_name_string |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
Specify storage (by id or name) space to select from. |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
=item B => $index_id_uint32_or_name_string |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
Specify index (by id or name) to use. |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
=item B => $limit_uint32 |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
Max tuples to select. It is set to C<< MAX_INT32 >> by default. |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
=item B => $bool |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
Don't C (see L), disable L processing. |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
=item B => $by |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
Return a hashref of the resultset. If you C the result set, |
996
|
|
|
|
|
|
|
then C<$by> must be a field name of the hash you return, |
997
|
|
|
|
|
|
|
otherwise it must be a number of field of the tuple. |
998
|
|
|
|
|
|
|
C will be returned in case of error. |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
=back |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
=back |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
=cut |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
my @select_param_ok = qw/use_index raw want next_rows limit offset raise hashify timeout format hash_by callback return_fh default_limit_by_keys/; |
1007
|
|
|
|
|
|
|
sub Select { |
1008
|
0
|
0
|
|
0
|
1
|
|
confess q/Select isnt callable in void context/ unless defined wantarray; |
1009
|
0
|
|
|
|
|
|
my ($param, $namespace) = $_[0]->_validate_param(\@_, @select_param_ok); |
1010
|
0
|
|
|
|
|
|
my ($self, @keys) = @_; |
1011
|
0
|
0
|
|
|
|
|
local $self->{raise} = $param->{raise} if defined $param->{raise}; |
1012
|
0
|
0
|
0
|
|
|
|
@keys = @{$keys[0]} if @keys && ref $keys[0] eq 'ARRAY' and 1 == @{$param->{index}->{keys}} || (@keys && ref $keys[0]->[0] eq 'ARRAY'); |
|
0
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
1013
|
|
|
|
|
|
|
|
1014
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: SELECT(NS:$namespace->{namespace},IDX:$param->{use_index})[@{[map{ref$_?qq{[@$_]}:$_}@keys]}]") if $self->{debug} >= 3; |
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
|
1016
|
0
|
|
|
|
|
|
my ($msg,$payload); |
1017
|
0
|
0
|
|
|
|
|
if(exists $param->{next_rows}) { |
1018
|
0
|
0
|
0
|
|
|
|
confess "$self->{name}: One and only one key can be used to get N>0 rows after it" if @keys != 1 || !$param->{next_rows}; |
1019
|
0
|
|
|
|
|
|
$msg = 15; |
1020
|
0
|
|
|
|
|
|
$self->_pack_keys($namespace, $param->{index}, @keys); |
1021
|
0
|
|
|
|
|
|
$payload = pack("LL a*", $namespace->{namespace}, $param->{next_rows}, join('',@keys)), |
1022
|
|
|
|
|
|
|
} else { |
1023
|
0
|
|
|
|
|
|
$payload = $self->_PackSelect($param, $namespace, @keys); |
1024
|
0
|
0
|
|
|
|
|
$msg = $param->{format} ? 21 : 17; |
1025
|
|
|
|
|
|
|
} |
1026
|
|
|
|
|
|
|
|
1027
|
0
|
0
|
|
|
|
|
local $namespace->{unpack_format} = $param->{unpack_format} if $param->{unpack_format}; |
1028
|
|
|
|
|
|
|
|
1029
|
0
|
|
|
|
|
|
my $r = []; |
1030
|
|
|
|
|
|
|
|
1031
|
0
|
|
0
|
|
|
|
$param->{want} ||= !1; |
1032
|
0
|
|
|
|
|
|
my $wantarray = wantarray; |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
my $cb = sub { |
1035
|
0
|
|
|
0
|
|
|
my ($r) = (@_); |
1036
|
|
|
|
|
|
|
|
1037
|
0
|
0
|
|
|
|
|
$self->_PostSelect($r, $param, $namespace) if $r; |
1038
|
|
|
|
|
|
|
|
1039
|
0
|
0
|
0
|
|
|
|
if ($r && defined(my $p = $param->{hash_by})) { |
1040
|
0
|
|
|
|
|
|
my %h; |
1041
|
0
|
0
|
|
|
|
|
if (@$r) { |
1042
|
0
|
0
|
|
|
|
|
if (ref $r->[0] eq 'HASH') { |
|
|
0
|
|
|
|
|
|
1043
|
0
|
0
|
|
|
|
|
confess "Bad hash_by `$p' for HASH" unless exists $r->[0]->{$p}; |
1044
|
0
|
|
|
|
|
|
$h{$_->{$p}} = $_ for @$r; |
1045
|
|
|
|
|
|
|
} elsif (ref $r->[0] eq 'ARRAY') { |
1046
|
0
|
0
|
0
|
|
|
|
confess "Bad hash_by `$p' for ARRAY" unless $p =~ m/^\d+$/ && $p >= 0 && $p < @{$r->[0]}; |
|
0
|
|
0
|
|
|
|
|
1047
|
0
|
|
|
|
|
|
$h{$_->[$p]} = $_ for @$r; |
1048
|
|
|
|
|
|
|
} else { |
1049
|
0
|
|
|
|
|
|
confess "i dont know how to hash_by ".ref($r->[0]); |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
} |
1052
|
0
|
|
|
|
|
|
$r = \%h; |
1053
|
|
|
|
|
|
|
} |
1054
|
|
|
|
|
|
|
|
1055
|
0
|
0
|
|
|
|
|
if ($param->{callback}) { |
1056
|
0
|
|
|
|
|
|
return $param->{callback}->($r); |
1057
|
|
|
|
|
|
|
} |
1058
|
|
|
|
|
|
|
|
1059
|
0
|
0
|
0
|
|
|
|
if ($param->{return_fh} && ref $param->{return_fh} eq 'CODE') { |
1060
|
0
|
|
|
|
|
|
return $param->{return_fh}->($r); |
1061
|
|
|
|
|
|
|
} |
1062
|
|
|
|
|
|
|
|
1063
|
0
|
0
|
|
|
|
|
return unless $r; |
1064
|
|
|
|
|
|
|
|
1065
|
0
|
0
|
|
|
|
|
return $r if defined $param->{hash_by}; |
1066
|
0
|
0
|
|
|
|
|
return $r if $param->{want} eq 'arrayref'; |
1067
|
0
|
0
|
|
|
|
|
$wantarray = wantarray if $param->{return_fh}; |
1068
|
|
|
|
|
|
|
|
1069
|
0
|
0
|
|
|
|
|
if ($wantarray) { |
1070
|
0
|
|
|
|
|
|
return @{$r}; |
|
0
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
} else { |
1072
|
0
|
0
|
|
|
|
|
confess "$self->{name}: too many keys in scalar context" if @keys > 1; |
1073
|
0
|
|
|
|
|
|
return $r->[0]; |
1074
|
|
|
|
|
|
|
} |
1075
|
0
|
|
|
|
|
|
}; |
1076
|
|
|
|
|
|
|
|
1077
|
0
|
0
|
0
|
|
|
|
if (@keys && $payload) { |
1078
|
|
|
|
|
|
|
$r = $self->_chat( |
1079
|
|
|
|
|
|
|
msg => $msg, |
1080
|
|
|
|
|
|
|
payload => $payload, |
1081
|
0
|
|
|
0
|
|
|
unpack => sub { $self->_unpack_select($namespace, "SELECT", @_) }, |
1082
|
0
|
0
|
0
|
|
|
|
retry => $param->{return_fh} ? 1 : $self->{select_retry}, |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
timeout => $param->{timeout} || $self->{select_timeout}, |
1084
|
|
|
|
|
|
|
callback => $param->{callback} ? $cb : 0, |
1085
|
|
|
|
|
|
|
return_fh=> $param->{return_fh} ? $cb : 0, |
1086
|
|
|
|
|
|
|
) or return; |
1087
|
0
|
0
|
|
|
|
|
return $r if $param->{return_fh}; |
1088
|
0
|
0
|
|
|
|
|
return 1 if $param->{callback}; |
1089
|
|
|
|
|
|
|
} else { |
1090
|
0
|
|
|
|
|
|
$r = []; |
1091
|
|
|
|
|
|
|
} |
1092
|
|
|
|
|
|
|
|
1093
|
0
|
|
|
|
|
|
return $cb->($r); |
1094
|
|
|
|
|
|
|
} |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
sub SelectUnion { |
1097
|
0
|
|
|
0
|
0
|
|
confess "not supported yet"; |
1098
|
0
|
|
|
|
|
|
my ($param) = $_[0]->_validate_param(\@_, qw/raw raise/); |
1099
|
0
|
|
|
|
|
|
my ($self, @reqs) = @_; |
1100
|
0
|
0
|
|
|
|
|
return [] unless @reqs; |
1101
|
0
|
0
|
|
|
|
|
local $self->{raise} = $param->{raise} if defined $param->{raise}; |
1102
|
0
|
0
|
|
|
|
|
confess "bad param" if grep { ref $_ ne 'ARRAY' } @reqs; |
|
0
|
|
|
|
|
|
|
1103
|
0
|
|
0
|
|
|
|
$param->{want} ||= 0; |
1104
|
0
|
|
|
|
|
|
for my $req (@reqs) { |
1105
|
0
|
|
|
|
|
|
my ($param, $namespace) = $self->_validate_param($req, @select_param_ok); |
1106
|
0
|
|
|
|
|
|
$req = { |
1107
|
|
|
|
|
|
|
payload => $self->_PackSelect($param, $namespace, $req), |
1108
|
|
|
|
|
|
|
param => $param, |
1109
|
|
|
|
|
|
|
namespace => $namespace, |
1110
|
|
|
|
|
|
|
}; |
1111
|
|
|
|
|
|
|
} |
1112
|
0
|
|
|
|
|
|
my $r = $self->_chat( |
1113
|
|
|
|
|
|
|
msg => 18, |
1114
|
|
|
|
|
|
|
payload => pack("L (a*)*", scalar(@reqs), map { $_->{payload} } @reqs), |
1115
|
0
|
|
|
0
|
|
|
unpack => sub { $self->_unpack_select_multi([map { $_->{namespace} } @reqs], "SMULTI", @_) }, |
|
0
|
|
|
|
|
|
|
1116
|
0
|
0
|
0
|
|
|
|
retry => $self->{select_retry}, |
1117
|
|
|
|
|
|
|
timeout => $param->{select_timeout} || $self->{timeout}, |
1118
|
|
|
|
|
|
|
callback => $param->{callback}, |
1119
|
|
|
|
|
|
|
) or return; |
1120
|
0
|
0
|
|
|
|
|
confess __LINE__."$self->{name}: something wrong" if @$r != @reqs; |
1121
|
0
|
|
|
|
|
|
my $ea = each_arrayref $r, \@reqs; |
1122
|
0
|
|
|
|
|
|
while(my ($res, $req) = $ea->()) { |
1123
|
0
|
|
|
|
|
|
$self->_PostSelect($res, { %$param, %{$req->{param}} }, $req->{namespace}); |
|
0
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
} |
1125
|
0
|
|
|
|
|
|
return $r; |
1126
|
|
|
|
|
|
|
} |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
=pod |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=head3 Delete |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
Delete tuple from storage. Return false upon error. |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
my $n_deleted = $box->Delete($key) or die $box->ErrorStr; |
1135
|
|
|
|
|
|
|
my $n_deleted = $box->Delete($key, \%options) or die $box->ErrorStr; |
1136
|
|
|
|
|
|
|
warn "Nothing was deleted" unless int $n_deleted; |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
my $deleted_tuple_set = $box->Delete($key, { want_deleted_tuples => 1 }) or die $box->ErrorStr; |
1139
|
|
|
|
|
|
|
warn "Nothing was deleted" unless @$deleted_tuple_set; |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
=over |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
=item B<%options> |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
=over |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
=item B => $space_id_uint32_or_name_string |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
Specify storage space (by id or name) to work on. |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=item B => $bool |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
if C<$bool> then return deleted tuple. |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
=back |
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
=back |
1158
|
|
|
|
|
|
|
|
1159
|
|
|
|
|
|
|
=cut |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
sub Delete { |
1162
|
0
|
|
|
0
|
1
|
|
my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/want_deleted_tuple want_result raw/); |
1163
|
0
|
|
|
|
|
|
my ($self, $key) = @_; |
1164
|
|
|
|
|
|
|
|
1165
|
0
|
0
|
|
|
|
|
$param->{want_result} = $param->{want_deleted_tuple} if !defined $param->{want_result}; |
1166
|
|
|
|
|
|
|
|
1167
|
0
|
|
|
|
|
|
my $flags = 0; |
1168
|
0
|
0
|
|
|
|
|
$flags |= WANT_RESULT if $param->{want_result}; |
1169
|
|
|
|
|
|
|
|
1170
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: DELETE(NS:$namespace->{namespace},KEY:$key,F:$flags)") if $self->{debug} >= 3; |
1171
|
|
|
|
|
|
|
|
1172
|
0
|
0
|
|
|
|
|
confess "$self->{name}\->Delete: for now key cardinality of 1 is only allowed" unless 1 == @{$param->{index}->{keys}}; |
|
0
|
|
|
|
|
|
|
1173
|
0
|
|
|
|
|
|
$self->_pack_keys($namespace, $param->{index}, $key); |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
my $cb = sub { |
1176
|
0
|
|
|
0
|
|
|
my ($r) = @_; |
1177
|
|
|
|
|
|
|
|
1178
|
0
|
0
|
|
|
|
|
if($param->{want_result}) { |
1179
|
0
|
|
|
|
|
|
$self->_PostSelect($r, $param, $namespace); |
1180
|
0
|
|
0
|
|
|
|
$r = $r && $r->[0]; |
1181
|
|
|
|
|
|
|
} |
1182
|
|
|
|
|
|
|
|
1183
|
0
|
0
|
|
|
|
|
return $param->{callback}->($r) if $param->{callback}; |
1184
|
0
|
|
|
|
|
|
return $r; |
1185
|
0
|
|
|
|
|
|
}; |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
my $r = $self->_chat( |
1188
|
|
|
|
|
|
|
msg => $flags ? 21 : 20, |
1189
|
|
|
|
|
|
|
payload => $flags ? pack("L L a*", $namespace->{namespace}, $flags, $key) : pack("L a*", $namespace->{namespace}, $key), |
1190
|
0
|
|
|
0
|
|
|
unpack => sub { $self->_unpack_affected($flags, $namespace, @_) }, |
1191
|
0
|
0
|
0
|
|
|
|
callback => $param->{callback} && $cb, |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
) or return; |
1193
|
|
|
|
|
|
|
|
1194
|
0
|
0
|
|
|
|
|
return 1 if $param->{callback}; |
1195
|
0
|
|
|
|
|
|
return $cb->($r); |
1196
|
|
|
|
|
|
|
} |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
sub OP_SET () { 0 } |
1199
|
|
|
|
|
|
|
sub OP_ADD () { 1 } |
1200
|
|
|
|
|
|
|
sub OP_AND () { 2 } |
1201
|
|
|
|
|
|
|
sub OP_XOR () { 3 } |
1202
|
|
|
|
|
|
|
sub OP_OR () { 4 } |
1203
|
|
|
|
|
|
|
sub OP_SPLICE () { 5 } |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
my %update_ops = ( |
1206
|
|
|
|
|
|
|
set => OP_SET, |
1207
|
|
|
|
|
|
|
add => OP_ADD, |
1208
|
|
|
|
|
|
|
and => OP_AND, |
1209
|
|
|
|
|
|
|
xor => OP_XOR, |
1210
|
|
|
|
|
|
|
or => OP_OR, |
1211
|
|
|
|
|
|
|
splice => sub { |
1212
|
|
|
|
|
|
|
confess "value for operation splice must be an ARRAYREF of " if ref $_[0] ne 'ARRAY' || @{$_[0]} < 1; |
1213
|
|
|
|
|
|
|
$_[0]->[0] = 0x7FFFFFFF unless defined $_[0]->[0]; |
1214
|
|
|
|
|
|
|
$_[0]->[0] = pack 'l', $_[0]->[0]; |
1215
|
|
|
|
|
|
|
$_[0]->[1] = defined $_[0]->[1] ? pack 'l', $_[0]->[1] : ''; |
1216
|
|
|
|
|
|
|
$_[0]->[2] = '' unless defined $_[0]->[2]; |
1217
|
|
|
|
|
|
|
return (OP_SPLICE, [ pack '(w/a*)*', @{$_[0]} ]); |
1218
|
|
|
|
|
|
|
}, |
1219
|
|
|
|
|
|
|
append => sub { splice => [undef, 0, $_[0]] }, |
1220
|
|
|
|
|
|
|
prepend => sub { splice => [0, 0, $_[0]] }, |
1221
|
|
|
|
|
|
|
cutbeg => sub { splice => [0, $_[0], '' ] }, |
1222
|
|
|
|
|
|
|
cutend => sub { splice => [-$_[0], $_[0], '' ] }, |
1223
|
|
|
|
|
|
|
substr => 'splice', |
1224
|
|
|
|
|
|
|
); |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
!ref $_ && m/^\D/ and $_ = $update_ops{$_} || die "bad link" for values %update_ops; |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
my %update_arg_fmt = ( |
1229
|
|
|
|
|
|
|
(map { $_ => 'l' } OP_ADD), |
1230
|
|
|
|
|
|
|
(map { $_ => 'L' } OP_AND, OP_XOR, OP_OR), |
1231
|
|
|
|
|
|
|
); |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
my %ops_type = ( |
1234
|
|
|
|
|
|
|
(map { $_ => 'any' } OP_SET), |
1235
|
|
|
|
|
|
|
(map { $_ => 'number' } OP_ADD, OP_AND, OP_XOR, OP_OR), |
1236
|
|
|
|
|
|
|
(map { $_ => 'string' } OP_SPLICE), |
1237
|
|
|
|
|
|
|
); |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
BEGIN { |
1240
|
1
|
|
|
1
|
|
5
|
for my $op (qw/Append Prepend Cutbeg Cutend Substr/) { |
1241
|
5
|
50
|
|
0
|
0
|
506
|
eval q/ |
|
0
|
0
|
|
0
|
0
|
|
|
|
0
|
0
|
|
0
|
0
|
|
|
|
0
|
0
|
|
0
|
0
|
|
|
|
0
|
0
|
|
0
|
0
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
sub /.$op.q/ { |
1243
|
|
|
|
|
|
|
my $param = ref $_[-1] eq 'HASH' ? pop : {}; |
1244
|
|
|
|
|
|
|
my ($self, $key, $field_num, $val) = @_; |
1245
|
|
|
|
|
|
|
$self->UpdateMulti($key, [ $field_num => /.lc($op).q/ => $val ], $param); |
1246
|
|
|
|
|
|
|
} |
1247
|
|
|
|
|
|
|
1; |
1248
|
|
|
|
|
|
|
/ or die $@; |
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
} |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
=pod |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
=head3 UpdateMulti |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
Apply several update operations to a tuple. |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
my @op = ([ f1 => add => 10 ], [ f1 => and => 0xFF], [ f2 => set => time() ], [ misc_string => cutend => 3 ]); |
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
my $n_updated = $box->UpdateMulti($key, @op) or die $box->ErrorStr; |
1261
|
|
|
|
|
|
|
my $n_updated = $box->UpdateMulti($key, @op, \%options) or die $box->ErrorStr; |
1262
|
|
|
|
|
|
|
warn "Nothing was updated" unless int $n_updated; |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
my $updated_tuple_set = $box->UpdateMulti($key, @op, { want_result => 1 }) or die $box->ErrorStr; |
1265
|
|
|
|
|
|
|
warn "Nothing was updated" unless @$updated_tuple_set; |
1266
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
Different fields can be updated at one shot. |
1268
|
|
|
|
|
|
|
The same field can be updated more than once. |
1269
|
|
|
|
|
|
|
All update operations are done atomically. |
1270
|
|
|
|
|
|
|
Returns false upon error. |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
=over |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
=item B<@op> = ([ $field => $op => $value ], ...) |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
=over |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
=item B<$field> |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
Field-to-update number or name (see L, L). |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
=item B<$op> |
1283
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
=over |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
=item B |
1287
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
Set C<< $field >> to C<< $value >> |
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
=item B, B, B, B |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
Apply an arithmetic operation to C<< $field >> with argument C<< $value >> |
1293
|
|
|
|
|
|
|
Currently arithmetic operations are supported only for int32 (4-byte length) fields (and C<$value>s too) |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
=item B, B |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
Apply a perl-like L operation to C<< $field >>. B<$value> = [$OFFSET, $LENGTH, $REPLACE_WITH]. |
1298
|
|
|
|
|
|
|
substr is just an alias. |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
=item B, B |
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
Append or prepend C<< $field >> with C<$value> string. |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
=item B, B |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
Cut C<< $value >> bytes from beginning or end of C<< $field >>. |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
=back |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
=back |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
=item B<%options> |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
=over |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
=item B => $space_id_uint32_or_name_string |
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
Specify storage space (by id or name) to work on. |
1319
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
=item B => $bool |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
if C<$bool> then return updated tuple. |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
=back |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
=cut |
1327
|
|
|
|
|
|
|
|
1328
|
|
|
|
|
|
|
sub UpdateMulti { |
1329
|
0
|
|
|
0
|
1
|
|
my ($param, $namespace) = $_[0]->_validate_param(\@_, qw/want_updated_tuple want_result _flags raw/); |
1330
|
0
|
|
|
|
|
|
my ($self, $key, @op) = @_; |
1331
|
|
|
|
|
|
|
|
1332
|
0
|
0
|
|
|
|
|
$self->_debug("$self->{name}: UPDATEMULTI(NS:$namespace->{namespace},KEY:$key)[@{[map{$_?qq{[@$_]}:q{-}}@op]}]") if $self->{debug} >= 3; |
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
|
1334
|
0
|
0
|
|
|
|
|
confess "$self->{name}\->UpdateMulti: for now key cardinality of 1 is only allowed" unless 1 == @{$param->{index}->{keys}}; |
|
0
|
|
|
|
|
|
|
1335
|
0
|
0
|
|
|
|
|
confess "$self->{name}: too many op" if scalar @op > 128; |
1336
|
|
|
|
|
|
|
|
1337
|
0
|
0
|
|
|
|
|
$param->{want_result} = $param->{want_updated_tuple} if !defined $param->{want_result}; |
1338
|
|
|
|
|
|
|
|
1339
|
0
|
|
0
|
|
|
|
my $flags = $param->{_flags} || 0; |
1340
|
0
|
0
|
|
|
|
|
$flags |= WANT_RESULT if $param->{want_result}; |
1341
|
|
|
|
|
|
|
|
1342
|
0
|
|
|
|
|
|
my $fmt = $namespace->{field_format}; |
1343
|
0
|
|
|
|
|
|
my $long_fmt = $namespace->{long_field_format}; |
1344
|
0
|
|
|
|
|
|
my $fields_hash = $namespace->{fields_hash}; |
1345
|
0
|
|
|
|
|
|
my $long_fields_hash = $namespace->{long_fields_hash}; |
1346
|
|
|
|
|
|
|
|
1347
|
0
|
|
|
|
|
|
foreach (@op) { |
1348
|
0
|
0
|
0
|
|
|
|
confess "$self->{name}: bad op <$_>" if ref ne 'ARRAY' or @$_ != 3; |
1349
|
0
|
|
|
|
|
|
my ($field_num, $op, $value) = @$_; |
1350
|
0
|
|
|
|
|
|
my $long_field_num; |
1351
|
|
|
|
|
|
|
|
1352
|
0
|
0
|
0
|
|
|
|
if(ref $field_num eq 'ARRAY' && $long_fmt) { |
|
|
0
|
|
|
|
|
|
1353
|
0
|
|
|
|
|
|
my ($i, $n) = @$field_num; |
1354
|
0
|
0
|
|
|
|
|
if($n =~ m/^[A-Za-z]/) { |
1355
|
0
|
0
|
|
|
|
|
confess "no such long field $n in space $namespace->{name}($namespace->{space})" unless exists $long_fields_hash->{$n}; |
1356
|
0
|
|
|
|
|
|
$n = $long_fields_hash->{$n}; |
1357
|
|
|
|
|
|
|
} |
1358
|
0
|
|
|
|
|
|
$field_num = $n + @$fmt + $i*@$long_fmt; |
1359
|
|
|
|
|
|
|
} elsif($field_num =~ m/^[A-Za-z]/) { |
1360
|
0
|
0
|
|
|
|
|
confess "no such field $field_num in space $namespace->{name}($namespace->{space})" unless exists $fields_hash->{$field_num}; |
1361
|
0
|
|
|
|
|
|
$field_num = $fields_hash->{$field_num}; |
1362
|
|
|
|
|
|
|
} |
1363
|
|
|
|
|
|
|
|
1364
|
0
|
0
|
0
|
|
|
|
$long_field_num = ($field_num - @$fmt) % @$long_fmt if $field_num >= @$fmt && $long_fmt; |
1365
|
|
|
|
|
|
|
|
1366
|
0
|
0
|
|
|
|
|
my $field_type = $namespace->{string_keys}->{$field_num} ? 'string' : 'number'; |
1367
|
|
|
|
|
|
|
|
1368
|
0
|
|
|
|
|
|
my $is_array = 0; |
1369
|
0
|
0
|
|
|
|
|
if ($op eq 'bit_set') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1370
|
0
|
|
|
|
|
|
$op = OP_OR; |
1371
|
|
|
|
|
|
|
} elsif ($op eq 'bit_clear') { |
1372
|
0
|
|
|
|
|
|
$op = OP_AND; |
1373
|
0
|
|
|
|
|
|
$value = ~$value; |
1374
|
|
|
|
|
|
|
} elsif ($op =~ /^num_(add|sub)$/) { |
1375
|
0
|
0
|
|
|
|
|
$value = -$value if $1 eq 'sub'; |
1376
|
0
|
|
|
|
|
|
$op = OP_ADD; |
1377
|
|
|
|
|
|
|
} else { |
1378
|
0
|
0
|
|
|
|
|
confess "$self->{name}: bad op <$op>" unless exists $update_ops{$op}; |
1379
|
0
|
|
|
|
|
|
$op = $update_ops{$op}; |
1380
|
|
|
|
|
|
|
} |
1381
|
|
|
|
|
|
|
|
1382
|
0
|
|
|
|
|
|
while(ref $op eq 'CODE') { |
1383
|
0
|
|
|
|
|
|
($op, $value) = &$op($value); |
1384
|
0
|
0
|
|
|
|
|
$op = $update_ops{$op} if exists $update_ops{$op}; |
1385
|
|
|
|
|
|
|
} |
1386
|
|
|
|
|
|
|
|
1387
|
0
|
0
|
0
|
|
|
|
confess "Are you sure you want to apply `$ops_type{$op}' operation to $field_type field?" if $ops_type{$op} ne $field_type && $ops_type{$op} ne 'any'; |
1388
|
|
|
|
|
|
|
|
1389
|
0
|
0
|
|
|
|
|
$value = [ $value ] unless ref $value; |
1390
|
0
|
0
|
|
|
|
|
confess "dunno what to do with ref `$value'" if ref $value ne 'ARRAY'; |
1391
|
|
|
|
|
|
|
|
1392
|
0
|
0
|
0
|
|
|
|
confess "bad fieldnum: $field_num" if $field_num >= @$fmt && !defined $long_field_num; |
1393
|
0
|
0
|
0
|
|
|
|
confess "bad long_fieldnum: $long_field_num" if defined $long_field_num && $long_field_num >= @$long_fmt; |
1394
|
|
|
|
|
|
|
|
1395
|
0
|
|
0
|
|
|
|
$value = pack($update_arg_fmt{$op} || ($field_num < @$fmt ? $fmt->[$field_num] : $long_fmt->[$long_field_num]), @$value); |
1396
|
0
|
|
|
|
|
|
$_ = pack('LCw/a*', $field_num, $op, $value); |
1397
|
|
|
|
|
|
|
} |
1398
|
|
|
|
|
|
|
|
1399
|
0
|
|
|
|
|
|
$self->_pack_keys($namespace, $param->{index}, $key); |
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
my $cb = sub { |
1402
|
0
|
|
|
0
|
|
|
my ($r) = @_; |
1403
|
|
|
|
|
|
|
|
1404
|
0
|
0
|
|
|
|
|
if($param->{want_result}) { |
1405
|
0
|
|
|
|
|
|
$self->_PostSelect($r, $param, $namespace); |
1406
|
0
|
|
0
|
|
|
|
$r = $r && $r->[0]; |
1407
|
|
|
|
|
|
|
} |
1408
|
|
|
|
|
|
|
|
1409
|
0
|
0
|
|
|
|
|
return $param->{callback}->($r) if $param->{callback}; |
1410
|
0
|
|
|
|
|
|
return $r; |
1411
|
0
|
|
|
|
|
|
}; |
1412
|
|
|
|
|
|
|
|
1413
|
|
|
|
|
|
|
my $r = $self->_chat( |
1414
|
|
|
|
|
|
|
msg => 19, |
1415
|
|
|
|
|
|
|
payload => pack("LL a* L (a*)*" , $namespace->{namespace}, $flags, $key, scalar(@op), @op), |
1416
|
0
|
|
|
0
|
|
|
unpack => sub { $self->_unpack_affected($flags, $namespace, @_) }, |
1417
|
0
|
0
|
0
|
|
|
|
callback => $param->{callback} && $cb, |
1418
|
|
|
|
|
|
|
) or return; |
1419
|
|
|
|
|
|
|
|
1420
|
0
|
0
|
|
|
|
|
return 1 if $param->{callback}; |
1421
|
0
|
|
|
|
|
|
return $cb->($r); |
1422
|
|
|
|
|
|
|
} |
1423
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
sub Update { |
1425
|
0
|
0
|
|
0
|
0
|
|
my $param = ref $_[-1] eq 'HASH' ? pop : {}; |
1426
|
0
|
|
|
|
|
|
my ($self, $key, $field_num, $value) = @_; |
1427
|
0
|
|
|
|
|
|
$self->UpdateMulti($key, [$field_num => set => $value ], $param); |
1428
|
|
|
|
|
|
|
} |
1429
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
sub AndXorAdd { |
1431
|
0
|
0
|
|
0
|
0
|
|
my $param = ref $_[-1] eq 'HASH' ? pop : {}; |
1432
|
0
|
|
|
|
|
|
my ($self, $key, $field_num, $and, $xor, $add) = @_; |
1433
|
0
|
|
|
|
|
|
my @upd; |
1434
|
0
|
0
|
|
|
|
|
push @upd, [$field_num => and => $and] if defined $and; |
1435
|
0
|
0
|
|
|
|
|
push @upd, [$field_num => xor => $xor] if defined $xor; |
1436
|
0
|
0
|
|
|
|
|
push @upd, [$field_num => add => $add] if defined $add; |
1437
|
0
|
|
|
|
|
|
$self->UpdateMulti($key, @upd, $param); |
1438
|
|
|
|
|
|
|
} |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
sub Bit { |
1441
|
0
|
0
|
|
0
|
0
|
|
my $param = ref $_[-1] eq 'HASH' ? pop : {}; |
1442
|
0
|
|
|
|
|
|
my ($self, $key, $field_num, %arg) = @_; |
1443
|
0
|
0
|
|
|
|
|
confess "$self->{name}: unknown op '@{[keys %arg]}'" if grep { not /^(bit_clear|bit_set|set)$/ } keys(%arg); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
|
1445
|
0
|
|
0
|
|
|
|
$arg{bit_clear} ||= 0; |
1446
|
0
|
|
0
|
|
|
|
$arg{bit_set} ||= 0; |
1447
|
0
|
|
|
|
|
|
my @op; |
1448
|
0
|
0
|
|
|
|
|
push @op, [$field_num => set => $arg{set}] if exists $arg{set}; |
1449
|
0
|
0
|
|
|
|
|
push @op, [$field_num => bit_clear => $arg{bit_clear}] if $arg{bit_clear}; |
1450
|
0
|
0
|
|
|
|
|
push @op, [$field_num => bit_set => $arg{bit_set}] if $arg{bit_set}; |
1451
|
|
|
|
|
|
|
|
1452
|
0
|
|
|
|
|
|
$self->UpdateMulti($key, @op, $param); |
1453
|
|
|
|
|
|
|
} |
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
sub Num { |
1456
|
0
|
0
|
|
0
|
0
|
|
my $param = ref $_[-1] eq 'HASH' ? pop : {}; |
1457
|
0
|
|
|
|
|
|
my ($self, $key, $field_num, %arg) = @_; |
1458
|
0
|
0
|
|
|
|
|
confess "$self->{name}: unknown op '@{[keys %arg]}'" if grep { not /^(num_add|num_sub|set)$/ } keys(%arg); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
|
1460
|
0
|
|
0
|
|
|
|
$arg{num_add} ||= 0; |
1461
|
0
|
|
0
|
|
|
|
$arg{num_sub} ||= 0; |
1462
|
|
|
|
|
|
|
|
1463
|
0
|
|
|
|
|
|
$arg{num_add} -= $arg{num_sub}; |
1464
|
0
|
|
|
|
|
|
my @op; |
1465
|
0
|
0
|
|
|
|
|
push @op, [$field_num => set => $arg{set}] if exists $arg{set}; |
1466
|
0
|
|
|
|
|
|
push @op, [$field_num => num_add => $arg{num_add}]; # if $arg{num_add}; |
1467
|
0
|
|
|
|
|
|
$self->UpdateMulti($key, @op, $param); |
1468
|
|
|
|
|
|
|
} |
1469
|
|
|
|
|
|
|
|
1470
|
|
|
|
|
|
|
=head2 AnyEvent |
1471
|
|
|
|
|
|
|
|
1472
|
|
|
|
|
|
|
C<< Insert, UpdateMulti, Select, Delete, Call >> methods can be given the following options: |
1473
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
=over |
1475
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
=item B => sub { my ($data, $error) = @_; } |
1477
|
|
|
|
|
|
|
|
1478
|
|
|
|
|
|
|
Do an async request using AnyEvent. |
1479
|
|
|
|
|
|
|
C<< $data >> contains unpacked and processed according to request options data. |
1480
|
|
|
|
|
|
|
C<< $error >> contains a message string in case of error. |
1481
|
|
|
|
|
|
|
Set up C<< raise => 0 >> to use this option. |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
=back |
1484
|
|
|
|
|
|
|
|
1485
|
|
|
|
|
|
|
=head2 "Continuations" |
1486
|
|
|
|
|
|
|
|
1487
|
|
|
|
|
|
|
C<< Select >> methods can be given the following options: |
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
=over |
1490
|
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
=item B => 1 |
1492
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
The request does only send operation on network, and returns |
1494
|
|
|
|
|
|
|
C<< { fh => $IO_Handle, continue => $code } >> or false if send operation failed. |
1495
|
|
|
|
|
|
|
C<< $code >> reads data from network, unpacks, processes according to options and returns it. |
1496
|
|
|
|
|
|
|
|
1497
|
|
|
|
|
|
|
You should handle timeouts and retries manually (using select() call for example). |
1498
|
|
|
|
|
|
|
Usage example: |
1499
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
my $continuation = $box->Select(13,{ return_fh => 1 }); |
1501
|
|
|
|
|
|
|
ok $continuation, "select/continuation"; |
1502
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
my $rin = ''; |
1504
|
|
|
|
|
|
|
vec($rin,$continuation->{fh}->fileno,1) = 1; |
1505
|
|
|
|
|
|
|
my $ein = $rin; |
1506
|
|
|
|
|
|
|
ok 0 <= select($rin,undef,$ein,2), "select/continuation/select"; |
1507
|
|
|
|
|
|
|
|
1508
|
|
|
|
|
|
|
my $res = $continuation->{continue}->(); |
1509
|
|
|
|
|
|
|
use Data::Dumper; |
1510
|
|
|
|
|
|
|
is_deeply $res, [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], "select/continuation/result"; |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
=back |
1513
|
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
=head2 LongTuple |
1515
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
If C given to L, or C given to L ends with a star (C<< * >>) |
1517
|
|
|
|
|
|
|
I is enabled. Last field or group of fields of C represent variable-length |
1518
|
|
|
|
|
|
|
tail of the tuple. C option given to L will fold the tail into array of hashes. |
1519
|
|
|
|
|
|
|
|
1520
|
|
|
|
|
|
|
$box->Insert(1,"2",3); #1 |
1521
|
|
|
|
|
|
|
$box->Insert(3,"2",3,4,5); #2 |
1522
|
|
|
|
|
|
|
$box->Insert(5,"2",3,4,5,6,7); #3 |
1523
|
|
|
|
|
|
|
|
1524
|
|
|
|
|
|
|
If we set up |
1525
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
format => "L&CL*", |
1527
|
|
|
|
|
|
|
fields => [qw/ a b c d /], # d is the folding field here |
1528
|
|
|
|
|
|
|
# no long_fields - no folding into hash |
1529
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
we'll get: |
1531
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
$result = $box->Select([1,2,3,4,5]); |
1533
|
|
|
|
|
|
|
$result = [ |
1534
|
|
|
|
|
|
|
{ a => 1, b => "2", c => 3, d => [] }, #1 |
1535
|
|
|
|
|
|
|
{ a => 3, b => "2", c => 3, d => [4,5] }, #2 |
1536
|
|
|
|
|
|
|
{ a => 5, b => "2", c => 3, d => [4,5,6,7] }, #3 |
1537
|
|
|
|
|
|
|
]; |
1538
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
And if we set up |
1540
|
|
|
|
|
|
|
|
1541
|
|
|
|
|
|
|
format => "L&C(LL)*", |
1542
|
|
|
|
|
|
|
fields => [qw/ a b c d /], # d is the folding field here |
1543
|
|
|
|
|
|
|
long_fields => [qw/ d1 d2 /], |
1544
|
|
|
|
|
|
|
|
1545
|
|
|
|
|
|
|
we'll get: |
1546
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
$result = [ |
1548
|
|
|
|
|
|
|
{ a => 1, b => "2", c => 3, d => [] }, #1 |
1549
|
|
|
|
|
|
|
{ a => 3, b => "2", c => 3, d => [{d1=>4, d2=>5}] }, #2 |
1550
|
|
|
|
|
|
|
{ a => 5, b => "2", c => 3, d => [{d1=>4, d2=>5}, {d1=>6, d2=>7}] }, #3 |
1551
|
|
|
|
|
|
|
]; |
1552
|
|
|
|
|
|
|
|
1553
|
|
|
|
|
|
|
L can be given a field number in several ways: |
1554
|
|
|
|
|
|
|
|
1555
|
|
|
|
|
|
|
=over |
1556
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
=item $linear_index_int |
1558
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
$box->UpdateMulti(5, [ 5 => set => $val ]) #3: set 6 to $val |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
=item an arrayref of [$index_of_folded_subtuple_int, $long_field_name_str_or_index_int] |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
$box->UpdateMulti(5, [ [1,0] => set => $val ]) #3: set 6 to $val |
1564
|
|
|
|
|
|
|
$box->UpdateMulti(5, [ [1,'d1'] => set => $val ]) #3: set 6 to $val |
1565
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
=back |
1567
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
=head2 utf8 |
1569
|
|
|
|
|
|
|
|
1570
|
|
|
|
|
|
|
Utf8 strings are supported very simply. When pushing any data to tarantool (with any query, read or write), |
1571
|
|
|
|
|
|
|
the utf8 flag is set off, so all data is pushed as bytestring. When reading response, for fields marked |
1572
|
|
|
|
|
|
|
a dollar sign C<< $ >> (see L) (including such in L tail) utf8 flag is set on. |
1573
|
|
|
|
|
|
|
That's all. Validity is on your own. |
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
=head1 LICENCE AND COPYRIGHT |
1577
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. |
1579
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
=head1 SEE ALSO |
1581
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
=over |
1583
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
=item * |
1585
|
|
|
|
|
|
|
|
1586
|
|
|
|
|
|
|
L |
1587
|
|
|
|
|
|
|
|
1588
|
|
|
|
|
|
|
=item * |
1589
|
|
|
|
|
|
|
|
1590
|
|
|
|
|
|
|
L |
1591
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
=back |
1593
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
=cut |
1595
|
|
|
|
|
|
|
|
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
1; |