Fixed problem with large keys over 65535 bytes.
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Engine / DBI.pm
1 package DBM::Deep::Engine::DBI;
2
3 use 5.006_000;
4
5 use strict;
6 use warnings FATAL => 'all';
7
8 use base 'DBM::Deep::Engine';
9
10 use DBM::Deep::Sector::DBI ();
11 use DBM::Deep::Storage::DBI ();
12
13 sub sector_type { 'DBM::Deep::Sector::DBI' }
14 sub iterator_class { 'DBM::Deep::Iterator::DBI' }
15
16 sub new {
17     my $class = shift;
18     my ($args) = @_;
19
20     $args->{storage} = DBM::Deep::Storage::DBI->new( $args )
21         unless exists $args->{storage};
22
23     my $self = bless {
24         storage => undef,
25     }, $class;
26
27     # Grab the parameters we want to use
28     foreach my $param ( keys %$self ) {
29         next unless exists $args->{$param};
30         $self->{$param} = $args->{$param};
31     }
32
33     return $self;
34 }
35
36 sub setup {
37     my $self = shift;
38     my ($obj) = @_;
39
40     # Default the id to 1. This means that we will be creating a row if there
41     # isn't one. The assumption is that the row_id=1 cannot never be deleted. I
42     # don't know if this is a good assumption.
43     $obj->{base_offset} ||= 1;
44
45     my ($rows) = $self->storage->read_from(
46         refs => $obj->_base_offset,
47         qw( ref_type ),
48     );
49
50     # We don't have a row yet.
51     unless ( @$rows ) {
52         $self->storage->write_to(
53             refs => $obj->_base_offset,
54             ref_type => $obj->_type,
55         );
56     }
57
58     my $sector = DBM::Deep::Sector::DBI::Reference->new({
59         engine => $self,
60         offset => $obj->_base_offset,
61     });
62 }
63
64 sub read_value {
65     my $self = shift;
66     my ($obj, $key) = @_;
67
68     my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
69         or return;
70
71 #    if ( $sector->staleness != $obj->_staleness ) {
72 #        return;
73 #    }
74
75 #    my $key_md5 = $self->_apply_digest( $key );
76
77     my $value_sector = $sector->get_data_for({
78         key => $key,
79 #        key_md5    => $key_md5,
80         allow_head => 1,
81     });
82
83     unless ( $value_sector ) {
84         $value_sector = DBM::Deep::Sector::DBI::Scalar->new({
85             engine    => $self,
86             data      => undef,
87             data_type => 'S',
88         });
89
90         $sector->write_data({
91 #            key_md5 => $key_md5,
92             key     => $key,
93             value   => $value_sector,
94         });
95     }
96
97     return $value_sector->data;
98 }
99
100 sub get_classname {
101     my $self = shift;
102     my ($obj) = @_;
103
104     return;
105 }
106
107 sub make_reference {
108     my $self = shift;
109     my ($obj, $old_key, $new_key) = @_;
110
111     my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
112         or return;
113
114 #    if ( $sector->staleness != $obj->_staleness ) {
115 #        return;
116 #    }
117
118     my $value_sector = $sector->get_data_for({
119         key        => $old_key,
120         allow_head => 1,
121     });
122
123     unless ( $value_sector ) {
124         $value_sector = DBM::Deep::Sector::DBI::Scalar->new({
125             engine => $self,
126             data   => undef,
127         });
128
129         $sector->write_data({
130             key     => $old_key,
131             value   => $value_sector,
132         });
133     }
134
135     if ( $value_sector->isa( 'DBM::Deep::Sector::DBI::Reference' ) ) {
136         $sector->write_data({
137             key     => $new_key,
138             value   => $value_sector,
139         });
140         $value_sector->increment_refcount;
141     }
142     else {
143         $sector->write_data({
144             key     => $new_key,
145             value   => $value_sector->clone,
146         });
147     }
148
149     return;
150 }
151
152 # exists returns '', not undefined.
153 sub key_exists {
154     my $self = shift;
155     my ($obj, $key) = @_;
156
157     my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
158         or return '';
159
160 #    if ( $sector->staleness != $obj->_staleness ) {
161 #        return '';
162 #    }
163
164     my $data = $sector->get_data_for({
165 #        key_md5    => $self->_apply_digest( $key ),
166         key        => $key,
167         allow_head => 1,
168     });
169
170     # exists() returns 1 or '' for true/false.
171     return $data ? 1 : '';
172 }
173
174 sub delete_key {
175     my $self = shift;
176     my ($obj, $key) = @_;
177
178     my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
179         or return '';
180
181 #    if ( $sector->staleness != $obj->_staleness ) {
182 #        return '';
183 #    }
184
185     return $sector->delete_key({
186 #        key_md5    => $self->_apply_digest( $key ),
187         key        => $key,
188         allow_head => 0,
189     });
190 }
191
192 sub write_value {
193     my $self = shift;
194     my ($obj, $key, $value) = @_;
195
196     my $r = Scalar::Util::reftype( $value ) || '';
197     {
198         last if $r eq '';
199         last if $r eq 'HASH';
200         last if $r eq 'ARRAY';
201
202         DBM::Deep->_throw_error(
203             "Storage of references of type '$r' is not supported."
204         );
205     }
206
207     # Load the reference entry
208     # Determine if the row was deleted under us
209     # 
210
211     my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
212         or die "Cannot load sector at '@{[$obj->_base_offset]}'\n";;
213
214     my ($type, $class);
215     if ( $r eq 'ARRAY' || $r eq 'HASH' ) {
216         my $tmpvar;
217         if ( $r eq 'ARRAY' ) {
218             $tmpvar = tied @$value;
219         } elsif ( $r eq 'HASH' ) {
220             $tmpvar = tied %$value;
221         }
222
223         if ( $tmpvar ) {
224             my $is_dbm_deep = eval { local $SIG{'__DIE__'}; $tmpvar->isa( 'DBM::Deep' ); };
225
226             unless ( $is_dbm_deep ) {
227                 DBM::Deep->_throw_error( "Cannot store something that is tied." );
228             }
229
230             unless ( $tmpvar->_engine->storage == $self->storage ) {
231                 DBM::Deep->_throw_error( "Cannot store values across DBM::Deep files. Please use export() instead." );
232             }
233
234             # Load $tmpvar's sector
235
236             # First, verify if we're storing the same thing to this spot. If we
237             # are, then this should be a no-op. -EJS, 2008-05-19
238             
239             # See whether or not we are storing ourselves to ourself.
240             # Write the sector as data in this reference (keyed by $key)
241             my $value_sector = $self->load_sector( $tmpvar->_base_offset, 'refs' );
242             $sector->write_data({
243                 key     => $key,
244 #                key_md5 => $self->_apply_digest( $key ),
245                 value   => $value_sector,
246             });
247             $value_sector->increment_refcount;
248
249             return 1;
250         }
251
252         $type = substr( $r, 0, 1 );
253         $class = 'DBM::Deep::Sector::DBI::Reference';
254     }
255     else {
256         if ( tied($value) ) {
257             DBM::Deep->_throw_error( "Cannot store something that is tied." );
258         }
259
260         $class = 'DBM::Deep::Sector::DBI::Scalar';
261         $type  = 'S';
262     }
263
264     # Create this after loading the reference sector in case something bad
265     # happens. This way, we won't allocate value sector(s) needlessly.
266     my $value_sector = $class->new({
267         engine => $self,
268         data   => $value,
269         type   => $type,
270     });
271
272     $sector->write_data({
273         key     => $key,
274 #        key_md5 => $self->_apply_digest( $key ),
275         value   => $value_sector,
276     });
277
278     # This code is to make sure we write all the values in the $value to the
279     # disk and to make sure all changes to $value after the assignment are
280     # reflected on disk. This may be counter-intuitive at first, but it is
281     # correct dwimmery.
282     #   NOTE - simply tying $value won't perform a STORE on each value. Hence,
283     # the copy to a temp value.
284     if ( $r eq 'ARRAY' ) {
285         my @temp = @$value;
286         tie @$value, 'DBM::Deep', {
287             base_offset => $value_sector->offset,
288 #            staleness   => $value_sector->staleness,
289             storage     => $self->storage,
290             engine      => $self,
291         };
292         @$value = @temp;
293         bless $value, 'DBM::Deep::Array' unless Scalar::Util::blessed( $value );
294     }
295     elsif ( $r eq 'HASH' ) {
296         my %temp = %$value;
297         tie %$value, 'DBM::Deep', {
298             base_offset => $value_sector->offset,
299 #            staleness   => $value_sector->staleness,
300             storage     => $self->storage,
301             engine      => $self,
302         };
303
304         %$value = %temp;
305         bless $value, 'DBM::Deep::Hash' unless Scalar::Util::blessed( $value );
306     }
307
308     return 1;
309 }
310
311 sub begin_work {
312     my $self = shift;
313     my ($obj) = @_;
314 }
315
316 sub rollback {
317     my $self = shift;
318     my ($obj) = @_;
319 }
320
321 sub commit {
322     my $self = shift;
323     my ($obj) = @_;
324 }
325
326
327 1;
328 __END__