r8204@rob-kinyons-computer-2 (orig r10021): rkinyon | 2007-09-28 20:00:36 -0400
rkinyon [Mon, 1 Oct 2007 15:17:40 +0000 (15:17 +0000)]
 Have a 98% solution to making references work.
 r8205@rob-kinyons-computer-2 (orig r10027):  rkinyon | 2007-09-30 13:59:07 -0400
 cached singletons for most cases. The external reference issue is starting to come into larger focus
 r8206@rob-kinyons-computer-2 (orig r10031):  rkinyon | 2007-10-01 11:15:50 -0400
 Added coverage report and tests that were wrong
 r8207@rob-kinyons-computer-2 (orig r10032):  rkinyon | 2007-10-01 11:16:12 -0400
 Fixed date on release of 1.0005

14 files changed:
Changes
lib/DBM/Deep.pm
lib/DBM/Deep.pod
lib/DBM/Deep/Array.pm
lib/DBM/Deep/Engine.pm
lib/DBM/Deep/File.pm
lib/DBM/Deep/Hash.pm
t/31_references.t
t/33_transactions.t
t/39_singletons.t
t/41_transaction_multilevel.t
t/44_upgrade_db.t
t/97_dump_file.t
utils/upgrade_db.pl

diff --git a/Changes b/Changes
index 94c7011..8618e7c 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,14 @@
 Revision history for DBM::Deep.
 
+1.0005 Oct 01 11:15:00 2007 EDT
+    - (This version is compatible with 1.0004)
+    - Added proper singleton support. This means that the following now works:
+        $db->{foo} = [ 1 .. 3];
+        my $x = $db->{foo};
+        my $y = $db->{foo};
+        is( $x, $y ); # Now passes
+      - This means that Data::Dumper now properly reports when $db->{foo} = $db->{bar}
+
 1.0004 Sep 28 12:15:00 2007 EDT
     - (This version is compatible with 1.0003)
     - Fixed the Changes file (wrong version was displayed for 1.0003)
index a72833d..8eb36b9 100644 (file)
@@ -5,7 +5,7 @@ use 5.006_000;
 use strict;
 use warnings;
 
-our $VERSION = q(1.0004);
+our $VERSION = q(1.0005);
 
 use Fcntl qw( :flock );
 
@@ -17,6 +17,10 @@ use Scalar::Util ();
 use DBM::Deep::Engine;
 use DBM::Deep::File;
 
+use overload
+    '""' => sub { overload::StrVal( $_[0] ) },
+    fallback => 1;
+
 ##
 # Setup constants for users to pass to new()
 ##
@@ -251,7 +255,7 @@ sub optimize {
     );
 
     $self->lock();
-    #DBM::Deep::Engine::Sector::Reference->_clear_cache;
+    $self->_engine->clear_cache;
     $self->_copy_node( $db_temp );
     undef $db_temp;
 
index 8b4b689..6b5b2e6 100644 (file)
@@ -945,12 +945,13 @@ the reference. Again, this would generally be considered a feature.
 
 =back
 
-=head2 Data::Dumper and references
+=head2 External references and transactions
 
-As of 1.0003, support for independent Perl datastructures was added (q.v. L</CIRCULAR REFERENCES>
-for more info). However, because DBM::Deep doesn't properly provide the same
-in-memory data-structure for a given location on disk, Data::Dumper (and
-friends) doesn't properly note this. This will be addressed in a future release.
+If you do C<my $x = $db-E<gt>{foo};>, then start a transaction, $x will be
+referencing the database from outside the transaction. A fix for this (and other
+issues with how external references into the database) is being looked into. This
+is the skipped set of tests in t/39_singletons.t and a related issue is the focus
+of t/37_delete_edge_cases.t
 
 =head2 File corruption
 
@@ -1026,12 +1027,12 @@ B<Devel::Cover> report on this distribution's test suite.
   ------------------------------------------ ------ ------ ------ ------ ------
   File                                         stmt   bran   cond    sub  total
   ------------------------------------------ ------ ------ ------ ------ ------
-  blib/lib/DBM/Deep.pm                         94.5   85.0   90.5  100.0   93.6
-  blib/lib/DBM/Deep/Array.pm                  100.0   94.3  100.0  100.0   98.7
-  blib/lib/DBM/Deep/Engine.pm                  95.9   84.9   81.7  100.0   92.8
+  blib/lib/DBM/Deep.pm                         96.9   88.3   90.5  100.0   95.7
+  blib/lib/DBM/Deep/Array.pm                  100.0   95.7  100.0  100.0   99.0
+  blib/lib/DBM/Deep/Engine.pm                  95.5   84.7   81.6   98.4   92.4
   blib/lib/DBM/Deep/File.pm                    97.2   81.6   66.7  100.0   91.9
   blib/lib/DBM/Deep/Hash.pm                   100.0  100.0  100.0  100.0  100.0
-  Total                                        96.5   86.5   83.5  100.0   94.0
+  Total                                        96.7   87.0   83.3   99.2   94.1
   ------------------------------------------ ------ ------ ------ ------ ------
 
 =head1 MORE INFORMATION
index f9b9af2..eb092ac 100644 (file)
@@ -5,7 +5,7 @@ use 5.006_000;
 use strict;
 use warnings;
 
-our $VERSION = q(1.0004);
+our $VERSION = q(1.0005);
 
 # This is to allow DBM::Deep::Array to handle negative indices on
 # its own. Otherwise, Perl would intercept the call to negative
index 99198fe..f8656a3 100644 (file)
@@ -5,7 +5,7 @@ use 5.006_000;
 use strict;
 use warnings;
 
-our $VERSION = q(1.0004);
+our $VERSION = q(1.0005);
 
 use Scalar::Util ();
 
@@ -725,7 +725,7 @@ sub _load_sector {
     my ($offset) = @_;
 
     # Add a catch for offset of 0 or 1
-    return if $offset <= 1;
+    return if !$offset || $offset <= 1;
 
     my $type = $self->storage->read_at( $offset, 1 );
     return if $type eq chr(0);
@@ -875,6 +875,9 @@ sub set_trans_loc { $_[0]{trans_loc} = $_[1] }
 sub chains_loc     { $_[0]{chains_loc} }
 sub set_chains_loc { $_[0]{chains_loc} = $_[1] }
 
+sub cache       { $_[0]{cache} ||= {} }
+sub clear_cache { %{$_[0]->cache} = () }
+
 sub _dump_file {
     my $self = shift;
 
@@ -894,6 +897,10 @@ sub _dump_file {
     );
 
     my $return = "";
+
+    # Header values
+    $return .= "NumTxns: " . $self->num_txns . $/;
+
     # Read the free sector chains
     my %sectors;
     foreach my $multiple ( 0 .. 2 ) {
@@ -948,10 +955,17 @@ sub _dump_file {
                     $return .= sprintf "%08d", unpack($StP{$self->byte_size},
                         substr( $bucket->[-1], $self->hash_size, $self->byte_size),
                     );
-                    foreach my $txn ( 0 .. $self->num_txns - 1 ) {
+                    my $l = unpack( $StP{$self->byte_size},
+                        substr( $bucket->[-1],
+                            $self->hash_size + $self->byte_size,
+                            $self->byte_size,
+                        ),
+                    );
+                    $return .= sprintf " %08d", $l;
+                    foreach my $txn ( 0 .. $self->num_txns - 2 ) {
                         my $l = unpack( $StP{$self->byte_size},
                             substr( $bucket->[-1],
-                                $self->hash_size + $self->byte_size + $txn * ($self->byte_size + $STALE_SIZE),
+                                $self->hash_size + 2 * $self->byte_size + $txn * ($self->byte_size + $STALE_SIZE),
                                 $self->byte_size,
                             ),
                         );
@@ -1673,55 +1687,50 @@ sub get_classname {
     return $self->engine->_load_sector( $class_offset )->data;
 }
 
-#XXX Add singleton handling here
-{
-    my %cache;
-    # XXX This is insufficient
-#    sub _clear_cache { %cache = (); }
-    sub data {
-        my $self = shift;
+sub data {
+    my $self = shift;
 
-#        unless ( $cache{ $self->offset } ) {
-            my $new_obj = DBM::Deep->new({
-                type        => $self->type,
-                base_offset => $self->offset,
-                staleness   => $self->staleness,
-                storage     => $self->engine->storage,
-                engine      => $self->engine,
-            });
+    unless ( $self->engine->cache->{ $self->offset } ) {
+        my $new_obj = DBM::Deep->new({
+            type        => $self->type,
+            base_offset => $self->offset,
+            staleness   => $self->staleness,
+            storage     => $self->engine->storage,
+            engine      => $self->engine,
+        });
 
-            if ( $self->engine->storage->{autobless} ) {
-                my $classname = $self->get_classname;
-                if ( defined $classname ) {
-                    bless $new_obj, $classname;
-                }
+        if ( $self->engine->storage->{autobless} ) {
+            my $classname = $self->get_classname;
+            if ( defined $classname ) {
+                bless $new_obj, $classname;
             }
+        }
 
-            $cache{$self->offset} = $new_obj;
-#        }
-        return $cache{$self->offset};
+        $self->engine->cache->{$self->offset} = $new_obj;
     }
+    return $self->engine->cache->{$self->offset};
+}
 
-    sub free {
-        my $self = shift;
+sub free {
+    my $self = shift;
 
-        # We're not ready to be removed yet.
-        if ( $self->decrement_refcount > 0 ) {
-            return;
-        }
+    # We're not ready to be removed yet.
+    if ( $self->decrement_refcount > 0 ) {
+        return;
+    }
 
-        # Rebless the object into DBM::Deep::Null.
-#        %{$cache{ $self->offset }} = ();
-#        bless $cache{$self->offset}, 'DBM::Deep::Null';
+    # Rebless the object into DBM::Deep::Null.
+    %{ $self->engine->cache->{ $self->offset } } = ();
+    bless $self->engine->cache->{ $self->offset }, 'DBM::Deep::Null';
+    delete $self->engine->cache->{ $self->offset };
 
-        my $blist_loc = $self->get_blist_loc;
-        $self->engine->_load_sector( $blist_loc )->free if $blist_loc;
+    my $blist_loc = $self->get_blist_loc;
+    $self->engine->_load_sector( $blist_loc )->free if $blist_loc;
 
-        my $class_loc = $self->get_class_offset;
-        $self->engine->_load_sector( $class_loc )->free if $class_loc;
+    my $class_loc = $self->get_class_offset;
+    $self->engine->_load_sector( $class_loc )->free if $class_loc;
 
-        $self->SUPER::free();
-    }
+    $self->SUPER::free();
 }
 
 sub increment_refcount {
@@ -1828,10 +1837,19 @@ sub free {
         my $l = unpack( $StP{$e->byte_size}, substr( $rest, $e->hash_size, $e->byte_size ) );
         my $s = $e->_load_sector( $l ); $s->free if $s;
 
-        foreach my $txn ( 0 .. $e->num_txns - 1 ) {
+        # Delete the HEAD sector
+        $l = unpack( $StP{$e->byte_size},
+            substr( $rest,
+                $e->hash_size + $e->byte_size,
+                $e->byte_size,
+            ),
+        );
+        $s = $e->_load_sector( $l ); $s->free if $s;
+
+        foreach my $txn ( 0 .. $e->num_txns - 2 ) {
             my $l = unpack( $StP{$e->byte_size},
                 substr( $rest,
-                    $e->hash_size + $e->byte_size + $txn * ($e->byte_size + $STALE_SIZE),
+                    $e->hash_size + 2 * $e->byte_size + $txn * ($e->byte_size + $STALE_SIZE),
                     $e->byte_size,
                 ),
             );
@@ -2208,10 +2226,11 @@ sub set_entry {
 package DBM::Deep::Null;
 
 use overload
-    'bool'   => sub { undef},
+    'bool'   => sub { undef },
     '""'     => sub { undef },
-    '0+'     => sub { undef},
-    fallback => 1;
+    '0+'     => sub { undef },
+    fallback => 1,
+    nomethod => 'AUTOLOAD';
 
 sub AUTOLOAD { return; }
 
index ccedf04..6571c2e 100644 (file)
@@ -5,7 +5,7 @@ use 5.006_000;
 use strict;
 use warnings;
 
-our $VERSION = q(1.0004);
+our $VERSION = q(1.0005);
 
 use Fcntl qw( :DEFAULT :flock :seek );
 
index b703705..a342d62 100644 (file)
@@ -5,7 +5,7 @@ use 5.006_000;
 use strict;
 use warnings;
 
-our $VERSION = q(1.0004);
+our $VERSION = q(1.0005);
 
 use base 'DBM::Deep';
 
index 0184795..af9bc30 100644 (file)
@@ -1,6 +1,7 @@
 use strict;
 
 use Test::More tests => 16;
+use Test::Deep;
 use Test::Exception;
 use t::common qw( new_fh );
 
@@ -19,8 +20,8 @@ $db->{hash} = \%hash;
 isa_ok( tied(%hash), 'DBM::Deep::Hash' );
 
 is( $db->{hash}{foo}, 1 );
-is_deeply( $db->{hash}{bar}, [ 1 .. 3 ] );
-is_deeply( $db->{hash}{baz}, { a => 42 } );
+cmp_deeply( $db->{hash}{bar}, noclass([ 1 .. 3 ]) );
+cmp_deeply( $db->{hash}{baz}, noclass({ a => 42 }) );
 
 $hash{foo} = 2;
 is( $db->{hash}{foo}, 2 );
@@ -39,8 +40,8 @@ $db->{array} = \@array;
 isa_ok( tied(@array), 'DBM::Deep::Array' );
 
 is( $db->{array}[0], 1 );
-is_deeply( $db->{array}[1], [ 1 .. 3 ] );
-is_deeply( $db->{array}[2], { a => 42 } );
+cmp_deeply( $db->{array}[1], noclass([ 1 .. 3 ]) );
+cmp_deeply( $db->{array}[2], noclass({ a => 42 }) );
 
 $array[0] = 2;
 is( $db->{array}[0], 2 );
index cdf18ad..1edd082 100644 (file)
@@ -233,7 +233,3 @@ SKIP: {
 }
 
 __END__
-
-Tests to add:
-* Two transactions running at the same time
-* Doing a clear on the head while a transaction is running
index 8a3573e..45afc60 100644 (file)
@@ -1,29 +1,64 @@
 use strict;
-use Test::More tests => 5;
+use Test::More tests => 11;
 use Test::Deep;
 use t::common qw( new_fh );
 
 use_ok( 'DBM::Deep' );
 
-my ($fh, $filename) = new_fh();
-my $db = DBM::Deep->new(
-    file => $filename,
-    locking => 1,
-    autoflush => 1,
-);
+{
+    my ($fh, $filename) = new_fh();
+    my $db = DBM::Deep->new(
+        file => $filename,
+        locking => 1,
+        autoflush => 1,
+    );
 
-$db->{foo} = { a => 'b' };
-my $x = $db->{foo};
-my $y = $db->{foo};
+    $db->{a} = 1;
+    $db->{foo} = { a => 'b' };
+    my $x = $db->{foo};
+    my $y = $db->{foo};
 
-print "$x -> $y\n";
-
-TODO: {
-    local $TODO = "Singletons are unimplmeneted yet";
     is( $x, $y, "The references are the same" );
 
     delete $db->{foo};
     is( $x, undef );
     is( $y, undef );
+    is( $x + 0, undef );
+    is( $y + 0, undef );
+    is( $db->{foo}, undef );
+
+    # These shenanigans work to get another hashref
+    # into the same data location as $db->{foo} was.
+    $db->{foo} = {};
+    delete $db->{foo};
+    $db->{foo} = {};
+    $db->{bar} = {};
+
+    is( $x, undef );
+    is( $y, undef );
+}
+
+SKIP: {
+    skip "What do we do with external references and txns?", 2;
+    my ($fh, $filename) = new_fh();
+    my $db = DBM::Deep->new(
+        file => $filename,
+        locking => 1,
+        autoflush => 1,
+        num_txns => 2,
+    );
+
+    $db->{foo} = { a => 'b' };
+    my $x = $db->{foo};
+
+    $db->begin_work;
+    
+        $db->{foo} = { c => 'd' };
+        my $y = $db->{foo};
+
+        # XXX What should happen here with $x and $y?
+        is( $x, $y );
+        is( $x->{c}, 'd' );
+
+    $db->rollback;
 }
-is( $db->{foo}, undef );
index aa2a959..3351e98 100644 (file)
@@ -10,14 +10,14 @@ my $db1 = DBM::Deep->new(
     file => $filename,
     locking => 1,
     autoflush => 1,
-    num_txns  => 16,
+    num_txns  => 2,
 );
 
 my $db2 = DBM::Deep->new(
     file => $filename,
     locking => 1,
     autoflush => 1,
-    num_txns  => 16,
+    num_txns  => 2,
 );
 
 $db1->{x} = { foo => 'y' };
index c39d153..759dbf0 100644 (file)
@@ -13,7 +13,7 @@ BEGIN {
     }
 }
 
-plan tests => 202;
+plan tests => 212;
 
 use t::common qw( new_fh );
 use File::Spec;
@@ -63,7 +63,7 @@ my @output_versions = (
     '0.981', '0.982', '0.983',
     '0.99_01', '0.99_02', '0.99_03', '0.99_04',
     '1.00', '1.000', '1.0000', '1.0001', '1.0002',
-    '1.0003', '1.0004',
+    '1.0003', '1.0004', '1.0005',
 );
 
 foreach my $input_filename (
@@ -117,7 +117,7 @@ foreach my $input_filename (
             eval "use DBM::Deep::10002";
             $db = DBM::Deep::10002->new( $output_filename );
         }
-        elsif ( $v =~ /^1\.000[34]/ ) {
+        elsif ( $v =~ /^1\.000[3-5]/ ) {
             push @INC, 'lib';
             eval "use DBM::Deep";
             $db = DBM::Deep->new( $output_filename );
index 931cb07..1445517 100644 (file)
@@ -11,6 +11,7 @@ my $db = DBM::Deep->new(
 );
 
 is( $db->_dump_file, <<"__END_DUMP__", "Dump of initial file correct" );
+NumTxns: 1
 Chains(B):
 Chains(D):
 Chains(I):
@@ -20,6 +21,7 @@ __END_DUMP__
 $db->{foo} = 'bar';
 
 is( $db->_dump_file, <<"__END_DUMP__", "Dump of initial file correct" );
+NumTxns: 1
 Chains(B):
 Chains(D):
 Chains(I):
index 960abce..3a7c86d 100755 (executable)
@@ -28,7 +28,7 @@ my %is_dev = (
 my %opts = (
   man => 0,
   help => 0,
-  version => '1.0004',
+  version => '1.0005',
   autobless => 1,
 );
 GetOptions( \%opts,
@@ -77,7 +77,7 @@ my %db;
   elsif ( $ver =~ /^1\.000?[0-2]?/) {
     $ver = 2;
   }
-  elsif ( $ver =~ /^1\.000[34]/) {
+  elsif ( $ver =~ /^1\.000[3-5]/) {
     $ver = 3;
   }
   else {