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