line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package KiokuDB::Collapser; |
4
|
20
|
|
|
20
|
|
13563
|
use Moose; |
|
20
|
|
|
|
|
38
|
|
|
20
|
|
|
|
|
218
|
|
5
|
|
|
|
|
|
|
|
6
|
20
|
|
|
20
|
|
108060
|
no warnings 'recursion'; |
|
20
|
|
|
|
|
33
|
|
|
20
|
|
|
|
|
839
|
|
7
|
|
|
|
|
|
|
|
8
|
20
|
|
|
20
|
|
9768
|
use Scope::Guard; |
|
20
|
|
|
|
|
7582
|
|
|
20
|
|
|
|
|
856
|
|
9
|
20
|
|
|
20
|
|
122
|
use Carp qw(croak); |
|
20
|
|
|
|
|
30
|
|
|
20
|
|
|
|
|
873
|
|
10
|
20
|
|
|
20
|
|
90
|
use Scalar::Util qw(isweak refaddr reftype); |
|
20
|
|
|
|
|
28
|
|
|
20
|
|
|
|
|
904
|
|
11
|
|
|
|
|
|
|
|
12
|
20
|
|
|
20
|
|
6691
|
use KiokuDB::Entry; |
|
20
|
|
|
|
|
55
|
|
|
20
|
|
|
|
|
889
|
|
13
|
20
|
|
|
20
|
|
9996
|
use KiokuDB::Entry::Skip; |
|
20
|
|
|
|
|
55
|
|
|
20
|
|
|
|
|
779
|
|
14
|
20
|
|
|
20
|
|
8857
|
use KiokuDB::Reference; |
|
20
|
|
|
|
|
61
|
|
|
20
|
|
|
|
|
769
|
|
15
|
20
|
|
|
20
|
|
9511
|
use KiokuDB::Collapser::Buffer; |
|
20
|
|
|
|
|
63
|
|
|
20
|
|
|
|
|
803
|
|
16
|
20
|
|
|
20
|
|
9797
|
use KiokuDB::Error::UnknownObjects; |
|
20
|
|
|
|
|
58
|
|
|
20
|
|
|
|
|
833
|
|
17
|
|
|
|
|
|
|
|
18
|
20
|
|
|
20
|
|
11299
|
use Data::Visitor 0.18; |
|
20
|
|
|
|
|
408608
|
|
|
20
|
|
|
|
|
799
|
|
19
|
|
|
|
|
|
|
|
20
|
20
|
|
|
20
|
|
146
|
use Set::Object qw(set); |
|
20
|
|
|
|
|
31
|
|
|
20
|
|
|
|
|
1166
|
|
21
|
|
|
|
|
|
|
|
22
|
20
|
|
|
20
|
|
103
|
use namespace::clean -except => 'meta'; |
|
20
|
|
|
|
|
28
|
|
|
20
|
|
|
|
|
130
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
extends qw(Data::Visitor); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
with qw(KiokuDB::Role::UUIDs); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
has '+tied_as_objects' => ( default => 1 ); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
has live_objects => ( |
31
|
|
|
|
|
|
|
isa => "KiokuDB::LiveObjects", |
32
|
|
|
|
|
|
|
is => "ro", |
33
|
|
|
|
|
|
|
required => 1, |
34
|
|
|
|
|
|
|
); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
has backend => ( |
37
|
|
|
|
|
|
|
does => "KiokuDB::Backend", |
38
|
|
|
|
|
|
|
is => "ro", |
39
|
|
|
|
|
|
|
required => 1, |
40
|
|
|
|
|
|
|
); |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
has typemap_resolver => ( |
43
|
|
|
|
|
|
|
isa => "KiokuDB::TypeMap::Resolver", |
44
|
|
|
|
|
|
|
is => "ro", |
45
|
|
|
|
|
|
|
handles => [qw(collapse_method id_method)], |
46
|
|
|
|
|
|
|
required => 1, |
47
|
|
|
|
|
|
|
); |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
has compact => ( |
50
|
|
|
|
|
|
|
isa => "Bool", |
51
|
|
|
|
|
|
|
is => "rw", |
52
|
|
|
|
|
|
|
default => 1, |
53
|
|
|
|
|
|
|
); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
has '+weaken' => ( |
56
|
|
|
|
|
|
|
default => 0, |
57
|
|
|
|
|
|
|
); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
has '_buffer' => ( |
60
|
|
|
|
|
|
|
isa => "KiokuDB::Collapser::Buffer", |
61
|
|
|
|
|
|
|
is => "ro", |
62
|
|
|
|
|
|
|
clearer => "_clear_buffer", |
63
|
|
|
|
|
|
|
writer => "_set_buffer", |
64
|
|
|
|
|
|
|
); |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub collapse { |
67
|
1568
|
|
|
1568
|
0
|
8679
|
my ( $self, %args ) = @_; |
68
|
|
|
|
|
|
|
|
69
|
1568
|
|
|
|
|
3842
|
my $objects = delete $args{objects}; |
70
|
|
|
|
|
|
|
|
71
|
1568
|
|
|
|
|
3275
|
my $r; |
72
|
|
|
|
|
|
|
|
73
|
1568
|
100
|
|
|
|
4803
|
if ( $args{shallow} ) { |
74
|
529
|
|
|
|
|
3340
|
$args{only} = set(@$objects); |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
1568
|
|
|
|
|
58090
|
my $buf = KiokuDB::Collapser::Buffer->new( |
78
|
|
|
|
|
|
|
live_objects => $self->live_objects, |
79
|
|
|
|
|
|
|
options => \%args, |
80
|
|
|
|
|
|
|
); |
81
|
|
|
|
|
|
|
|
82
|
1568
|
|
|
1568
|
|
13659
|
my $g = Scope::Guard->new(sub { $self->_clear_buffer }); |
|
1568
|
|
|
|
|
70558
|
|
83
|
1568
|
|
|
|
|
71178
|
$self->_set_buffer($buf); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# recurse through the object, accumilating entries |
86
|
1568
|
|
|
|
|
13153
|
$self->visit(@$objects); |
87
|
|
|
|
|
|
|
|
88
|
1546
|
|
|
|
|
15725
|
my @ids = $buf->merged_objects_to_ids(@$objects); |
89
|
|
|
|
|
|
|
|
90
|
1546
|
|
|
|
|
47254
|
$buf->first_class->insert(@ids); |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# compact UUID space by merging simple non shared structures into a single |
93
|
|
|
|
|
|
|
# deep entry |
94
|
1546
|
100
|
|
|
|
39814
|
$buf->compact_entries if $self->compact; |
95
|
|
|
|
|
|
|
|
96
|
1546
|
|
|
|
|
9836
|
return ( $buf, @ids ); |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub may_compact { |
100
|
68
|
|
|
68
|
0
|
130
|
my ( $self, $ref_or_id ) = @_; |
101
|
|
|
|
|
|
|
|
102
|
68
|
50
|
|
|
|
1803
|
my $id = ref($ref_or_id) ? $ref_or_id->id : $ref_or_id; |
103
|
|
|
|
|
|
|
|
104
|
68
|
|
|
|
|
1588
|
not $self->_buffer->first_class->includes($id); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub make_entry { |
108
|
9591
|
|
|
9591
|
0
|
31674
|
my ( $self, %args ) = @_; |
109
|
|
|
|
|
|
|
|
110
|
9591
|
|
50
|
|
|
39237
|
my $meta = delete $args{meta} || {}; |
111
|
|
|
|
|
|
|
|
112
|
9591
|
|
|
|
|
15224
|
my $object = $args{object}; |
113
|
|
|
|
|
|
|
|
114
|
9591
|
100
|
|
|
|
17300
|
if ( my $id = $args{id} ) { |
115
|
9487
|
|
|
|
|
257071
|
my $l = $self->live_objects; |
116
|
|
|
|
|
|
|
|
117
|
9487
|
|
|
|
|
30259
|
my $prev = $l->object_to_entry($object); |
118
|
|
|
|
|
|
|
|
119
|
9487
|
100
|
100
|
|
|
32592
|
if ( !$prev and $l->id_in_storage($id) ) { |
120
|
|
|
|
|
|
|
# FIXME Backend->store( insert => [ ... ], update => [ ... ] ) |
121
|
|
|
|
|
|
|
# this happens when keep_entries is false |
122
|
137
|
|
|
|
|
562
|
$prev = KiokuDB::Entry->new( root => $l->id_in_root_set($id) ); # force the operation to be an update |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
9487
|
100
|
|
|
|
250531
|
my $entry = KiokuDB::Entry->new( |
126
|
|
|
|
|
|
|
( $prev ? ( prev => $prev ) : () ), |
127
|
|
|
|
|
|
|
%args, |
128
|
|
|
|
|
|
|
); |
129
|
|
|
|
|
|
|
|
130
|
9487
|
|
|
|
|
268244
|
$self->_buffer->insert_entry( $id => $entry, $object, %$meta ); |
131
|
|
|
|
|
|
|
|
132
|
9487
|
|
|
|
|
31645
|
return $entry; |
133
|
|
|
|
|
|
|
} else { |
134
|
|
|
|
|
|
|
# intrinsic |
135
|
104
|
|
|
|
|
2953
|
my $entry = KiokuDB::Entry->new(%args); |
136
|
|
|
|
|
|
|
|
137
|
104
|
|
|
|
|
3075
|
$self->_buffer->insert_intrinsic( $object => $entry, %$meta ); |
138
|
|
|
|
|
|
|
|
139
|
104
|
|
|
|
|
883
|
return $entry; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub make_skip_entry { |
144
|
167
|
|
|
167
|
0
|
585
|
my ( $self, %args ) = @_; |
145
|
|
|
|
|
|
|
|
146
|
167
|
|
|
|
|
302
|
my $object = $args{object}; |
147
|
|
|
|
|
|
|
|
148
|
167
|
|
66
|
|
|
4318
|
my $prev = $args{prev} || $self->live_objects->object_to_entry($object); |
149
|
|
|
|
|
|
|
|
150
|
167
|
|
|
|
|
376
|
my $id = $args{id}; |
151
|
|
|
|
|
|
|
|
152
|
167
|
50
|
|
|
|
420
|
unless ( $id ) { |
153
|
0
|
0
|
|
|
|
0
|
croak "skip entries must have an ID" unless $prev; |
154
|
0
|
|
|
|
|
0
|
$id = $prev->id; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
167
|
|
|
|
|
494
|
return undef; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub make_ref { |
161
|
12994
|
|
|
12994
|
0
|
19156
|
my ( $self, $id, $value ) = @_; |
162
|
|
|
|
|
|
|
|
163
|
12994
|
|
|
|
|
26646
|
my $weak = isweak($_[2]); |
164
|
|
|
|
|
|
|
|
165
|
12994
|
100
|
|
|
|
62542
|
$self->_buffer->first_class->insert($id) if $weak; |
166
|
|
|
|
|
|
|
|
167
|
12994
|
100
|
|
|
|
345519
|
return KiokuDB::Reference->new( |
168
|
|
|
|
|
|
|
id => $id, |
169
|
|
|
|
|
|
|
$weak ? ( is_weak => 1 ) : () |
170
|
|
|
|
|
|
|
); |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub visit_seen { |
174
|
2462
|
|
|
2462
|
1
|
13850
|
my ( $self, $seen, $prev ) = @_; |
175
|
|
|
|
|
|
|
|
176
|
2462
|
|
|
|
|
58879
|
my $b = $self->_buffer; |
177
|
|
|
|
|
|
|
|
178
|
2462
|
50
|
100
|
|
|
6265
|
if ( my $entry = $b->intrinsic_entry($seen) ) { |
|
|
100
|
|
|
|
|
|
179
|
0
|
|
|
|
|
0
|
return $entry->clone; |
180
|
|
|
|
|
|
|
} elsif ( my $id = $self->_buffer->object_to_id($seen) || $self->live_objects->object_to_id($seen) ) { |
181
|
2461
|
100
|
|
|
|
7084
|
$self->_buffer->first_class->insert($id) unless blessed($seen); |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# return a uuid ref |
184
|
2461
|
|
|
|
|
5359
|
return $self->make_ref( $id => $_[1] ); |
185
|
|
|
|
|
|
|
} else { |
186
|
1
|
|
|
|
|
7
|
KiokuDB::Error::UnknownObjects->throw( objects => [ $seen ] ); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub visit_ref_fallback { |
191
|
8658
|
|
|
8658
|
0
|
10552
|
my ( $self, $ref ) = @_; |
192
|
|
|
|
|
|
|
|
193
|
8658
|
|
|
|
|
207164
|
my $o = $self->_buffer->options; |
194
|
|
|
|
|
|
|
|
195
|
8658
|
50
|
66
|
|
|
36337
|
if ( my $entry = $o->{only_in_storage} && $self->live_objects->object_to_entry($ref) ) { |
196
|
0
|
|
|
|
|
0
|
return $self->make_ref( $entry->id => $_[1] ); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
8658
|
100
|
33
|
|
|
20030
|
if ( my $id = $self->_ref_id($ref) ) { |
|
|
50
|
|
|
|
|
|
200
|
6286
|
50
|
66
|
|
|
154929
|
if ( !$self->compact and my $only = $o->{only} ) { |
201
|
0
|
0
|
|
|
|
0
|
unless ( $only->contains($ref) ) { |
202
|
0
|
|
|
|
|
0
|
return $self->make_ref( $id => $_[1] ); |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
6286
|
|
|
|
|
15111
|
my $collapsed = $self->visit_ref_data($_[1]); |
207
|
|
|
|
|
|
|
|
208
|
6286
|
100
|
100
|
|
|
20272
|
if ( ref($collapsed) eq 'KiokuDB::Reference' and $collapsed->id eq $id ) { |
209
|
33
|
|
|
|
|
887
|
return $collapsed; # tied |
210
|
|
|
|
|
|
|
} else { |
211
|
6253
|
|
|
|
|
6459
|
push @{ $self->_buffer->simple_entries }, $id; |
|
6253
|
|
|
|
|
169370
|
|
212
|
|
|
|
|
|
|
|
213
|
6253
|
|
|
|
|
15722
|
$self->make_entry( |
214
|
|
|
|
|
|
|
id => $id, |
215
|
|
|
|
|
|
|
object => $ref, |
216
|
|
|
|
|
|
|
data => $collapsed, |
217
|
|
|
|
|
|
|
); |
218
|
|
|
|
|
|
|
|
219
|
6253
|
|
|
|
|
14818
|
return $self->make_ref( $id => $_[1] ); |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
} elsif ( $self->compact and not isweak($_[1]) ) { |
222
|
|
|
|
|
|
|
# for now we assume this data just won't be shared, instead of |
223
|
|
|
|
|
|
|
# compacting it later. |
224
|
2372
|
|
|
|
|
8889
|
return $self->SUPER::visit_ref($_[1]); |
225
|
|
|
|
|
|
|
} else { |
226
|
0
|
|
|
|
|
0
|
KiokuDB::Error::UnknownObjects->throw( objects => [ $ref ] ); |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub visit_ref_data { |
231
|
6342
|
|
|
6342
|
0
|
7817
|
my ( $self, $ref ) = @_; |
232
|
6342
|
|
|
|
|
22846
|
$self->SUPER::visit_ref($_[1]); |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub _ref_id { |
236
|
8695
|
|
|
8695
|
|
11859
|
my ( $self, $ref ) = @_; |
237
|
|
|
|
|
|
|
|
238
|
8695
|
|
|
|
|
211112
|
my $l = $self->live_objects; |
239
|
|
|
|
|
|
|
|
240
|
8695
|
100
|
|
|
|
27059
|
if ( my $id = $l->object_to_id($ref) ) { |
241
|
223
|
|
|
|
|
624
|
return $id; |
242
|
|
|
|
|
|
|
} else { |
243
|
8472
|
|
|
|
|
197539
|
my $b = $self->_buffer; |
244
|
|
|
|
|
|
|
|
245
|
8472
|
100
|
|
|
|
214114
|
if ( $b->options->{only_known} ) { |
246
|
2372
|
50
|
|
|
|
55496
|
if ( $self->compact ) { |
247
|
|
|
|
|
|
|
# if we're compacting this is not an error, we just compact in place |
248
|
|
|
|
|
|
|
# and we generate an error if we encounter this data again in visit_seen |
249
|
2372
|
|
|
|
|
56614
|
return; |
250
|
|
|
|
|
|
|
} else { |
251
|
0
|
|
|
|
|
0
|
KiokuDB::Error::UnknownObjects->throw( objects => [ $ref ] ); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
} else { |
254
|
6100
|
|
|
|
|
15326
|
my $id = $self->generate_uuid; |
255
|
6100
|
|
|
|
|
14761
|
$b->insert( $id => $ref ); |
256
|
6100
|
|
|
|
|
16431
|
return $id; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# avoid retying, we want to get back Reference or Entry objects |
262
|
37
|
|
|
37
|
0
|
310
|
sub visit_tied_hash { shift->visit_tied(@_) } |
263
|
0
|
|
|
0
|
0
|
0
|
sub visit_tied_array { shift->visit_tied(@_) } |
264
|
0
|
|
|
0
|
0
|
0
|
sub visit_tied_scalar { shift->visit_tied(@_) } |
265
|
0
|
|
|
0
|
0
|
0
|
sub visit_tied_glob { shift->visit_tied(@_) } |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub visit_tied { |
268
|
37
|
|
|
37
|
1
|
84
|
my ( $self, $tied, $ref ) = @_; |
269
|
|
|
|
|
|
|
|
270
|
37
|
|
|
|
|
108
|
my $tie = $self->visit($tied); |
271
|
|
|
|
|
|
|
|
272
|
37
|
50
|
|
|
|
331
|
if ( my $id = $self->_ref_id($ref) ) { |
273
|
37
|
50
|
33
|
|
|
953
|
if ( !$self->compact and my $only = $self->_buffer->options->{only} ) { |
274
|
0
|
0
|
|
|
|
0
|
unless ( $only->contains($ref) ) { |
275
|
0
|
|
|
|
|
0
|
return $self->make_ref( $id => $_[1] ); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
37
|
|
|
|
|
76
|
push @{ $self->_buffer->simple_entries }, $id; |
|
37
|
|
|
|
|
964
|
|
280
|
|
|
|
|
|
|
|
281
|
37
|
|
|
|
|
269
|
$self->make_entry( |
282
|
|
|
|
|
|
|
id => $id, |
283
|
|
|
|
|
|
|
object => $ref, |
284
|
|
|
|
|
|
|
data => $tie, |
285
|
|
|
|
|
|
|
tied => substr(reftype($ref), 0, 1), |
286
|
|
|
|
|
|
|
); |
287
|
|
|
|
|
|
|
|
288
|
37
|
|
|
|
|
145
|
return $self->make_ref( $id => $_[2] ); |
289
|
|
|
|
|
|
|
} else { |
290
|
0
|
|
|
|
|
0
|
return $self->make_entry( |
291
|
|
|
|
|
|
|
object => $ref, |
292
|
|
|
|
|
|
|
data => $tie, |
293
|
|
|
|
|
|
|
tied => substr(reftype($ref), 0, 1), |
294
|
|
|
|
|
|
|
); |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
4371
|
|
|
4371
|
1
|
60527
|
sub visit_object { shift->visit_with_typemap(@_) } |
299
|
8738
|
|
|
8738
|
1
|
96775
|
sub visit_ref { shift->visit_with_typemap(@_) } |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub visit_with_typemap { |
302
|
13109
|
|
|
13109
|
0
|
14075
|
my ( $self, $ref ) = @_; |
303
|
|
|
|
|
|
|
|
304
|
13109
|
|
|
|
|
40176
|
my $collapse = $self->collapse_method(ref $ref); |
305
|
|
|
|
|
|
|
|
306
|
13093
|
|
|
|
|
41599
|
shift->$collapse(@_); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
sub collapse_first_class { |
310
|
4253
|
|
|
4253
|
0
|
6498
|
my ( $self, $collapse, $object, @entry_args ) = @_; |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# Data::Visitor stuff for circular refs |
313
|
4253
|
|
|
|
|
14303
|
$self->_register_mapping( $object, $object ); |
314
|
|
|
|
|
|
|
|
315
|
4253
|
|
|
|
|
137758
|
my ( $l, $b ) = ( $self->live_objects, $self->_buffer ); |
316
|
|
|
|
|
|
|
|
317
|
4253
|
|
|
|
|
14881
|
my $id = $l->object_to_id($object); |
318
|
4253
|
|
|
|
|
12453
|
my $in_storage = $l->id_in_storage($id); |
319
|
|
|
|
|
|
|
|
320
|
4253
|
|
|
|
|
116955
|
my $o = $b->options; |
321
|
|
|
|
|
|
|
|
322
|
4253
|
100
|
100
|
|
|
13443
|
if ( $o->{only_in_storage} && $in_storage ) { |
323
|
3
|
|
|
|
|
10
|
return $self->make_ref( $id => $_[2] ); |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
4250
|
100
|
|
|
|
46739
|
if ( my $only = $o->{only} ) { |
327
|
1679
|
100
|
|
|
|
9165
|
unless ( $only->contains($object) ) { |
328
|
878
|
100
|
|
|
|
4350
|
if ( $in_storage ) { |
329
|
876
|
|
|
|
|
2454
|
return $self->make_ref( $id => $_[2] ); |
330
|
|
|
|
|
|
|
} else { |
331
|
2
|
|
|
|
|
18
|
KiokuDB::Error::UnknownObjects->throw( objects => [ $object ] ); |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
3372
|
100
|
|
|
|
10989
|
unless ( $id ) { |
337
|
2184
|
100
|
|
|
|
6050
|
if ( $o->{only_known} ) { |
338
|
3
|
|
|
|
|
18
|
KiokuDB::Error::UnknownObjects->throw( objects => [ $object ] ); |
339
|
|
|
|
|
|
|
} else { |
340
|
2181
|
|
|
|
|
8966
|
my $id_method = $self->id_method(ref $object); |
341
|
|
|
|
|
|
|
|
342
|
2181
|
|
|
|
|
8667
|
$id = $self->$id_method($object); |
343
|
|
|
|
|
|
|
|
344
|
2181
|
100
|
|
|
|
8371
|
if ( defined( my $conflict = $l->id_to_object($id) ) ) { |
345
|
66
|
|
|
|
|
310
|
return $self->id_conflict( $id, $_[2], $conflict ); |
346
|
|
|
|
|
|
|
} else { |
347
|
2115
|
|
|
|
|
6457
|
$b->insert( $id => $object ); |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
3303
|
|
|
|
|
12945
|
my @args = ( |
353
|
|
|
|
|
|
|
object => $object, |
354
|
|
|
|
|
|
|
id => $id, |
355
|
|
|
|
|
|
|
class => ref($object), |
356
|
|
|
|
|
|
|
@entry_args, |
357
|
|
|
|
|
|
|
); |
358
|
|
|
|
|
|
|
|
359
|
3303
|
|
|
|
|
13166
|
$self->$collapse(@args); |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# we pass $_[1], an alias, so that isweak works |
362
|
3298
|
|
|
|
|
10695
|
return $self->make_ref( $id => $_[2] ); |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub id_conflict { |
366
|
66
|
|
|
66
|
0
|
127
|
my ( $self, $id, $object, $other ) = @_; |
367
|
|
|
|
|
|
|
|
368
|
66
|
|
|
|
|
242
|
$self->make_skip_entry( id => $id, object => $object ); |
369
|
|
|
|
|
|
|
|
370
|
66
|
|
|
|
|
1686
|
$self->_buffer->insert( $id => $object ); |
371
|
|
|
|
|
|
|
|
372
|
66
|
|
|
|
|
304
|
return $self->make_ref( $id => $_[2] ); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub collapse_intrinsic { |
377
|
104
|
|
|
104
|
0
|
182
|
my ( $self, $collapse, $object, @entry_args ) = @_; |
378
|
|
|
|
|
|
|
|
379
|
104
|
|
|
|
|
201
|
my $class = ref $object; |
380
|
|
|
|
|
|
|
|
381
|
104
|
|
|
|
|
301
|
my @args = ( |
382
|
|
|
|
|
|
|
object => $object, |
383
|
|
|
|
|
|
|
class => $class, |
384
|
|
|
|
|
|
|
@entry_args, |
385
|
|
|
|
|
|
|
); |
386
|
|
|
|
|
|
|
|
387
|
104
|
|
|
|
|
291
|
return $self->$collapse(@args); |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# we don't reblass in collapse_naive |
391
|
|
|
|
|
|
|
sub retain_magic { |
392
|
8676
|
|
|
8676
|
1
|
226690
|
my ( $self, $proto, $clone ) = @_; |
393
|
8676
|
|
|
|
|
27192
|
return $clone; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
__PACKAGE__ |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
__END__ |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=pod |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=head1 NAME |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
KiokuDB::Collapser - Collapse object hierarchies to entry |
407
|
|
|
|
|
|
|
data |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=head1 SYNOPSIS |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# mostly internal |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=head1 DESCRIPTION |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
The collapser simplifies real objects into L<KiokuDB::Entry> objects to pass to |
416
|
|
|
|
|
|
|
the backend. |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Non object data is collapsed by walking it with L<Data::Visitor> (which |
419
|
|
|
|
|
|
|
L<KiokuDB::Collapser> inherits from). |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
Object collapsing is detailed in L</"COLLAPSING STRATEGIES">. |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
The object's data will be copied into the L<KiokuDB::Entry> with references to |
424
|
|
|
|
|
|
|
other data structures translated into L<KiokuDB::Reference> objects. |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
Reference addresses are mapped to unique identifiers, which are generated as |
427
|
|
|
|
|
|
|
necessary. |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=head2 Compacting |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
If C<compact> is disabled then every reference is symbolic, and every data |
432
|
|
|
|
|
|
|
structure has an entry. |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
If compacting is enabled (the default) the minimum number of entry objects |
435
|
|
|
|
|
|
|
required for consistency is created. |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
Every blessed, shared or tied data structure requires an entry object, as does |
438
|
|
|
|
|
|
|
every target of a weak reference. "Simple" structures, such as plain |
439
|
|
|
|
|
|
|
hashes/arrays will be left inline as data intrinsic to the object it was found in. |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Compacting is usually desirable, but sometimes isn't (for instance with an RDF |
442
|
|
|
|
|
|
|
like store). |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=head1 COLLAPSING STRATEGIES |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
Collapsing strategies are chosen based on the type of the object being |
447
|
|
|
|
|
|
|
collapsed, using L<KiokuDB::TypeMap::Resolver>. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
The resolver consults the typemap (L<KiokuDB::TypeMap>), and caches the results |
450
|
|
|
|
|
|
|
as keyed by C<ref $object>. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
The typemap contains normal entries (keyed by C<ref $object eq $class>) or isa |
453
|
|
|
|
|
|
|
entries (filtered by C<< $object->isa($class) >>). The rationale is that a typemap |
454
|
|
|
|
|
|
|
entry for a superclass might not support all subclasses as well. |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Any strategy may be collapsed as a first class object, or intrinsicly, inside |
457
|
|
|
|
|
|
|
its parent (in which case it isn't assigned a UUID). This is determined based |
458
|
|
|
|
|
|
|
on the C<intrinsic> attribute to the entry. For instance, if L<Path::Class> |
459
|
|
|
|
|
|
|
related objects should be collapsed as if they are values, the following |
460
|
|
|
|
|
|
|
typemap entry can be used: |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
isa_entries => { |
463
|
|
|
|
|
|
|
'Path::Class::Entity' => KiokuDB::TypeMap::Entry::Callback->new( |
464
|
|
|
|
|
|
|
intrinsic => 1, |
465
|
|
|
|
|
|
|
collapse => "stringify", |
466
|
|
|
|
|
|
|
expand => "new", |
467
|
|
|
|
|
|
|
), |
468
|
|
|
|
|
|
|
}, |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
If no typemap entry exists, L<KiokuDB::TypeMap::Entry::MOP> is used by default. |
471
|
|
|
|
|
|
|
See L<KiokuDB::TypeMap::Resolver> for more details. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
These are the strategies in brief: |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=head2 MOP |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
When the object has a L<Class::MOP> registered metaclass (any L<Moose> object, |
478
|
|
|
|
|
|
|
but not only), the MOP is used to walk the object's attributes and construct |
479
|
|
|
|
|
|
|
the simplified version without breaking encapsulation. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
See L<KiokuDB::TypeMap::Entry::MOP>. |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=head2 Naive |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
This collapsing strategy simply walks the object's data using L<Data::Visitor>. |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
This allows collapsing of L<Class::Accessor> based objects, for instance, but |
488
|
|
|
|
|
|
|
should be used with care. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
See L<KiokuDB::TypeMap::Entry::Naive> |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=head2 Callback |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
This collapsing strategy allows callbacks to be used to map the types. |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
It is more limited than the other strategies, but very convenient for simple |
497
|
|
|
|
|
|
|
values. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
See L<KiokuDB::TypeMap::Entry::Callback> for more details. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=head2 Passthrough |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
This delegates collapsing to the backend serialization. This is convenient for |
504
|
|
|
|
|
|
|
when a backend uses e.g. L<Storable> to serialize entries, and the object in |
505
|
|
|
|
|
|
|
question already has a C<STORABLE_freeze> and C<STORABLE_thaw> method. |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=cut |
508
|
|
|
|
|
|
|
|