File Coverage

blib/lib/NoSQL/PL2SQL/Object.pm
Criterion Covered Total %
statement 27 507 5.3
branch 0 282 0.0
condition 0 107 0.0
subroutine 9 62 14.5
pod 2 27 7.4
total 38 985 3.8


line stmt bran cond sub pod time code
1             package NoSQL::PL2SQL::Clone ;
2             our @ISA = qw( NoSQL::PL2SQL::Object ) ;
3 2     2   17187 use strict;
  2         5  
  2         492  
4 2     2   14 use warnings;
  2         6  
  2         2150  
5              
6             sub update {
7 0     0     my $self = shift ;
8 0           my $data = $self->{globals}->{memory}->{ $self->objectkey }->[0] ;
9              
10 0 0         if ( $self->{reftype} eq 'hashref' ) {
    0          
    0          
11 0   0       my %out = map { $_ => NoSQL::PL2SQL::Object::item(
  0            
12             $data->{$_} )->[1] || $data->{$_} }
13             keys %$data ;
14 0           $self->{data} = \%out ;
15             }
16             elsif ( $self->{reftype} eq 'arrayref' ) {
17 0 0         my @out = map { NoSQL::PL2SQL::Object::item( $_ ) || $_ }
  0            
18             @$data ;
19 0           $self->{data} = \@out ;
20             }
21             elsif ( $self->{reftype} eq 'scalarref' ) {
22 0           $self->{data} = \$data ;
23             }
24             else {
25 0           $self->{data} = $data ;
26             }
27             }
28              
29             sub sqlclone {
30 0     0     my $self = shift ;
31 0           my $data = $self->{data} ;
32 0           my $out ;
33              
34 0 0         if ( $self->{reftype} eq 'hashref' ) {
    0          
    0          
35 0           my %o = map { $_ => innerclone( $data->{ $_ } ) }
  0            
36             keys %$data ;
37 0           $out = $self->mybless( \%o ) ;
38             }
39             elsif ( $self->{reftype} eq 'arrayref' ) {
40 0           my @o = map { innerclone( $_ ) } @$data ;
  0            
41 0           $out = $self->mybless( \@o ) ;
42             }
43             elsif ( $self->{reftype} eq 'scalarref' ) {
44 0           my $o = $$data ;
45 0           $out = $self->mybless( \$o ) ;
46             }
47             else {
48 0           $out = $data ;
49             }
50              
51 0           for my $v ( values %{ $self->{globals}->{memory} } ) {
  0            
52 0           my $item = NoSQL::PL2SQL::Object::item( $v->[0] ) ;
53 0 0 0       delete $item->[1]->{clone} if $item && $item->[1] ;
54             }
55              
56 0           return $out ;
57             }
58              
59             sub innerclone {
60 0     0     return NoSQL::PL2SQL::Object::innerclone( @_ ) ;
61             }
62              
63             sub DESTROY {
64 0     0     my $self = shift ;
65              
66 0 0 0       $self->{globals}->{lock} ||= new NoSQL::PL2SQL::Lock $self
      0        
67             if $self->{data} && ! $self->{globals}->{rollback} ;
68             }
69              
70              
71             package NoSQL::PL2SQL::Lock ;
72 2     2   22 use strict;
  2         4  
  2         70  
73 2     2   11 use warnings;
  2         4  
  2         1654  
74              
75             my @errors ;
76              
77             sub new {
78 0     0     my $package = shift ;
79 0           my $o = shift ;
80 0           my $dsn = $o->{sqltable} ;
81 0           my $header = $o->{globals}->{header} ;
82 0           my $out = [ $dsn, $header ] ;
83              
84 0           my $lock = $dsn->new->update( undef => [ deleted => 1 ] )->{nvp} ;
85 0           my $incr = $dsn->new->update( undef =>
86             [ intdata => $header->{intdata} +1 ] )->{nvp} ;
87              
88 0           for ( my $ct = 0 ; ! setlock( $dsn, $lock, $header->{id} ) ; $ct++ ) {
89 0           select undef, undef, undef, .200 ; ## wait 5 seconds
90 0 0         next if $ct < 50 ;
91              
92             ## deadlock failure
93 0 0         @errors = NoSQL::PL2SQL->sqlerror unless @errors ;
94 0           NoSQL::PL2SQL::sqlcarp( $header->{objecttype}, $errors[7],
95             { timestamp => time,
96             recordid => $header->{id} },
97             $o->sqlclone,
98             sprintf "%s: %d", $errors[7], $o->{top} ) ;
99              
100 0           $o->{globals}->{rollback} = 1 ;
101 0           return $out ;
102             }
103              
104 0           my $updates = $dsn->sqlupdate( $incr,
105             [ id => $header->{id} ],
106             [ intdata => $header->{intdata} ] ) ;
107              
108 0 0         unless ( $updates *1 ) {
109 0           $o->{globals}->{rollback} = 1 ;
110 0           push @$out, $o->mybless( $o->sqlclone ) ;
111 0   0       my $serial = $dsn->fetch( [ id => $header->{id} ]
112             )->{ $header->{id} }->{intdata} || 0 ;
113 0           $incr = $dsn->new->update(
114             undef => [ intdata => ++$serial ] )->{nvp} ;
115 0           $dsn->sqlupdate( $incr, [ id => $header->{id} ] ) ;
116             }
117              
118 0           return bless $out, $package ;
119             }
120              
121             sub setlock {
122 0     0     my $dsn = shift ;
123 0           my $lock = shift ;
124 0           my $id = shift ;
125 0           my $r = $dsn->sqlupdate( $lock,
126             [ id => $id ],
127             $dsn->exclude( [ deleted => 1 ] ) ) ;
128 0           return $r *1 ;
129             }
130              
131             sub DESTROY {
132 0     0     my $self = shift ;
133 0           my $dsn = $self->[0] ;
134 0           my $header = $self->[1] ;
135              
136 0 0         if ( @$self == 3 ) {
137             ## delete all records except header
138 0           $dsn->delete( [ objectid => $header->{objectid} ],
139             [ objecttype => $header->{objecttype}, 1 ],
140             $dsn->exclude( [ id => $header->{id} ] )
141             ) ;
142              
143             ## insert clone
144 0           my @nodes = NoSQL::PL2SQL::Node->factory(
145             0, $header->{objectid}, $self->[2] ) ;
146 0           pop @nodes ;
147 0           my ( $ll, $refs ) = NoSQL::PL2SQL::Node->insertall(
148             $dsn, NoSQL::PL2SQL::Node->combine( @nodes )
149             ) ;
150              
151             ## swap out new header
152 0           my $refto = $dsn->new->update( undef, [ refto => $ll ]
153             )->{nvp} ;
154 0           $dsn->sqlupdate( $refto, [ id => $header->{id} ] ) ;
155             }
156              
157 0           my $unlock = $dsn->new->update( undef => [ deleted => 0 ] )->{nvp} ;
158 0           $dsn->sqlupdate( $unlock, [ id => $header->{id} ] ) ;
159             }
160              
161              
162             package NoSQL::PL2SQL::Object ;
163              
164 2     2   72 use 5.008009;
  2         13  
  2         83  
165 2     2   11 use strict;
  2         6  
  2         84  
166 2     2   11 use warnings;
  2         6  
  2         57  
167 2     2   17 use Scalar::Util ;
  2         4  
  2         13673  
168              
169             require Exporter;
170              
171             our @ISA = qw(Exporter);
172              
173             # Items to export into callers namespace by default. Note: do not export
174             # names by default without a very good reason. Use EXPORT_OK instead.
175             # Do not simply export all your public functions/methods/constants.
176              
177             # This allows declaration use NoSQL::PL2SQL::Object ':all';
178             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
179             # will save memory.
180             our %EXPORT_TAGS = ( 'all' => [ qw() ] ) ;
181              
182             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ) ;
183              
184             our @EXPORT = qw() ;
185              
186             our $VERSION = '0.08';
187              
188             # Preloaded methods go here.
189              
190             sub TIEHASH {
191 0     0     my $self = shift ;
192 0           my $out = bless { %$self }, $self->package ;
193 0 0         $out->{top} = shift @_ if @_ ;
194 0           $out->{reftype} = $out->record->{reftype} ;
195 0           return $out ;
196             }
197              
198             sub TIEARRAY {
199 0     0     my $self = shift ;
200 0           my $out = bless { %$self }, $self->package ;
201 0 0         $out->{top} = shift @_ if @_ ;
202 0           $out->{reftype} = $out->record->{reftype} ;
203 0           return $out ;
204             }
205              
206             sub TIESCALAR {
207 0     0     my $self = shift ;
208 0           my $out = bless { %$self }, $self->package ;
209 0 0         $out->{top} = shift @_ if @_ ;
210 0           $out->{data} = shift ;
211 0           $out->{reftype} = $out->record->{reftype} ;
212 0           return $out ;
213             }
214              
215             ## sub destroy {} ## for ctags
216             ## avoid running during global destruction
217             sub DESTROY {
218 0     0     my $self = shift ;
219 0           my @xml = () ;
220              
221 0           delete $self->{globals}->{clone} ;
222              
223 0 0         return if $self->{globals}->{rollback} ;
224 0 0         return unless $self->{update} ;
225 0 0 0       return if $self->{top} && ! $self->record ; ## deleted
226              
227 0 0         map { $self->{sqltable}->delete( $_ ) } @{ $self->{delete} || [] } ;
  0            
  0            
228              
229 0 0         if ( $self->{top} ) {
230 0           @xml = $self->updates ;
231              
232 0 0         if ( ! exists $xml[-1]{sql}{reftype} ) {}
    0          
233             elsif ( grep $_ eq $xml[-1]{sql}{reftype},
234             qw( scalar scalarref ) ) {
235 0           my @chain = $self->linklist('chainedstring') ;
236 0 0         map { $_->{sql}->{id} = shift @chain if @chain }
  0            
237             reverse @xml ;
238              
239 0           map { $self->{sqltable}->delete( $_ ) } @chain ;
  0            
240             }
241             }
242             else {
243 0           my $parent = $self->record( $self->{parent } ) ;
244 0           my $r = $parent->{reftype} ;
245 0 0         my $o = $r eq 'hashref'? { $self->{key} => $self->{data} }:
    0          
    0          
246             $r eq 'arrayref'? [ $self->{data} ]:
247             $r eq 'scalarref'? \$self->{data}:
248             return warn ;
249 0           @xml = NoSQL::PL2SQL::Node->factory( 0,
250             $parent->{objectid}, $o,
251             $parent->{objecttype} ) ;
252 0           pop @xml ; ## perldata
253 0 0         pop @xml unless grep $_ eq $self->{reftype},
254             qw( arrayref hashref ) ;
255              
256 0 0         $xml[-1]{sql}{intkey} = $self->{sequence}
257             if exists $self->{sequence} ;
258             }
259              
260 0           my @combined = NoSQL::PL2SQL::Node::combine( @xml ) ;
261              
262             ## references a new element
263 0   0       foreach my $memo ( grep $_->memory &&
264             $self->{globals}->{adds}->{ $_->memory },
265             @combined ) {
266 0           my $src = $self->{globals}->{adds}->{ $memo->memory } ;
267              
268 0           map { $memo->{sql}->{$_} = $src->{$_} }
  0            
269             qw( blesstype reftype refto ) ;
270 0 0         $memo->{sql}->{refto} = $src->{id}
271             if $memo->{sql}->{reftype} eq 'scalarref' ;
272             }
273              
274             ## references an existing element
275 0 0         if ( my $mrec = $self->refrecord( 1 ) ) {
276 0           splice @combined, 0, -1 ;
277              
278 0           my $src = $self->record( $mrec ) ;
279 0           map { $combined[-1]{sql}{$_} = $src->{$_} }
  0            
280             qw( blesstype reftype refto ) ;
281 0 0         $combined[-1]{sql}{refto} = $mrec
282             if $combined[-1]{sql}{reftype} eq 'scalarref' ;
283             }
284              
285 0           my ( $ll, $refs ) = NoSQL::PL2SQL::Node->insertall(
286             $self->{sqltable}, @combined ) ;
287 0           map { $self->{globals}->{adds}->{$_} = $refs->{$_} } keys %$refs ;
  0            
288              
289 0 0         if ( $self->{parent} ) {
290 0           my $last = NoSQL::PL2SQL::Perldata::lastitem(
291             $self->{perldata}, $self->{parent} ) ;
292 0           $self->{sqltable}->update( $last->[1],
293             [ $last->[0] => $ll ] ) ;
294 0           $self->record( $last->[1] )->{ $last->[0] } = $ll ;
295             }
296             }
297              
298             sub self {
299 0     0 0   my $self = shift ;
300 0           $self->data ;
301 0   0       return $self->refrecord || $self ;
302             }
303              
304             sub linklist {
305 0     0 0   my $self = shift ;
306 0           my $k = pop ;
307 0           my $r = $self->record( @_ ) ;
308              
309 0           my @o = $r->{id} ;
310 0 0 0       $o[0] *= -1 if $r->{deleted} && ! $k ;
311              
312 0   0       my $kk = $k || 'item' ;
313 0 0         push @o, $self->linklist( $r->{$kk}, $k ) if $r->{$kk} ;
314 0           return @o ;
315             }
316              
317             sub record {
318 0     0 0   my $self = shift ;
319 0 0         my $k = @_? shift( @_ ): $self->{top} ;
320              
321 0 0         return $self->{perldata}->{ $k } if $k ;
322              
323 0           my @v = values %{ $self->{perldata} } ;
  0            
324 0           return { %{ $v[0] } } ; ## clone is not writable
  0            
325             }
326              
327             sub topnode {
328 0     0 0   my $self = shift ;
329 0 0         my $top = @_? shift( @_ ): $self->{globals}->{top} ;
330              
331 0           my @ok = grep $_->[1] == $top,
332 0           values %{ $self->{globals}->{memory} } ;
333 0           return $ok[0][0] ;
334             }
335              
336             ## The copy creates a second reference to avoid inadvertant destruction
337             sub load {
338 0     0 0   my $self = shift ;
339 0           $self->{data} = shift ;
340 0 0         $self->{copy} = shift if @_ ;
341 0           $self->{ref} = $self->{data} ;
342              
343 0 0         return $self->{data} unless ref $self->{data} ;
344              
345 0           my @elements = $self->{reftype} eq 'hashref'?
346 0           values %{ $self->{data} }:
347 0           $self->{reftype} eq 'arrayref'? @{ $self->{data} }:
348 0 0         $self->{reftype} eq 'scalarref'? ( ${ $self->{data} } ):
    0          
    0          
349             warn "unknown reftype: " .$self->{reftype} ;
350              
351 0           foreach my $o ( grep ref $_, @elements ) {
352 0           my @oo = @{ item( $o ) } ;
  0            
353 0 0         next unless $oo[1]{top} ;
354              
355 0           my $k = $self->{globals}->{records}->{ $oo[1]{top} } ;
356 0 0         my $rec = $self->{globals}->{memory}->{$k} if $k ;
357 0 0         $self->{data} = $rec->[0] if $rec ;
358              
359 0           $self->{globals}->{records}->{ $oo[1]{top} }
360             = $self->objectkey ;
361 0           $self->{globals}->{refcount}->{ $oo[1]{top} }++ ;
362              
363 0 0 0       my $refto = $oo[1]{reftype} eq 'scalarref'?
364             $oo[1]->refto || $oo[1]->{top}: '' ;
365 0 0 0       $oo[1]->loadscalarref( $refto )
366             if $refto && ! exists
367             $oo[1]->{globals}->{scalarrefs}->{$refto} ;
368              
369 0 0         next if $oo[1] == $o ;
370 0           $oo[1]->memorymap( $o ) ;
371             }
372              
373 0           return $self->{data} ;
374             }
375              
376             ## refactoring? may duplicate Perldata::fetchextract()
377             sub loadscalarref {
378 0     0 0   my $self = shift ;
379 0           my $refto = shift ;
380              
381 0           my $rr = $self->newelement(
382             NoSQL::PL2SQL::Perldata::item(
383             $self->{perldata}, $refto )->[1]
384             ) ;
385 0           map { delete $rr->{$_} }
  0            
386             qw( parent last update globals ) ;
387 0           $rr->{reftype} = $self->{reftype} ;
388 0           $rr->{top} = $refto ;
389 0           $self->{globals}->{scalarrefs}->{ $refto } = $rr ;
390 0           $self->{globals}->{refcount}->{ $refto }++ ;
391             }
392              
393             sub memorymap {
394 0     0 0   my $self = shift ;
395 0           my $target = shift ;
396 0           my $k = $self->objectkey ;
397 0 0         my $top = @_? shift( @_ ): $self->{top} ;
398              
399 0           $self->{globals}->{memory}->{$k} = [ $target, $top ] ;
400 0           Scalar::Util::weaken( $self->{globals}->{memory}->{$k}->[0] ) ;
401              
402 0           return $target ;
403             }
404              
405             sub data {
406 0     0 1   my $self = shift ;
407 0           my @inner = () ;
408 0           my $blesstype = $self->record->{blesstype} ;
409 0           my $refto = $self->record->{refto} ;
410              
411 0           my @args = @_ ;
412              
413 0 0         if ( $self->{reftype} eq 'hashref' ) {
    0          
    0          
414 0 0         unless ( $self->{data} ) {
415 0 0         @inner = NoSQL::PL2SQL::Perldata::fetchextract(
416             $self, $refto, 'textkey' )
417             if $refto ;
418              
419 0           $self->load( { @inner }, { @inner } ) ;
420 0 0         bless $self->{data}, $blesstype if $blesstype ;
421             }
422              
423 0 0         if ( @args ) {
424 0           my $item = item( $self->{data}->{ $args[0] } ) ;
425 0 0         return @args == 1? $item: $item->[ $args[1] ] ;
426             }
427             }
428              
429             elsif ( $self->{reftype} eq 'arrayref' ) {
430 0 0         unless ( $self->{data} ) {
431 0 0         @inner = NoSQL::PL2SQL::Perldata::fetchextract(
432             $self, $refto, 'intkey' )
433             if $refto ;
434 0           my @sorter = () ;
435 0           push @sorter, [ splice @inner, 0, 2 ] while @inner ;
436 0           my @sorted = map { $_->[1] }
  0            
437 0           sort { $a->[0] <=> $b->[0] }
438             @sorter ;
439              
440 0           $self->load( \@sorted, [ @sorted ] ) ;
441 0 0         bless $self->{data}, $blesstype if $blesstype ;
442             }
443              
444 0 0         if ( @args ) {
445 0           my $item = item( $self->{data}->[ $args[0] ] ) ;
446 0 0         return @args == 1? $item: $item->[ $args[1] ] ;
447             }
448             }
449              
450             elsif ( $self->{reftype} eq 'scalarref' ) {
451 0   0       my $top = $self->record->{refto} || $self->{top} ;
452 0 0         return @args == 2? $self->scalarref->[0]:
    0          
453             @args == 1? $self->scalarref:
454             $self->topnode( $top ) ;
455             }
456              
457             else {
458 0           return $self->{reftype} ;
459             }
460              
461 0           return $self->{data} ;
462             }
463              
464             sub refto {
465 0     0 0   my $self = shift ;
466 0           return NoSQL::PL2SQL::Perldata::refto(
467             $self->{perldata}, $self->{top} ) ;
468             }
469              
470             sub sqlclone {
471 0     0 0   my $tied = shift ;
472 0           my $self = item( $tied )->[1] ;
473 0           my $out = innerclone( $tied, $self ) ;
474              
475 0           for my $v ( values %{ $self->{globals}->{memory} } ) {
  0            
476 0           my $item = item( $v->[0] ) ;
477 0 0 0       delete $item->[1]->{clone} if $item && $item->[1] ;
478             }
479              
480 0           delete $self->{clone} ;
481 0           return $out ;
482             }
483              
484             sub innerclone {
485 0     0 0   my $tied = shift ;
486              
487 0 0         my $self = @_? $_[0]: item( $tied )->[1] ;
488 0 0         return $tied unless defined $self ;
489              
490 0           my $data = $self->data ;
491 0   0       my $reference = NoSQL::PL2SQL::Object::item( $data )->[1] || 0 ;
492 0 0 0       return $reference->{clone} if $reference && $reference->{clone} ;
493              
494 0 0         if ( $self->{reftype} eq 'hashref' ) {
    0          
    0          
495 0           my %o = map { $_ => innerclone( $self->data->{ $_ } ) }
  0            
496             keys %$data ;
497 0           return $self->{clone} = $self->mybless( \%o ) ;
498             }
499             elsif ( $self->{reftype} eq 'arrayref' ) {
500 0           my @o = map { innerclone( $_ ) } @$data ;
  0            
501 0           return $self->{clone} = $self->mybless( \@o ) ;
502             }
503             elsif ( $self->{reftype} eq 'scalarref' ) {
504 0           my $o = ${ $self->data } ;
  0            
505 0           return $self->mybless( \$o ) ;
506             }
507              
508 0           return $self->{clone} = $self->{data} ;
509             }
510              
511             sub scalarref {
512 0     0 0   my $self = shift ;
513             my $data = $self->{globals}->{scalarrefs}->{
514 0   0       $self->refto || $self->{top} }->{data} ;
515 0           return [ $data, $self ] ;
516             }
517              
518             ## what it is as a reference is not what it is as a standalone object
519              
520             sub item {
521 0     0 0   my $self = shift ;
522            
523 0 0         return [ $self, undef ] unless ref $self ;
524 0 0 0       return [ $self, tied %$self ] if ref $self eq 'HASH' && tied %$self ;
525 0 0 0       return [ $self, tied @$self ] if ref $self eq 'ARRAY' && tied @$self ;
526 0 0 0       return [ $self, tied $$self ] if ref $self eq 'SCALAR' && tied $$self ;
527              
528 0 0         return [ $self, undef ]
529             if grep ref $self eq $_, qw( HASH ARRAY SCALAR ) ;
530 0 0         return [ $self->{data}, $self ] if ref $self eq __PACKAGE__ ;
531              
532 0 0 0       return [ $self, tied %$self ] if $self->isa('HASH') && tied %$self ;
533 0 0 0       return [ $self, tied @$self ] if $self->isa('ARRAY') && tied @$self ;
534 0 0 0       return [ $self, tied $$self ] if $self->isa('SCALAR') && tied $$self ;
535              
536 0           return [ $self, undef ] ;
537             }
538              
539             sub update {
540 0     0 0   my $self = shift ;
541 0           my @sorted = () ;
542 0           push @sorted, [ splice @_, 0, 2 ] while @_ ;
543 0           map { $self->record->{ $_->[0] } = $_->[1] } @sorted ;
  0            
544 0           $self->{update} = 1 ;
545 0 0         $self->{globals}->{clone}->update
546             if $self->{globals}->{clone} ;
547 0           return $self ;
548             }
549              
550             sub mybless {
551 0     0 0   my $self = shift ;
552 0 0         my $o = @_? shift( @_ ): $self ;
553              
554 0           my $blesstype = $self->record->{blesstype} ;
555 0 0 0       return $o unless ref $o && $blesstype ;
556 0 0         return $o if grep $_ eq $blesstype, qw( SCALAR ARRAY HASH ) ;
557 0           return bless $o, $blesstype ;
558             }
559              
560             sub resequence {
561 0     0 0   my $self = shift ;
562 0           map { $self->data( $_, 1 )->update()->{sequence} = $_ }
  0            
563 0           0..$#{ $self->{data} } ;
564 0           return $self->FETCHSIZE ;
565             }
566              
567             sub refcount {
568 0     0 0   my $self = shift ;
569 0 0         my $recid = @_? shift( @_ ): $self->{top} ;
570              
571 0 0         return --$self->{globals}->{refcount}->{ $recid } > 0?
572             (): ( $recid ) ;
573             }
574              
575             sub refrecord {
576 0     0 0   my $self = shift ;
577 0 0         return undef unless ref $self->{data} ;
578              
579 0           my $ii = item( $self->{data} ) ;
580 0 0         return undef unless ref $ii->[1] eq ref $self ;
581              
582 0           my $k = $ii->[1]->objectkey ;
583 0           my $v = $self->{globals}->{memory}->{$k} ;
584 0 0         return undef unless $v ;
585              
586 0 0         return $v->[ $_[0] ] if @_ ;
587              
588 0           my $rv = item( $v->[0] )->[1] ;
589 0   0       return $rv->refrecord( @_ ) || $rv ;
590             }
591              
592             sub getkids {
593 0     0 0   my $o = shift ;
594 0           my @out = () ;
595 0 0         return () unless ref $o ;
596              
597 0 0         @out = values %$o if ref $o eq 'HASH' ;
598 0 0 0       @out = @$o unless @out || ref $o ne 'ARRAY' ;
599 0 0         return () if ref $o eq 'SCALAR' ;
600 0 0         return () if ref $o eq __PACKAGE__ ;
601              
602 0 0 0       @out = values %$o unless @out || ! $o->isa('HASH') ;
603 0 0 0       @out = @$o unless @out || ! $o->isa('ARRAY') ;
604 0           return grep ref $_, map { item( $_ )->[1] } @out ;
  0            
605             }
606              
607             sub setreference {
608 0     0 0   my $v = shift ;
609 0           my $o = item( $v )->[1] ;
610              
611 0 0         if ( ref $o ne __PACKAGE__ ) {}
    0          
612             elsif ( $o->{reftype} eq 'scalarref' ) {
613 0           $o->{globals}->{refcount}->{ $o->{top} }++ ;
614             }
615             else {
616 0           map { $o->{globals}->{refcount}->{ $_->{top} }++ }
  0            
617             getkids( $o->data ) ;
618             }
619              
620 0           return $v ;
621             }
622              
623             sub newelement {
624 0     0 0   my $clone = shift ;
625 0           my $self = bless {}, $clone->package ;
626 0           map { $self->{$_} = $clone->{$_} } @NoSQL::PL2SQL::members ;
  0            
627              
628 0           $self->{parent} = $clone->{top} ;
629 0           $self->{reftype} = 'item' ;
630 0 0         $self->{data} = setreference( shift @_ ) if @_ ;
631 0           $self->{update} = 1 ;
632 0 0         $self->{globals}->{clone}->update
633             if $self->{globals}->{clone} ;
634 0           return $self ;
635             }
636              
637             sub equals {
638 0     0 0   my $self = shift ;
639 0           my @dd = ( $self->{data}, $self->{ref} ) ;
640 0 0         $dd[1] = '' unless defined $dd[1] ;
641 0 0         return ref $dd[0]? $dd[0] == $dd[1]: $dd[0] eq $dd[1] ;
642             }
643              
644             sub scalarok {
645 0     0 0   my $self = shift ;
646              
647 0 0         return () unless $self->{top} ;
648 0 0         return () if grep $self->{reftype} eq $_,
649             qw( scalarref arrayref hashref ) ;
650 0 0         return () if ref $self->{data} ;
651 0 0         return () if exists $self->{ref} ;
652 0 0         return () if length $self->{data} > 512 ;
653              
654 0           my $item = NoSQL::PL2SQL::Perldata::item(
655             $self->{perldata}, $self->{top} )->[1] ;
656 0           my @types = NoSQL::PL2SQL::Node::typemap( $self->{data}, $item ) ;
657 0 0         return () unless $types[0] eq $types[1] ;
658              
659             ## scalars are perfectly identical
660 0 0 0       return ( undef ) unless $self->record->{defined} || defined $item ;
661 0 0 0       return ( undef ) if $self->record->{defined}
      0        
662             && defined $self->{data}
663             && $self->{data} eq $item ;
664              
665             ## update scalar information
666 0   0       $types[0] ||= 'stringrepr' ;
667 0           return ( chainedstring => undef,
668             stringrepr => $self->{data},
669             $types[0] => $self->{data},
670             defined => defined $self->{data} ) ;
671             }
672              
673             sub updates {
674 0     0 1   my $o = shift ;
675 0           my $r = $o->record ;
676              
677 0 0         my %nvp = map { $_ => exists $r->{$_}? $r->{$_}: undef }
  0            
678             qw( id intkey deleted ) ;
679 0 0         $nvp{intkey} = $o->{sequence} if exists $o->{sequence} ;
680              
681 0           my $self = bless { sql => \%nvp }, 'NoSQL::PL2SQL::Node' ;
682              
683 0           my %globals = map { $_ => $r->{$_} } qw( objectid objecttype ) ;
  0            
684              
685 0 0         if ( ! exists $o->{data} ) {}
    0          
    0          
    0          
686             elsif ( $o->record->{deleted} ) {}
687             elsif ( my @rewrite = $o->scalarok ) {
688 0 0         my %rewrite = @rewrite > 1? @rewrite: () ;
689 0           map { $self->{sql}->{$_} = $rewrite{$_} } keys %rewrite ;
  0            
690             }
691             elsif ( $o->equals( $o->{data}, $o->{ref} ) ) {}
692             ## existing container ##
693             else {
694             ## scalar/container replacement ##
695 0           my @xml = NoSQL::PL2SQL::Node->factory( 0,
696             $globals{objectid},
697             $o->{data},
698             $globals{objecttype} ) ;
699 0           pop @xml ; ## perldata element
700              
701             ## pl2sql can't distinguish a scalarref
702 0 0 0       $xml[-1]{sql}{reftype} = $o->{reftype}
703             if $xml[-1]{sql}{reftype} eq 'scalar'
704             && $o->{reftype} eq 'scalarref' ;
705              
706 0           map { $nvp{$_} = undef } qw( intdata doubledata stringdata ) ;
  0            
707 0           my $sql = $xml[-1]{sql} ;
708 0           map { $nvp{$_} = $sql->{$_} } keys %$sql ;
  0            
709 0           $xml[-1]{sql} = \%nvp ;
710              
711 0           foreach my $xml ( @xml ) {
712 0 0         map { $xml->{sql}->{$_} = $globals{$_} }
  0            
713             keys %globals
714             unless $xml == $xml[-1] ;
715             }
716              
717 0           return @xml ;
718             }
719              
720 0           return $self ;
721             }
722              
723             sub objectkey {
724 0     0 0   my $self = shift ;
725 0           return overload::StrVal( $self ) ;
726             }
727              
728             sub package {
729 0     0 0   return __PACKAGE__ ;
730             }
731              
732             sub FETCH {
733 0     0     my $self = self( shift @_ ) ;
734 0           my $k = shift ;
735 0           my $item = $self->data( $k ) ;
736              
737 0 0 0       return ref( $item->[0] )
738             && $item->[1]->{top}
739             && $item->[1]->{reftype} eq 'scalarref'?
740             $item->[1]->data: $item->[0] ;
741             }
742              
743             sub STORE {
744 0     0     my $self = self( shift @_ ) ;
745 0           my $v = pop ;
746 0 0         my $k = shift( @_ ) if @_ ;
747 0           my $element ;
748             my $o ;
749              
750 0 0         if ( $self->{reftype} eq 'hashref' ) {
    0          
    0          
751 0 0         $o = $self->{data}->{$k} = $self->{data}->{$k}?
752             item( $self->{data}->{$k} )->[1]:
753             $self->newelement ;
754 0           $o->{key} = $k ;
755             }
756             elsif ( $self->{reftype} eq 'arrayref' ) {
757 0           my $seqflag = ! $self->{data}->[$k] ;
758 0 0         $o = $self->{data}->[$k] = $self->{data}->[$k]?
759             item( $self->{data}->[$k] )->[1]:
760             $self->newelement ;
761              
762 0   0       map { $self->{data}->[$_] ||= $self->newelement() }
  0            
763 0           0..$#{ $self->{data} } ;
764 0 0         $self->resequence if $seqflag ;
765             }
766             elsif ( $self->{reftype} eq 'scalarref' ) {
767             $o = $self->{globals}->{scalarrefs}->{
768 0   0       $self->refto || $self->{top} } ;
769 0   0       $o->{globals}->{header} ||= $self->{globals}->{header} ;
770             }
771             else {
772 0           warn $self->{reftype} ;
773             }
774            
775 0 0         $o->CLEAR if grep $_ eq $o->{reftype}, qw( hashref arrayref ) ;
776 0           $o->{data} = setreference( $v ) ;
777 0           $o->update( deleted => undef ) ;
778              
779 0           return $o->{data} ;
780             }
781              
782             sub STORESIZE {
783 0     0     my $self = self( shift @_ ) ;
784 0           my $count = shift ;
785             }
786              
787             sub EXTEND {
788 0     0     my $self = self( shift @_ ) ;
789 0           $self->STORESIZE( @_ ) ;
790             }
791              
792             sub FETCHSIZE {
793 0     0     my $self = self( shift @_ ) ;
794 0           return scalar @{ $self->{data} } ;
  0            
795             }
796              
797             sub POP {
798 0     0     my $self = self( shift @_ ) ;
799 0           my $k = $self->FETCHSIZE -1 ;
800 0           return $self->DELETE( $k, 1 ) ;
801             }
802              
803             sub PUSH {
804 0     0     my $self = self( shift @_ ) ;
805 0           my @add = map { $self->newelement( $_ ) } @_ ;
  0            
806              
807 0           push @{ $self->{data} }, @add ;
  0            
808 0           return $self->resequence ;
809             }
810              
811             sub SHIFT {
812 0     0     my $self = self( shift @_ ) ;
813 0           my $k = 0 ;
814 0           my $rv = $self->DELETE( $k, 1 ) ;
815              
816 0           $self->resequence ;
817 0           return $rv ;
818             }
819              
820             sub UNSHIFT {
821 0     0     my $self = self( shift @_ ) ;
822              
823 0           unshift @{ $self->{data} },
  0            
824 0           map { $self->newelement( $_ ) } @_ ;
825 0           return $self->resequence ;
826             }
827              
828             sub SPLICE {
829 0     0     my $self = self( shift @_ ) ;
830 0 0         my $offset = @_? shift( @_ ): 0 ;
831 0 0         my $length = @_? shift( @_ ): $self->FETCHSIZE -$offset ;
832 0           my @add = map { $self->newelement( $_ ) } @_ ;
  0            
833              
834 0           my @sample = ( 0..$#{ $self->{data} } ) ;
  0            
835 0           my @gone = splice @sample, $offset, $length, map { \$_ }
  0            
836             0..$#add ;
837 0 0         my @data = map { ref $_? $add[ $$_ ]: $self->{data}->[$_] }
  0            
838             @sample ;
839              
840 0           my @rv = map { $self->FETCH( $_ ) } @gone ;
  0            
841 0           map { $self->DELETE( $_, 1 ) } reverse @gone ;
  0            
842              
843 0           $self->{data} = \@data ;
844 0           $self->resequence ;
845 0           return @rv ;
846             }
847              
848             sub DELETE {
849 0     0     my $rv = FETCH( @_ ) ;
850 0           my $self = self( shift @_ ) ;
851 0           my $k = shift ;
852 0           my $obliterate = shift ;
853              
854 0           $self->sqlclone ; ## need to expose all references
855              
856 0           my $item = $self->data( $k ) ;
857 0           my $o = $item->[1] ;
858 0 0         return undef unless $o ;
859              
860 0 0         $o->CLEAR unless $o->{reftype} eq 'item' ;
861 0 0         $o->update( deleted => 1 ) if $obliterate ;
862              
863 0 0         if ( $self->{reftype} eq 'hashref' ) {
    0          
    0          
864 0           $o->update( deleted => 1 ) ;
865 0           delete $self->{data}->{$k} ;
866             }
867             elsif ( $self->{reftype} eq 'arrayref' ) {
868 0           $o->update( defined => 0 ) ;
869 0           $obliterate?
870 0 0         splice @{ $self->{data} }, $k, 1:
871             delete $self->{data}->[$k] ;
872             }
873             elsif ( $self->{reftype} eq 'scalarref' ) {
874 0           $o->update( defined => 0 ) ;
875 0           delete $self->{data} ;
876             }
877              
878 0 0         delete $o->{update} unless $o->{top} ;
879 0           return $rv ;
880             }
881              
882             sub CLEAR {
883 0     0     my $self = self( shift @_ ) ;
884              
885 0           my @delete = NoSQL::PL2SQL::Perldata::descendants(
886             $self->{perldata}, $self->{top}, 1 ) ;
887 0           pop @delete ;
888              
889 0           my @deleteok = map { $self->refcount( $_ ) } @delete ;
  0            
890 0           $self->{delete} = \@deleteok ;
891              
892 0           map { delete $self->{perldata}->{$_} } @{ $self->{delete} } ;
  0            
  0            
893             }
894              
895             sub EXISTS {
896 0     0     my $self = self( shift @_ ) ;
897 0           my $k = shift ;
898              
899 0           return exists $self->{data}->{$k} ;
900             }
901              
902             sub FIRSTKEY {
903 0     0     my $self = self( shift @_ ) ;
904 0           $self->{keys} = [ keys %{ $self->{data} } ] ;
  0            
905 0           return $self->NEXTKEY ;
906             }
907              
908             sub NEXTKEY {
909 0     0     my $self = self( shift @_ ) ;
910 0           return shift @{ $self->{keys} } ;
  0            
911             }
912              
913             sub debug {
914 0     0 0   do {
915 2     2   26 no warnings ;
  2         4  
  2         263  
916 0           my @cc = caller ;
917 0           my $flag = sprintf '@%d> ', $cc[-1] ;
918 0           push @NoSQL::PL2SQL::debug, $flag .join( '|', @_ ) ;
919             } ;
920             }
921              
922             1;
923             __END__