Apply some changes
[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
1f1f7e24 104 my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
105 or return;
106
107 return $sector->get_classname;
2c70efe1 108}
109
110sub make_reference {
111 my $self = shift;
112 my ($obj, $old_key, $new_key) = @_;
641aa32d 113
114 my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
115 or return;
116
117# if ( $sector->staleness != $obj->_staleness ) {
118# return;
119# }
120
121 my $value_sector = $sector->get_data_for({
122 key => $old_key,
123 allow_head => 1,
124 });
125
126 unless ( $value_sector ) {
127 $value_sector = DBM::Deep::Sector::DBI::Scalar->new({
128 engine => $self,
129 data => undef,
130 });
131
132 $sector->write_data({
133 key => $old_key,
134 value => $value_sector,
135 });
136 }
137
138 if ( $value_sector->isa( 'DBM::Deep::Sector::DBI::Reference' ) ) {
139 $sector->write_data({
140 key => $new_key,
141 value => $value_sector,
142 });
143 $value_sector->increment_refcount;
144 }
145 else {
146 $sector->write_data({
147 key => $new_key,
148 value => $value_sector->clone,
149 });
150 }
151
152 return;
2c70efe1 153}
154
a4d36ff6 155# exists returns '', not undefined.
2c70efe1 156sub key_exists {
157 my $self = shift;
158 my ($obj, $key) = @_;
a4d36ff6 159
160 my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
161 or return '';
162
163# if ( $sector->staleness != $obj->_staleness ) {
164# return '';
165# }
166
167 my $data = $sector->get_data_for({
168# key_md5 => $self->_apply_digest( $key ),
169 key => $key,
170 allow_head => 1,
171 });
172
173 # exists() returns 1 or '' for true/false.
174 return $data ? 1 : '';
2c70efe1 175}
176
177sub delete_key {
178 my $self = shift;
179 my ($obj, $key) = @_;
a4d36ff6 180
181 my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
182 or return '';
183
184# if ( $sector->staleness != $obj->_staleness ) {
185# return '';
186# }
187
188 return $sector->delete_key({
189# key_md5 => $self->_apply_digest( $key ),
190 key => $key,
191 allow_head => 0,
192 });
2c70efe1 193}
194
195sub write_value {
196 my $self = shift;
197 my ($obj, $key, $value) = @_;
198
199 my $r = Scalar::Util::reftype( $value ) || '';
200 {
201 last if $r eq '';
202 last if $r eq 'HASH';
203 last if $r eq 'ARRAY';
204
205 DBM::Deep->_throw_error(
206 "Storage of references of type '$r' is not supported."
207 );
208 }
209
210 # Load the reference entry
211 # Determine if the row was deleted under us
212 #
213
a4d36ff6 214 my $sector = $self->load_sector( $obj->_base_offset, 'refs' )
215 or die "Cannot load sector at '@{[$obj->_base_offset]}'\n";;
216
217 my ($type, $class);
2c70efe1 218 if ( $r eq 'ARRAY' || $r eq 'HASH' ) {
219 my $tmpvar;
220 if ( $r eq 'ARRAY' ) {
221 $tmpvar = tied @$value;
222 } elsif ( $r eq 'HASH' ) {
223 $tmpvar = tied %$value;
224 }
225
226 if ( $tmpvar ) {
227 my $is_dbm_deep = eval { local $SIG{'__DIE__'}; $tmpvar->isa( 'DBM::Deep' ); };
228
229 unless ( $is_dbm_deep ) {
230 DBM::Deep->_throw_error( "Cannot store something that is tied." );
231 }
232
233 unless ( $tmpvar->_engine->storage == $self->storage ) {
234 DBM::Deep->_throw_error( "Cannot store values across DBM::Deep files. Please use export() instead." );
235 }
236
237 # Load $tmpvar's sector
238
239 # First, verify if we're storing the same thing to this spot. If we
240 # are, then this should be a no-op. -EJS, 2008-05-19
241
242 # See whether or not we are storing ourselves to ourself.
243 # Write the sector as data in this reference (keyed by $key)
2467cee7 244 my $value_sector = $self->load_sector( $tmpvar->_base_offset, 'refs' );
a4d36ff6 245 $sector->write_data({
246 key => $key,
2467cee7 247# key_md5 => $self->_apply_digest( $key ),
a4d36ff6 248 value => $value_sector,
249 });
2c70efe1 250 $value_sector->increment_refcount;
251
252 return 1;
253 }
254
255 $type = substr( $r, 0, 1 );
a4d36ff6 256 $class = 'DBM::Deep::Sector::DBI::Reference';
2c70efe1 257 }
258 else {
259 if ( tied($value) ) {
260 DBM::Deep->_throw_error( "Cannot store something that is tied." );
261 }
a4d36ff6 262
263 $class = 'DBM::Deep::Sector::DBI::Scalar';
264 $type = 'S';
2c70efe1 265 }
266
a4d36ff6 267 # Create this after loading the reference sector in case something bad
268 # happens. This way, we won't allocate value sector(s) needlessly.
269 my $value_sector = $class->new({
270 engine => $self,
271 data => $value,
272 type => $type,
273 });
274
275 $sector->write_data({
276 key => $key,
277# key_md5 => $self->_apply_digest( $key ),
278 value => $value_sector,
279 });
280
c2472ede 281 $self->_descend( $value, $value_sector );
2c70efe1 282
283 return 1;
284}
285
bd6b4f3c 286sub begin_work {
287 my $self = shift;
288 die "Transactions are not supported by this engine"
289 unless $self->supports('transactions');
290
291 if ( $self->in_txn ) {
292 DBM::Deep->_throw_error( "Cannot begin_work within an active transaction" );
293 }
294
295 $self->storage->begin_work;
296
297 $self->in_txn( 1 );
298
299 return 1;
300}
301
302sub rollback {
303 my $self = shift;
304 die "Transactions are not supported by this engine"
305 unless $self->supports('transactions');
306
307 if ( !$self->in_txn ) {
308 DBM::Deep->_throw_error( "Cannot rollback without an active transaction" );
309 }
310
311 $self->storage->rollback;
312
313 $self->in_txn( 0 );
314
315 return 1;
316}
317
318sub commit {
319 my $self = shift;
320 die "Transactions are not supported by this engine"
321 unless $self->supports('transactions');
322
323 if ( !$self->in_txn ) {
324 DBM::Deep->_throw_error( "Cannot commit without an active transaction" );
325 }
326
327 $self->storage->commit;
328
329 $self->in_txn( 0 );
330
331 return 1;
332}
333
334sub in_txn {
335 my $self = shift;
336 $self->{in_txn} = shift if @_;
337 $self->{in_txn};
338}
2c70efe1 339
580e5ee2 340sub supports {
bd6b4f3c 341 my $self = shift;
580e5ee2 342 my ($feature) = @_;
2c70efe1 343
bd6b4f3c 344 if ( $feature eq 'transactions' ) {
345# return 1 if $self->storage->driver eq 'sqlite';
346 return;
347 }
580e5ee2 348 return;
2c70efe1 349}
350
2c70efe1 3511;
352__END__