Fixed problem with second-level values being overwritten when accessed.
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Engine / DBI.pm
CommitLineData
2c70efe1 1package DBM::Deep::Engine::DBI;
2
3use 5.006_000;
4
5use strict;
6use warnings FATAL => 'all';
7
8use base 'DBM::Deep::Engine';
9
d6ecf579 10use DBM::Deep::Sector::DBI ();
11use DBM::Deep::Storage::DBI ();
12
13sub sector_type { 'DBM::Deep::Sector::DBI' }
19b913ce 14sub iterator_class { 'DBM::Deep::Iterator::DBI' }
d6ecf579 15
a4d36ff6 16sub 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
36sub 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}
d6ecf579 63
2c70efe1 64sub read_value {
65 my $self = shift;
66 my ($obj, $key) = @_;
a4d36ff6 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({
cf4a1344 85 engine => $self,
86 data => undef,
87 data_type => 'S',
a4d36ff6 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;
2c70efe1 98}
99
100sub get_classname {
101 my $self = shift;
102 my ($obj) = @_;
350896ee 103
104 return;
2c70efe1 105}
106
107sub make_reference {
108 my $self = shift;
109 my ($obj, $old_key, $new_key) = @_;
641aa32d 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;
2c70efe1 150}
151
a4d36ff6 152# exists returns '', not undefined.
2c70efe1 153sub key_exists {
154 my $self = shift;
155 my ($obj, $key) = @_;
a4d36ff6 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 : '';
2c70efe1 172}
173
174sub delete_key {
175 my $self = shift;
176 my ($obj, $key) = @_;
a4d36ff6 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 });
2c70efe1 190}
191
192sub 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
a4d36ff6 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);
2c70efe1 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)
a4d36ff6 241 my $value_sector = $self->load_sector( $tmpvar->_base_offset );
242 $sector->write_data({
243 key => $key,
244 key_md5 => $self->_apply_digest( $key ),
245 value => $value_sector,
246 });
2c70efe1 247 $value_sector->increment_refcount;
248
249 return 1;
250 }
251
252 $type = substr( $r, 0, 1 );
a4d36ff6 253 $class = 'DBM::Deep::Sector::DBI::Reference';
2c70efe1 254 }
255 else {
256 if ( tied($value) ) {
257 DBM::Deep->_throw_error( "Cannot store something that is tied." );
258 }
a4d36ff6 259
260 $class = 'DBM::Deep::Sector::DBI::Scalar';
261 $type = 'S';
2c70efe1 262 }
263
a4d36ff6 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
2c70efe1 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,
a4d36ff6 288# staleness => $value_sector->staleness,
2c70efe1 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,
a4d36ff6 299# staleness => $value_sector->staleness,
2c70efe1 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
2c70efe1 311sub begin_work {
312 my $self = shift;
313 my ($obj) = @_;
314}
315
316sub rollback {
317 my $self = shift;
318 my ($obj) = @_;
319}
320
321sub commit {
322 my $self = shift;
323 my ($obj) = @_;
324}
325
326
3271;
328__END__