File Coverage

blib/lib/MR/Tarantool/Box.pm
Criterion Covered Total %
statement 33 657 5.0
branch 1 470 0.2
condition 0 275 0.0
subroutine 12 58 20.6
pod 10 21 47.6
total 56 1481 3.7


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;