761f2688e57f5caf50d0ebea591a7db591d1f0d5
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Sector / DBI / Reference.pm
1 package DBM::Deep::Sector::DBI::Reference;
2
3 use 5.006_000;
4
5 use strict;
6 use warnings FATAL => 'all';
7
8 use base 'DBM::Deep::Sector::DBI';
9
10 sub table { 'refs' }
11
12 sub _init {
13     my $self = shift;
14
15     my $e = $self->engine;
16
17     unless ( $self->offset ) {
18         my $classname = Scalar::Util::blessed( delete $self->{data} );
19         $self->{offset} = $self->engine->storage->write_to(
20             refs => undef,
21             ref_type  => $self->type,
22             classname => $classname,
23         );
24     }
25     else {
26         my ($rows) = $self->engine->storage->read_from(
27             refs => $self->offset,
28             qw( ref_type ),
29         );
30
31         $self->{type} = $rows->[0]{ref_type};
32     }
33
34     return;
35 }
36
37 sub get_data_for {
38     my $self = shift;
39     my ($args) = @_;
40
41     my ($rows) = $self->engine->storage->read_from(
42         datas => { ref_id => $self->offset, key => $args->{key} },
43         qw( id ),
44     );
45
46     return unless $rows->[0]{id};
47
48     $self->load(
49         $self->engine,
50         $rows->[0]{id},
51         'datas',
52     );
53 }
54
55 sub write_data {
56     my $self = shift;
57     my ($args) = @_;
58
59     if ( ( $args->{value}->type || 'S' ) eq 'S' ) {
60         $args->{value}{offset} = $self->engine->storage->write_to(
61             datas => $args->{value}{offset},
62             ref_id    => $self->offset,
63             data_type => 'S',
64             key       => $args->{key},
65             value     => $args->{value}{data},
66         );
67
68         $args->{value}->reload;
69     }
70     else {
71         # Write the Scalar of the Reference
72         $self->engine->storage->write_to(
73             datas => undef,
74             ref_id    => $self->offset,
75             data_type => 'R',
76             key       => $args->{key},
77             value     => $args->{value}{offset},
78         );
79     }
80 }
81
82 sub delete_key {
83     my $self = shift;
84     my ($args) = @_;
85
86     my $old_value = $self->get_data_for({
87         key => $args->{key},
88     });
89
90     my $data;
91     if ( $old_value ) {
92         $data = $old_value->data({ export => 1 });
93         $old_value->free;
94     }
95
96     return $data;
97 }
98
99 sub get_classname {
100     my $self = shift;
101     my ($rows) = $self->engine->storage->read_from(
102         'refs', $self->offset,
103         qw( classname ),
104     );
105     return unless @$rows;
106     return $rows->[0]{classname};
107 }
108
109 sub data {
110     my $self = shift;
111     my ($args) = @_;
112     $args ||= {};
113
114     my $obj = DBM::Deep->new({
115         type        => $self->type,
116         base_offset => $self->offset,
117 #        staleness   => $self->staleness,
118         storage     => $self->engine->storage,
119         engine      => $self->engine,
120     });
121
122     if ( $self->engine->storage->{autobless} ) {
123         my $classname = $self->get_classname;
124         if ( defined $classname ) {
125             bless $obj, $classname;
126         }
127     }
128
129     # We're not exporting, so just return.
130     unless ( $args->{export} ) {
131         return $obj;
132     }
133
134     # We shouldn't export if this is still referred to.
135     if ( $self->get_refcount > 1 ) {
136         return $obj;
137     }
138
139     return $obj->export;
140 }
141
142 sub free {
143     my $self = shift;
144
145     # We're not ready to be removed yet.
146     if ( $self->decrement_refcount > 0 ) {
147         return;
148     }
149
150     $self->engine->storage->delete_from(
151         'datas', { ref_id => $self->offset },
152     );
153
154     $self->engine->storage->delete_from(
155         'datas', { value => $self->offset, data_type => 'R' },
156     );
157
158     $self->SUPER::free( @_ );
159 }
160
161 sub increment_refcount {
162     my $self = shift;
163     my $refcount = $self->get_refcount;
164     $refcount++;
165     $self->write_refcount( $refcount );
166     return $refcount;
167 }
168
169 sub decrement_refcount {
170     my $self = shift;
171     my $refcount = $self->get_refcount;
172     $refcount--;
173     $self->write_refcount( $refcount );
174     return $refcount;
175 }
176
177 sub get_refcount {
178     my $self = shift;
179     my ($rows) = $self->engine->storage->read_from(
180         'refs', $self->offset,
181         qw( refcount ),
182     );
183     return $rows->[0]{refcount};
184 }
185
186 sub write_refcount {
187     my $self = shift;
188     my ($num) = @_;
189     $self->engine->storage->{dbh}->do(
190         "UPDATE refs SET refcount = ? WHERE id = ?", undef,
191         $num, $self->offset,
192     );
193 }
194
195 1;
196 __END__