File Coverage

blib/lib/Geo/SpatialDB/Storage/LMDB_Storable.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Geo::SpatialDB::Storage::LMDB_Storable;
2             $Geo::SpatialDB::Storage::LMDB_Storable::VERSION = '0.000_001'; # TRIAL
3              
4 2     2   1271 $Geo::SpatialDB::Storage::LMDB_Storable::VERSION = '0.000001';use Moo 2;
  2         8797  
  2         8  
5 2     2   2226 use LMDB_File ':flags', ':cursor_op', ':error';
  0            
  0            
6             use Storable 'freeze', 'thaw';
7             sub _croak { require Carp; goto &Carp::croak }
8             use namespace::clean;
9              
10             extends 'Geo::SpatialDB::Storage';
11              
12             # ABSTRACT: Key/value storage on LMDB, encoding Perl objects with 'Storable'
13              
14              
15             has path => ( is => 'ro', required => 1 );
16             has readonly => ( is => 'ro', default => sub { 0 } );
17             has mapsize => ( is => 'ro', default => sub { 0xC00000000 } );
18             has run_with_scissors => ( is => 'ro', default => sub { 0 } );
19              
20             sub BUILD {
21             my $self= shift;
22             # Immediately try to access the DB so that errors get reported
23             # as soon as user creates the object
24             $self->get(0);
25             }
26              
27             sub DESTROY {
28             my $self= shift;
29             warn "Destroying LMDB_Storable instance with uncommitted data!"
30             if $self->_txn && $self->_written;
31             }
32              
33             has _env => ( is => 'lazy' );
34             sub _build__env {
35             my $self= shift;
36             my $path= $self->path;
37             LMDB::Env->new("$path", {
38             mapsize => $self->mapsize,
39             flags =>
40             (-d $path? 0 : MDB_NOSUBDIR)
41             | ($self->readonly? MDB_RDONLY : 0)
42             | ($self->run_with_scissors? MDB_WRITEMAP|MDB_NOMETASYNC : 0)
43             }
44             );
45             }
46              
47             has _txn => ( is => 'lazy', clearer => 1 );
48             has _written => ( is => 'rw' );
49             sub _build__txn {
50             shift->_env->BeginTxn;
51             }
52              
53             has _db => ( is => 'lazy', clearer => 1 );
54             sub _build__db {
55             shift->_txn->OpenDB();
56             }
57              
58             my $storable_magic= substr(freeze({}), 0, 1);
59             sub die_invalid_assumption {
60             die "Author has made invalid assumptions for your version of Storable and needs to fix his code";
61             }
62             $storable_magic =~ /[\0-\x19]/ or die_invalid_assumption();
63              
64              
65             sub get {
66             my $v= shift->_db->get(shift);
67             $v= thaw($v) if defined $v and substr($v, 0, 1) eq $storable_magic;
68             return $v;
69             }
70              
71              
72             sub put {
73             my ($self, $k, $v)= @_;
74             $self->{_written}= 1;
75             if (!defined $v) {
76             local $LMDB_File::die_on_err= 0;
77             my ($ret, $err);
78             {
79             local $@;
80             $ret= $self->_db->del($k);
81             $err= $@;
82             }
83             croak $err if $ret && $ret != MDB_NOTFOUND;
84             return;
85             }
86             elsif (ref $v) {
87             $v= freeze($v);
88             substr($v, 0, 1) eq $storable_magic
89             or die_invalid_assumption();
90             } else {
91             ord(substr($v, 0, 1)) > 0x1F or _croak("scalars must not start with control characters");
92             }
93             $self->_db->put($k, $v);
94             }
95              
96              
97             sub commit {
98             my $self= shift;
99             if ($self->_txn) {
100             $self->_txn->commit;
101             $self->_clear_db;
102             $self->_clear_txn;
103             }
104             $self->{_written}= 0;
105             }
106              
107             sub rollback {
108             my $self= shift;
109             if ($self->_txn) {
110             $self->_txn->abort;
111             $self->_clear_db;
112             $self->_clear_txn;
113             }
114             $self->{_written}= 0;
115             }
116              
117              
118             sub iterator {
119             my ($self, $key)= @_;
120             my $op= defined $key? MDB_SET_RANGE : MDB_FIRST;
121             my $cursor= $self->_db->Cursor;
122             my $data;
123             return sub {
124             local $LMDB_File::die_on_err= 0;
125             my $ret= $cursor->get($key, $data, $op);
126             $op= MDB_NEXT;
127             if ($ret) {
128             return if $ret == MDB_NOTFOUND;
129             die $LMDB_File::last_err
130             }
131             return $key unless wantarray;
132             $data= thaw($data) if substr($data, 0, 1) eq $storable_magic;
133             return ($key, $data);
134             }
135             }
136              
137             1;
138              
139             __END__