File Coverage

blib/lib/MCE/Shared/Minidb.pm
Criterion Covered Total %
statement 29 491 5.9
branch 0 378 0.0
condition 0 56 0.0
subroutine 10 72 13.8
pod 52 52 100.0
total 91 1049 8.6


line stmt bran cond sub pod time code
1             ###############################################################################
2             ## ----------------------------------------------------------------------------
3             ## A pure-Perl in-memory data store.
4             ##
5             ###############################################################################
6              
7             package MCE::Shared::Minidb;
8              
9 1     1   1151 use strict;
  1         2  
  1         26  
10 1     1   4 use warnings;
  1         1  
  1         19  
11              
12 1     1   16 use 5.010001;
  1         3  
13              
14 1     1   3 no warnings qw( threads recursion uninitialized numeric );
  1         2  
  1         51  
15              
16             our $VERSION = '1.885';
17              
18 1     1   6 use MCE::Shared::Base ();
  1         2  
  1         34  
19 1     1   6 use base 'MCE::Shared::Base::Common';
  1         2  
  1         289  
20              
21 1     1   465 use MCE::Shared::Ordhash ();
  1         2  
  1         18  
22 1     1   429 use MCE::Shared::Array ();
  1         2  
  1         19  
23 1     1   391 use MCE::Shared::Hash ();
  1         2  
  1         36  
24              
25             use overload (
26 1         4 q("") => \&MCE::Shared::Base::_stringify,
27             q(0+) => \&MCE::Shared::Base::_numify,
28             fallback => 1
29 1     1   5 );
  1         1  
30              
31             sub new {
32             # Parallel Hashes: [ HoH, HoA ]
33 0     0 1   bless [
34             MCE::Shared::Ordhash->new(), # Hash of Hashes (HoH)
35             MCE::Shared::Ordhash->new(), # Hash of Lists (HoA)
36             ], shift;
37             }
38              
39             ###############################################################################
40             ## ----------------------------------------------------------------------------
41             ## Private methods.
42             ##
43             ###############################################################################
44              
45             # _hfind ( { getkeys => 1 }, "query string" )
46             # _hfind ( { getvals => 1 }, "query string" )
47             # _hfind ( "query string" ) # pairs
48              
49             sub _hfind {
50 0     0     my $self = shift;
51 0 0         my $params = ref($_[0]) eq 'HASH' ? shift : {};
52              
53 0 0         if ( @_ == 2 ) {
54 0           my $key = shift;
55 0 0         return () unless exists($self->[0][0]{ $key });
56 0           $self->[0][0]{ $key }->_find($params, @_);
57             }
58             else {
59 0           my $query = shift;
60 0           $params->{'hfind'} = undef;
61              
62 0           MCE::Shared::Base::_find_hash(
63             $self->[0][0], $params, $query, $self->[0]
64             );
65             }
66             }
67              
68             # _lfind ( { getkeys => 1 }, "query string" )
69             # _lfind ( { getvals => 1 }, "query string" )
70             # _lfind ( "query string" ) # pairs
71              
72             sub _lfind {
73 0     0     my $self = shift;
74 0 0         my $params = ref($_[0]) eq 'HASH' ? shift : {};
75              
76 0 0         if ( @_ == 2 ) {
77 0           my $key = shift;
78 0 0         return () unless exists($self->[1][0]{ $key });
79 0           $self->[1][0]{ $key }->_find($params, @_);
80             }
81             else {
82 0           my $query = shift;
83 0           $params->{'lfind'} = undef;
84              
85 0           MCE::Shared::Base::_find_hash(
86             $self->[1][0], $params, $query, $self->[1]
87             );
88             }
89             }
90              
91             # _new_hash ( ) applies to HoH
92              
93             sub _new_hash {
94 0     0     MCE::Shared::Hash->new();
95             }
96              
97             # _new_list ( ) applies to HoA
98              
99             sub _new_list {
100 0     0     MCE::Shared::Array->new();
101             }
102              
103             # _qparse ( "select string" )
104             #
105             # The select_aref and select_href methods take a select string supporting
106             # field names or list indices and optionally sort modifiers. The syntax for
107             # the query string, between :WHERE and :ORDER BY, is the same as described
108             # in the documentation under the section labeled SYNTAX for QUERY STRING.
109             #
110             # The modifiers :WHERE, :AND, :OR, ORDER BY, ASC, DESC, ALPHA may be written
111             # using mixed case. e.g. :Where
112             #
113             # o Hash of Hashes (HoH)
114             # "f1 f2 f3 :WHERE f4 > 20 :AND key =~ /foo/ :ORDER BY f5 DESC ALPHA"
115             # "f5 f1 f2 :WHERE fN > 40 :AND key =~ /bar/ :ORDER BY key ALPHA"
116             # "f5 f1 f2 :WHERE fN > 40 :AND key =~ /bar/"
117             # "f5 f1 f2"
118             #
119             # * key matches on keys stored in the primary level hash (H)oH
120             #
121             # o Hash of Lists (HoA)
122             # "17 15 11 :where 12 > 20 :and key =~ /foo/ :order by 10 desc alpha"
123             # "17 15 11 :where 12 > 40 :and key =~ /bar/ :order by key alpha"
124             # "17 15 11 :where 12 > 40 :and key =~ /bar/"
125             # "17 15 11"
126             #
127             # * key matches on keys stored in the primary level hash (H)oA
128             # * above, list indices are given as 17, 15, 11, 12, and 10
129             # * the shorter form is allowed e.g. "4 > 20 :AND key =~ /baz/"
130              
131             sub _qparse {
132 0     0     my ( $q ) = @_;
133 0           my ( $f, $w, $o );
134              
135 0 0         if ( $q =~ /^([\S ]*):where[ ]+(.+):order by[ ]+(.+)/i ) {
    0          
    0          
    0          
    0          
136 0           ( $f, $w, $o ) = ( $1, $2, $3 );
137             }
138             elsif ( $q =~ /^([\S ]*):where[ ]+(.+)/i ) {
139 0           ( $f, $w ) = ( $1, $2 );
140             }
141             elsif ( $q =~ /^([\S ]*):order by[ ]+(.+)/i ) {
142 0           ( $f, $o ) = ( $1, $2 );
143             }
144             elsif ( $q =~ /^((?:key|\S+)[ ]+(?:=|!|<|>|e|n|l|g)\S?[ ]+\S.*)/ ) {
145 0           ( $w ) = ( $1 );
146             }
147             elsif ( $q =~ /^([\S ]*)/ ) {
148 0           ( $f ) = ( $1 );
149             }
150              
151 0           $f =~ s/[ ]+$//, $w =~ s/[ ]+$//, $o =~ s/[ ]+$//;
152              
153 0           return ( $f, $w, $o );
154             }
155              
156             # _hselect_aref ( "select string" ), see _qparse for description
157             #
158             # returns an array containing [ key, aref ] pairs
159              
160             sub _hselect_aref {
161 0     0     my ( $self, $query ) = @_;
162 0           my ( $f, $w, $o ) = _qparse($query);
163              
164 0           my @fields = split(' ', $f);
165 0           my $data = $self->[0][0];
166              
167 0 0         unless ( @fields ) {
168 0           warn("_hselect_aref: must specify fieldname(s)");
169 0           return ();
170             }
171              
172 0 0         if ( length $w ) {
173 0           my %match = map { $_ => 1 } ( $self->hkeys($w) );
  0            
174 0 0         map { !exists $match{$_} ? () : do {
  0 0          
175 0           my ( $k, @ret ) = ( $_ );
176 0           push @ret, $data->{$k}{$_} for @fields;
177 0           [ $k, \@ret ];
178             };
179             } ( length $o ? $self->hsort($o) : $self->hkeys() );
180             }
181             else {
182 0 0         map { my ( $k, @ret ) = ( $_ );
  0            
183 0           push @ret, $data->{$k}{$_} for @fields;
184 0           [ $k, \@ret ];
185             } ( length $o ? $self->hsort($o) : $self->hkeys() );
186             }
187             }
188              
189             # _hselect_href ( "select string" ), see _qparse for description
190             #
191             # returns an array containing [ key, href ] pairs
192              
193             sub _hselect_href {
194 0     0     my ( $self, $query ) = @_;
195 0           my ( $f, $w, $o ) = _qparse($query);
196              
197 0           my @fields = split(' ', $f);
198 0           my $data = $self->[0][0];
199              
200 0 0         if ( length $w ) {
201 0           my %match = map { $_ => 1 } ( $self->hkeys($w) );
  0            
202 0 0         if ( @fields ) {
203 0 0         map { !exists $match{$_} ? () : do {
  0 0          
204 0           my ( $k, %ret ) = ( $_ );
205 0           $ret{$_} = $data->{$k}{$_} for @fields;
206 0           [ $k, \%ret ];
207             };
208             } ( length $o ? $self->hsort($o) : $self->hkeys() );
209             }
210             else {
211 0 0         map { !exists $match{$_} ? () : [ $_, { %{ $data->{$_} } } ];
  0 0          
  0            
212             } ( length $o ? $self->hsort($o) : $self->hkeys() );
213             }
214             }
215             else {
216 0 0         if ( @fields ) {
217 0 0         map { my ( $k, %ret ) = ( $_ );
  0            
218 0           $ret{$_} = $data->{$k}{$_} for @fields;
219 0           [ $k, \%ret ];
220             } ( length $o ? $self->hsort($o) : $self->hkeys() );
221             }
222             else {
223 0 0         map { [ $_, { %{ $data->{$_} } } ];
  0            
  0            
224             } ( length $o ? $self->hsort($o) : $self->hkeys() );
225             }
226             }
227             }
228              
229             # _lselect_aref ( "select string" ), see _qparse for description
230             #
231             # returns an array containing [ key, aref ] pairs
232              
233             sub _lselect_aref {
234 0     0     my ( $self, $query ) = @_;
235 0           my ( $f, $w, $o ) = _qparse($query);
236              
237 0           my @fields = split(' ', $f);
238 0           my $data = $self->[1][0];
239              
240 0 0         if ( length $w ) {
241 0           my %match = map { $_ => 1 } ( $self->lkeys($w) );
  0            
242 0 0         if ( @fields ) {
243 0 0         map { !exists $match{$_} ? () : do {
  0 0          
244 0           my ( $k, @ret ) = ( $_ );
245 0           push @ret, $data->{$k}[$_] for @fields;
246 0           [ $k, \@ret ];
247             };
248             } ( length $o ? $self->lsort($o) : $self->lkeys() );
249             }
250             else {
251 0 0         map { !exists $match{$_} ? () : [ $_, [ @{ $data->{$_} } ] ];
  0 0          
  0            
252             } ( length $o ? $self->lsort($o) : $self->lkeys() );
253             }
254             }
255             else {
256 0 0         if ( @fields ) {
257 0 0         map { my ( $k, @ret ) = ( $_ );
  0            
258 0           push @ret, $data->{$k}[$_] for @fields;
259 0           [ $k, \@ret ];
260             } ( length $o ? $self->lsort($o) : $self->lkeys() );
261             }
262             else {
263 0 0         map { [ $_, [ @{ $data->{$_} } ] ];
  0            
  0            
264             } ( length $o ? $self->lsort($o) : $self->lkeys() );
265             }
266             }
267             }
268              
269             # _lselect_href ( "select string" ), see _qparse for description
270             #
271             # returns an array containing [ key, href ] pairs
272              
273             sub _lselect_href {
274 0     0     my ( $self, $query ) = @_;
275 0           my ( $f, $w, $o ) = _qparse($query);
276              
277 0           my @fields = split(' ', $f);
278 0           my $data = $self->[1][0];
279              
280 0 0         if ( length $w ) {
281 0           my %match = map { $_ => 1 } ( $self->lkeys($w) );
  0            
282 0 0         if ( @fields ) {
283 0 0         map { !exists $match{$_} ? () : do {
  0 0          
284 0           my ( $k, %ret ) = ( $_ );
285 0           $ret{$_} = $data->{$k}[$_] foreach @fields;
286 0           [ $k, \%ret ];
287             };
288             } ( length $o ? $self->lsort($o) : $self->lkeys() );
289             }
290             else {
291 0 0         map { !exists $match{$_} ? () : do {
  0 0          
292 0           my ( $k, %ret ) = ( $_ );
293 0           $ret{$_} = $data->{$k}[$_] for 0 .. @{ $data->{$k} } - 1;
  0            
294 0           [ $k, \%ret ];
295             };
296             } ( length $o ? $self->lsort($o) : $self->lkeys() );
297             }
298             }
299             else {
300 0 0         if ( @fields ) {
301 0 0         map { my ( $k, %ret ) = ( $_ );
  0            
302 0           $ret{$_} = $data->{$k}[$_] foreach @fields;
303 0           [ $k, \%ret ];
304             } ( length $o ? $self->lsort($o) : $self->lkeys() );
305             }
306             else {
307 0 0         map { my ( $k, %ret ) = ( $_ );
  0            
308 0           $ret{$_} = $data->{$k}[$_] for 0 .. @{ $data->{$k} } - 1;
  0            
309 0           [ $k, \%ret ];
310             } ( length $o ? $self->lsort($o) : $self->lkeys() );
311             }
312             }
313             }
314              
315             # _sort ( HoH, 0, "BY key [ ASC | DESC ] [ ALPHA ]" )
316             # _sort ( HoH, 0, "BY field [ ASC | DESC ] [ ALPHA ]" ) e.g. BY address
317             # _sort ( HoA, 1, "BY key [ ASC | DESC ] [ ALPHA ]" )
318             # _sort ( HoA, 1, "BY index [ ASC | DESC ] [ ALPHA ]" ) e.g. BY 9
319              
320             sub _sort {
321 0     0     my ( $o, $is_list, $request ) = @_;
322              
323 0 0         return () unless ( length $request );
324 0           $request =~ s/^[ ]*\bby\b[ ]*//i;
325              
326 0 0         if ( $request =~ /^[ ]*(\S+)[ ]*(.*)/ ) {
327 0           my ( $f, $modifiers, $alpha, $desc ) = ( $1, $2, 0, 0 );
328              
329 0 0         $alpha = 1 if ( $modifiers =~ /\balpha\b/i );
330 0 0         $desc = 1 if ( $modifiers =~ /\bdesc\b/i );
331              
332             # Return sorted keys, leaving the data intact.
333              
334 0 0         if ( defined wantarray ) {
    0          
335 0 0         if ( $f eq 'key' ) { # by key
336 0 0         if ( $alpha ) { ( $desc )
337 0           ? sort { $b cmp $a } $o->keys
338 0 0         : sort { $a cmp $b } $o->keys;
  0            
339             }
340             else { ( $desc )
341 0           ? sort { $b <=> $a } $o->keys
342 0 0         : sort { $a <=> $b } $o->keys;
  0            
343             }
344             }
345             else { # by field
346 0           my $d = $o->[0];
347 0 0         if ( $is_list ) {
348 0 0         if ( $alpha ) { ( $desc )
349 0           ? sort { $d->{$b}[$f] cmp $d->{$a}[$f] } $o->keys
350 0 0         : sort { $d->{$a}[$f] cmp $d->{$b}[$f] } $o->keys;
  0            
351             }
352             else { ( $desc )
353 0           ? sort { $d->{$b}[$f] <=> $d->{$a}[$f] } $o->keys
354 0 0         : sort { $d->{$a}[$f] <=> $d->{$b}[$f] } $o->keys;
  0            
355             }
356             }
357             else {
358 0 0         if ( $alpha ) { ( $desc )
359 0           ? sort { $d->{$b}{$f} cmp $d->{$a}{$f} } $o->keys
360 0 0         : sort { $d->{$a}{$f} cmp $d->{$b}{$f} } $o->keys;
  0            
361             }
362             else { ( $desc )
363 0           ? sort { $d->{$b}{$f} <=> $d->{$a}{$f} } $o->keys
364 0 0         : sort { $d->{$a}{$f} <=> $d->{$b}{$f} } $o->keys;
  0            
365             }
366             }
367             }
368             }
369              
370             # Sort in-place otherwise, in void context.
371              
372             elsif ( $f eq 'key' ) { # by key
373 0 0         if ( $alpha ) { ( $desc )
374 0           ? $o->_reorder( sort { $b cmp $a } $o->keys )
375 0 0         : $o->_reorder( sort { $a cmp $b } $o->keys );
  0            
376             }
377             else { ( $desc )
378 0           ? $o->_reorder( sort { $b <=> $a } $o->keys )
379 0 0         : $o->_reorder( sort { $a <=> $b } $o->keys );
  0            
380             }
381             }
382             else { # by field
383 0           my $d = $o->[0];
384 0 0         if ( $is_list ) {
385 0 0         if ( $alpha ) { ( $desc )
386 0           ? $o->_reorder( sort { $d->{$b}[$f] cmp $d->{$a}[$f] } $o->keys )
387 0 0         : $o->_reorder( sort { $d->{$a}[$f] cmp $d->{$b}[$f] } $o->keys );
  0            
388             }
389             else { ( $desc )
390 0           ? $o->_reorder( sort { $d->{$b}[$f] <=> $d->{$a}[$f] } $o->keys )
391 0 0         : $o->_reorder( sort { $d->{$a}[$f] <=> $d->{$b}[$f] } $o->keys );
  0            
392             }
393             }
394             else {
395 0 0         if ( $alpha ) { ( $desc )
396 0           ? $o->_reorder( sort { $d->{$b}{$f} cmp $d->{$a}{$f} } $o->keys )
397 0 0         : $o->_reorder( sort { $d->{$a}{$f} cmp $d->{$b}{$f} } $o->keys );
  0            
398             }
399             else { ( $desc )
400 0           ? $o->_reorder( sort { $d->{$b}{$f} <=> $d->{$a}{$f} } $o->keys )
401 0 0         : $o->_reorder( sort { $d->{$a}{$f} <=> $d->{$b}{$f} } $o->keys );
  0            
402             }
403             }
404             }
405             }
406             else {
407 0           ();
408             }
409             }
410              
411             ###############################################################################
412             ## ----------------------------------------------------------------------------
413             ## Common methods.
414             ##
415             ###############################################################################
416              
417             # dump ( "file.dat" )
418              
419             sub dump {
420 0     0 1   my ( $self, $file ) = @_;
421              
422 0 0         if ( length $file ) {
423 0 0         require Storable unless $INC{'Storable.pm'};
424              
425             # purge tombstones
426 0           $self->[0]->purge(), $self->[1]->purge();
427              
428 0           local $@; local $SIG{__DIE__};
  0            
429 0           eval { Storable::nstore($self, $file) };
  0            
430              
431 0 0         warn($@), return if $@;
432             }
433             else {
434 0           warn('Usage: $obj->dump("file.dat")');
435 0           return;
436             }
437              
438 0           1;
439             }
440              
441             # restore ( "file.dat" )
442              
443             sub restore {
444 0     0 1   my ( $self, $file ) = @_;
445              
446 0 0         if ( length $file ) {
447 0 0         require Storable unless $INC{'Storable.pm'};
448              
449 0           local $@; local $SIG{__DIE__};
  0            
450 0           my $obj = eval { Storable::retrieve($file) };
  0            
451 0 0         warn($@), return if $@;
452              
453 0 0         if ( ref($obj) ne 'MCE::Shared::Minidb' ) {
454 0           warn("$file isn't serialized Minidb data: ".ref($obj));
455 0           return;
456             }
457 0           $self->[1]->clear(), $self->[1] = delete $obj->[1];
458 0           $self->[0]->clear(), $self->[0] = delete $obj->[0];
459             }
460             else {
461 0           warn('Usage: $obj->restore("file.dat")');
462 0           return;
463             }
464              
465 0           1;
466             }
467              
468             # select_aref ( ":lists", "select string" )
469             # select_aref ( ":hashes", "select string" )
470             # select_aref ( "select string" ) same as ":hashes"
471              
472             sub select_aref {
473 0     0 1   my ( $self, @query ) = @_;
474              
475 0 0         if ( $query[0] =~ /^:lists$/i ) {
476 0           shift @query;
477 0           $self->_lselect_aref($query[0]);
478             }
479             else {
480 0 0         shift @query if ( $query[0] =~ /^:hashes$/i );
481 0           $self->_hselect_aref($query[0]);
482             }
483             }
484              
485             # select_href ( ":lists", "select string" )
486             # select_href ( ":hashes", "select string" )
487             # select_href ( "select string" ) same as ":hashes"
488              
489             sub select_href {
490 0     0 1   my ( $self, @query ) = @_;
491              
492 0 0         if ( $query[0] =~ /^:lists$/i ) {
493 0           shift @query;
494 0           $self->_lselect_href($query[0]);
495             }
496             else {
497 0 0         shift @query if ( $query[0] =~ /^:hashes$/i );
498 0           $self->_hselect_href($query[0]);
499             }
500             }
501              
502             ###############################################################################
503             ## ----------------------------------------------------------------------------
504             ## Hash of Hashes (HoH).
505             ##
506             ###############################################################################
507              
508             # hset ( key, field, value [, field, value, ... ] )
509              
510             sub hset {
511 0     0 1   my ( $self, $key ) = ( shift, shift );
512 0 0         return unless length($key);
513 0 0         if ( @_ ) {
514 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
515 0 0         if ( @_ == 2 ) {
516 0           $self->[0][0]{ $key }{ $_[0] } = $_[1];
517             } else {
518 0           $self->[0][0]{ $key }->mset(@_);
519             }
520             }
521             else {
522 0           return;
523             }
524             }
525              
526             # hget ( key, field [, field, ... ] )
527             # hget ( key )
528              
529             sub hget {
530 0     0 1   my ( $self, $key ) = ( shift, shift );
531 0 0         return unless length($key);
532 0 0         if ( @_ ) {
533 0 0         return unless exists($self->[0][0]{ $key });
534 0 0         if ( @_ == 1 ) {
535 0           $self->[0][0]{ $key }{ $_[0] };
536             } else {
537 0           $self->[0][0]{ $key }->mget(@_);
538             }
539             }
540             else {
541 0           $self->[0][0]{ $key };
542             }
543             }
544              
545             # hdel ( key, field [, field, ... ] )
546             # hdel ( key )
547              
548             sub hdel {
549 0     0 1   my ( $self, $key ) = ( shift, shift );
550 0 0         return unless length($key);
551 0 0         if ( @_ ) {
552 0 0         return unless exists($self->[0][0]{ $key });
553 0 0         if ( @_ == 1 ) {
554 0           delete $self->[0][0]{ $key }{ $_[0] };
555             } else {
556 0           $self->[0][0]{ $key }->mdel(@_);
557             }
558             }
559             else {
560 0           $self->[0]->del($key);
561             }
562             }
563              
564             # hexists ( key, field [, field, ... ] )
565             # hexists ( key )
566              
567             sub hexists {
568 0     0 1   my ( $self, $key ) = ( shift, shift );
569 0 0         return '' unless length($key);
570 0 0         if ( @_ ) {
571 0 0         return '' unless exists($self->[0][0]{ $key });
572 0 0         if ( @_ == 1 ) {
573 0           exists $self->[0][0]{ $key }{ $_[0] };
574             } else {
575 0           $self->[0][0]{ $key }->mexists(@_);
576             }
577             }
578             else {
579 0           exists $self->[0][0]{ $key };
580             }
581             }
582              
583             # hclear ( key )
584             # hclear ( )
585              
586             sub hclear {
587 0     0 1   my ( $self, $key ) = @_;
588 0 0         if ( @_ > 1 ) {
589 0 0         return unless exists($self->[0][0]{ $key });
590 0           $self->[0][0]{ $key }->clear();
591             }
592             else {
593 0           $self->[0]->clear();
594             }
595             }
596              
597             # hkeys ( key, [ field [, field, ... ] ] )
598             # hkeys ( key, "query string" )
599             # hkeys ( "query string" )
600             # hkeys ( )
601              
602             sub hkeys {
603 0     0 1   my $self = shift;
604              
605 0 0 0       if ( @_ == 1 && $_[0] =~ /^(?:key|\S+)[ ]+\S\S?[ ]+\S/ ) {
    0          
606 0           $self->_hfind({ getkeys => 1 }, @_);
607             }
608             elsif ( @_ ) {
609 0           my $key = shift;
610 0 0         return () unless exists($self->[0][0]{ $key });
611 0           $self->[0][0]{ $key }->keys(@_);
612             }
613             else {
614 0           $self->[0]->keys();
615             }
616             }
617              
618             # hpairs ( key, [ field [, field, ... ] ] )
619             # hpairs ( key, "query string" )
620             # hpairs ( "query string" )
621             # hpairs ( )
622              
623             sub hpairs {
624 0     0 1   my $self = shift;
625              
626 0 0 0       if ( @_ == 1 && $_[0] =~ /^(?:key|\S+)[ ]+\S\S?[ ]+\S/ ) {
    0          
627 0           $self->_hfind({}, @_);
628             }
629             elsif ( @_ ) {
630 0           my $key = shift;
631 0 0         return () unless exists($self->[0][0]{ $key });
632 0           $self->[0][0]{ $key }->pairs(@_);
633             }
634             else {
635 0           $self->[0]->pairs();
636             }
637             }
638              
639             # hvals ( key, [ field [, field, ... ] ] )
640             # hvals ( key, "query string" )
641             # hvals ( "query string" )
642             # hvals ( )
643              
644             sub hvals {
645 0     0 1   my $self = shift;
646              
647 0 0 0       if ( @_ == 1 && $_[0] =~ /^(?:key|\S+)[ ]+\S\S?[ ]+\S/ ) {
    0          
648 0           $self->_hfind({ getvals => 1 }, @_);
649             }
650             elsif ( @_ ) {
651 0           my $key = shift;
652 0 0         return () unless exists($self->[0][0]{ $key });
653 0           $self->[0][0]{ $key }->vals(@_);
654             }
655             else {
656 0           $self->[0]->vals();
657             }
658             }
659              
660             # hshift ( )
661              
662             sub hshift {
663 0     0 1   $_[0]->[0]->shift();
664             }
665              
666             # hsort ( "BY key [ ASC | DESC ] [ ALPHA ]" )
667             # hsort ( "BY field [ ASC | DESC ] [ ALPHA ]" )
668              
669             sub hsort {
670 0     0 1   my ( $self, $request ) = @_;
671 0 0         return () unless ( @_ == 2 );
672 0           _sort($self->[0], 0, $request);
673             }
674              
675             # happend ( key, field, string )
676              
677             sub happend {
678 0     0 1   my ( $self, $key ) = @_;
679 0 0         return unless length($key);
680 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
681 0   0       length( $self->[0][0]{ $key }{ $_[2] } .= $_[3] // '' );
682             }
683              
684             # hassign ( key, field, value [, field, value, ... ] )
685              
686             sub hassign {
687 0     0 1   my ( $self, $key ) = ( shift, shift );
688 0 0         return unless length($key);
689 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
690 0           $self->[0][0]{ $key }->assign(@_);
691             }
692              
693             # hdecr ( key, field )
694              
695             sub hdecr {
696 0     0 1   my ( $self, $key ) = @_;
697 0 0         return unless length($key);
698 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
699 0           --$self->[0][0]{ $key }{ $_[2] };
700             }
701              
702             # hdecrby ( key, field, number )
703              
704             sub hdecrby {
705 0     0 1   my ( $self, $key ) = @_;
706 0 0         return unless length($key);
707 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
708 0   0       $self->[0][0]{ $key }{ $_[2] } -= $_[3] || 0;
709             }
710              
711             # hincr ( key, field )
712              
713             sub hincr {
714 0     0 1   my ( $self, $key ) = @_;
715 0 0         return unless length($key);
716 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
717 0           ++$self->[0][0]{ $key }{ $_[2] };
718             }
719              
720             # hincrby ( key, field, number )
721              
722             sub hincrby {
723 0     0 1   my ( $self, $key ) = @_;
724 0 0         return unless length($key);
725 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
726 0   0       $self->[0][0]{ $key }{ $_[2] } += $_[3] || 0;
727             }
728              
729             # hgetdecr ( key, field )
730              
731             sub hgetdecr {
732 0     0 1   my ( $self, $key ) = @_;
733 0 0         return unless length($key);
734 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
735 0   0       $self->[0][0]{ $key }{ $_[2] }-- // 0;
736             }
737              
738             # hgetincr ( key, field )
739              
740             sub hgetincr {
741 0     0 1   my ( $self, $key ) = @_;
742 0 0         return unless length($key);
743 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
744 0   0       $self->[0][0]{ $key }{ $_[2] }++ // 0;
745             }
746              
747             # hgetset ( key, field, value )
748              
749             sub hgetset {
750 0     0 1   my ( $self, $key ) = ( shift, shift );
751 0 0         return unless length($key);
752 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
753 0           $self->[0][0]{ $key }->getset(@_);
754             }
755              
756             # hsetnx ( key, field, value )
757              
758             sub hsetnx {
759 0     0 1   my ( $self, $key ) = ( shift, shift );
760 0 0         return unless length($key);
761 0 0         $self->[0]->set($key, _new_hash()) unless exists($self->[0][0]{ $key });
762 0           $self->[0][0]{ $key }->setnx(@_);
763             }
764              
765             # hlen ( key, field )
766             # hlen ( key )
767             # hlen ( )
768              
769             sub hlen {
770 0     0 1   my $self = shift;
771 0 0         if ( @_ ) {
772 0           my $key = shift;
773 0 0         return 0 unless exists($self->[0][0]{ $key });
774 0           $self->[0][0]{ $key }->len(@_);
775             }
776             else {
777 0           $self->[0]->len();
778             }
779             }
780              
781             ###############################################################################
782             ## ----------------------------------------------------------------------------
783             ## Hash of Lists (HoA).
784             ##
785             ###############################################################################
786              
787             # lset ( key, index, value [, index, value, ... ] )
788              
789             sub lset {
790 0     0 1   my ( $self, $key ) = ( shift, shift );
791 0 0         return unless length($key);
792 0 0         if ( @_ ) {
793 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
794 0 0         if ( @_ == 2 ) {
795 0           $self->[1][0]{ $key }[ $_[0] ] = $_[1];
796             } else {
797 0           $self->[1][0]{ $key }->mset(@_);
798             }
799             }
800             else {
801 0           return;
802             }
803             }
804              
805             # lget ( key, index [, index, ... ] )
806             # lget ( key )
807              
808             sub lget {
809 0     0 1   my ( $self, $key ) = ( shift, shift );
810 0 0         return unless length($key);
811 0 0         if ( @_ ) {
812 0 0         return unless exists($self->[1][0]{ $key });
813 0 0         if ( @_ == 1 ) {
814 0           $self->[1][0]{ $key }[ $_[0] ];
815             } else {
816 0           $self->[1][0]{ $key }->mget(@_);
817             }
818             }
819             else {
820 0           $self->[1][0]{ $key };
821             }
822             }
823              
824             # ldel ( key, index [, index, ... ] )
825             # ldel ( key )
826              
827             sub ldel {
828 0     0 1   my ( $self, $key ) = ( shift, shift );
829 0 0         return unless length($key);
830 0 0         if ( @_ ) {
831 0 0         return unless exists($self->[1][0]{ $key });
832 0 0         if ( @_ == 1 ) {
833 0           delete $self->[1][0]{ $key }[ $_[0] ];
834             } else {
835 0           $self->[1][0]{ $key }->mdel(@_);
836             }
837             }
838             else {
839 0           $self->[1]->del($key);
840             }
841             }
842              
843             # lexists ( key, index [, index, ... ] )
844             # lexists ( key )
845              
846             sub lexists {
847 0     0 1   my ( $self, $key ) = ( shift, shift );
848 0 0         return '' unless length($key);
849 0 0         if ( @_ ) {
850 0 0         return '' unless exists($self->[1][0]{ $key });
851 0 0         if ( @_ == 1 ) {
852 0           exists $self->[1][0]{ $key }[ $_[0] ];
853             } else {
854 0           $self->[1][0]{ $key }->mexists(@_);
855             }
856             }
857             else {
858 0           exists $self->[1][0]{ $key };
859             }
860             }
861              
862             # lclear ( key )
863             # lclear ( )
864              
865             sub lclear {
866 0     0 1   my ( $self, $key ) = @_;
867 0 0         if ( @_ > 1 ) {
868 0 0         return unless exists($self->[1][0]{ $key });
869 0           $self->[1][0]{ $key }->clear();
870             }
871             else {
872 0           $self->[1]->clear();
873             }
874             }
875              
876             # lrange ( key, start, stop )
877              
878             sub lrange {
879 0     0 1   my ( $self, $key ) = ( shift, shift );
880 0 0 0       return () unless length($key) && exists($self->[1][0]{ $key });
881 0           $self->[1][0]{ $key }->range(@_);
882             }
883              
884             # lsplice ( key, offset [, length [, list ] ] )
885              
886             sub lsplice {
887 0     0 1   my ( $self, $key ) = ( shift, shift );
888 0 0 0       return unless length($key) && scalar(@_);
889 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
890 0           $self->[1][0]{ $key }->splice(@_);
891             }
892              
893             # lpop ( key )
894              
895             sub lpop {
896 0     0 1   my ( $self, $key ) = ( shift, shift );
897 0 0 0       return unless length($key) && exists($self->[1][0]{ $key });
898 0           shift @{ $self->[1][0]{ $key } };
  0            
899             }
900              
901             # lpush ( key, value [, value, ... ] )
902              
903             sub lpush {
904 0     0 1   my ( $self, $key ) = ( shift, shift );
905 0 0 0       return unless length($key) && scalar(@_);
906 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
907 0           unshift @{ $self->[1][0]{ $key } }, @_;
  0            
908             }
909              
910             # rpop ( key )
911              
912             sub rpop {
913 0     0 1   my ( $self, $key ) = ( shift, shift );
914 0 0 0       return unless length($key) && exists($self->[1][0]{ $key });
915 0           pop @{ $self->[1][0]{ $key } };
  0            
916             }
917              
918             # rpush ( key, value [, value, ... ] )
919              
920             sub rpush {
921 0     0 1   my ( $self, $key ) = ( shift, shift );
922 0 0 0       return unless length($key) && scalar(@_);
923 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
924 0           push @{ $self->[1][0]{ $key } }, @_;
  0            
925             }
926              
927             # lkeys ( key, [ index [, index, ... ] ] )
928             # lkeys ( key, "query string" )
929             # lkeys ( "query string" )
930             # lkeys ( )
931              
932             sub lkeys {
933 0     0 1   my $self = shift;
934              
935 0 0 0       if ( @_ == 1 && $_[0] =~ /^(?:key|\S+)[ ]+\S\S?[ ]+\S/ ) {
    0          
936 0           $self->_lfind({ getkeys => 1 }, @_);
937             }
938             elsif ( @_ ) {
939 0           my $key = shift;
940 0 0         return () unless exists($self->[1][0]{ $key });
941 0           $self->[1][0]{ $key }->keys(@_);
942             }
943             else {
944 0           $self->[1]->keys();
945             }
946             }
947              
948             # lpairs ( key, [ index [, index, ... ] ] )
949             # lpairs ( key, "query string" )
950             # lpairs ( "query string" )
951             # lpairs ( )
952              
953             sub lpairs {
954 0     0 1   my $self = shift;
955              
956 0 0 0       if ( @_ == 1 && $_[0] =~ /^(?:key|\S+)[ ]+\S\S?[ ]+\S/ ) {
    0          
957 0           $self->_lfind({}, @_);
958             }
959             elsif ( @_ ) {
960 0           my $key = shift;
961 0 0         return () unless exists($self->[1][0]{ $key });
962 0           $self->[1][0]{ $key }->pairs(@_);
963             }
964             else {
965 0           $self->[1]->pairs();
966             }
967             }
968              
969             # lvals ( key, [ index [, index, ... ] ] )
970             # lvals ( key, "query string" )
971             # lvals ( "query string" )
972             # lvals ( )
973              
974             sub lvals {
975 0     0 1   my $self = shift;
976              
977 0 0 0       if ( @_ == 1 && $_[0] =~ /^(?:key|\S+)[ ]+\S\S?[ ]+\S/ ) {
    0          
978 0           $self->_lfind({ getvals => 1 }, @_);
979             }
980             elsif ( @_ ) {
981 0           my $key = shift;
982 0 0         return () unless exists($self->[1][0]{ $key });
983 0           $self->[1][0]{ $key }->vals(@_);
984             }
985             else {
986 0           $self->[1]->vals();
987             }
988             }
989              
990             # lshift ( )
991              
992             sub lshift {
993 0     0 1   $_[0]->[1]->shift();
994             }
995              
996             # lsort ( "BY key [ ASC | DESC ] [ ALPHA ]" )
997             # lsort ( "BY index [ ASC | DESC ] [ ALPHA ]" )
998             #
999             # lsort ( key, "BY key [ ASC | DESC ] [ ALPHA ]" )
1000             # lsort ( key, "BY val [ ASC | DESC ] [ ALPHA ]" )
1001              
1002             sub lsort {
1003 0     0 1   my ( $self, $arg1, $arg2 ) = @_;
1004 0 0         if ( @_ == 2 ) {
1005 0           _sort($self->[1], 1, $arg1);
1006             }
1007             else {
1008 0 0 0       return () unless ( @_ == 3 && exists($self->[1][0]{ $arg1 }) );
1009 0           $self->[1][0]{ $arg1 }->sort($arg2);
1010             }
1011             }
1012              
1013             # lappend ( key, index, string )
1014              
1015             sub lappend {
1016 0     0 1   my ( $self, $key ) = @_;
1017 0 0         return unless length($key);
1018 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1019 0   0       length( $self->[1][0]{ $key }[ $_[2] ] .= $_[3] // '' );
1020             }
1021              
1022             # lassign ( key, value [, value, ... ] )
1023              
1024             sub lassign {
1025 0     0 1   my ( $self, $key ) = ( shift, shift );
1026 0 0         return unless length($key);
1027 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1028 0           $self->[1][0]{ $key }->assign(@_);
1029             }
1030              
1031             # ldecr ( key, index )
1032              
1033             sub ldecr {
1034 0     0 1   my ( $self, $key ) = @_;
1035 0 0         return unless length($key);
1036 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1037 0           --$self->[1][0]{ $key }[ $_[2] ];
1038             }
1039              
1040             # ldecrby ( key, index, number )
1041              
1042             sub ldecrby {
1043 0     0 1   my ( $self, $key ) = @_;
1044 0 0         return unless length($key);
1045 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1046 0   0       $self->[1][0]{ $key }[ $_[2] ] -= $_[3] || 0;
1047             }
1048              
1049             # lincr ( key, index )
1050              
1051             sub lincr {
1052 0     0 1   my ( $self, $key ) = @_;
1053 0 0         return unless length($key);
1054 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1055 0           ++$self->[1][0]{ $key }[ $_[2] ];
1056             }
1057              
1058             # lincrby ( key, index, number )
1059              
1060             sub lincrby {
1061 0     0 1   my ( $self, $key ) = @_;
1062 0 0         return unless length($key);
1063 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1064 0   0       $self->[1][0]{ $key }[ $_[2] ] += $_[3] || 0;
1065             }
1066              
1067             # lgetdecr ( key, index )
1068              
1069             sub lgetdecr {
1070 0     0 1   my ( $self, $key ) = @_;
1071 0 0         return unless length($key);
1072 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1073 0   0       $self->[1][0]{ $key }[ $_[2] ]-- // 0;
1074             }
1075              
1076             # lgetincr ( key, index )
1077              
1078             sub lgetincr {
1079 0     0 1   my ( $self, $key ) = @_;
1080 0 0         return unless length($key);
1081 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1082 0   0       $self->[1][0]{ $key }[ $_[2] ]++ // 0;
1083             }
1084              
1085             # lgetset ( key, index, value )
1086              
1087             sub lgetset {
1088 0     0 1   my ( $self, $key ) = ( shift, shift );
1089 0 0         return unless length($key);
1090 0 0         $self->[1]->set($key, _new_list()) unless exists($self->[1][0]{ $key });
1091 0           $self->[1][0]{ $key }->getset(@_);
1092             }
1093              
1094             # llen ( key, index )
1095             # llen ( key )
1096             # llen ( )
1097              
1098             sub llen {
1099 0     0 1   my $self = shift;
1100 0 0         if ( @_ ) {
1101 0           my $key = shift;
1102 0 0         return 0 unless exists($self->[1][0]{ $key });
1103 0           $self->[1][0]{ $key }->len(@_);
1104             }
1105             else {
1106 0           $self->[1]->len();
1107             }
1108             }
1109              
1110             1;
1111              
1112             __END__