File Coverage

blib/lib/Data/RecordStore/Converter.pm
Criterion Covered Total %
statement 231 232 99.5
branch 36 36 100.0
condition 3 3 100.0
subroutine 46 46 100.0
pod 1 1 100.0
total 317 318 99.6


line stmt bran cond sub pod time code
1             package Data::RecordStore::Converter;
2              
3 1     1   84884 use strict;
  1         3  
  1         31  
4 1     1   6 use warnings;
  1         2  
  1         24  
5              
6 1     1   14 use Data::RecordStore;
  1         3  
  1         28  
7              
8 1     1   6 use File::Copy::Recursive qw( dircopy dirmove );
  1         2  
  1         63  
9 1     1   6 use File::Path qw(remove_tree);
  1         2  
  1         2016  
10              
11              
12             =head2 Data::RecordStore::Converter->convert( $source_dir, $dest_dir )
13              
14             Analyzes the source directory to find version
15             then creates a new version of the store in the
16             destination directory.
17              
18             =cut
19             sub convert {
20 12     12 1 43803 my( $cls, $source_dir, $dest_dir, %args ) = @_;
21              
22 12 100       61 die "Data::RecordStore::Converter->convert must be given a destination directory" unless $dest_dir;
23              
24 11 100       43 die "Data::RecordStore::Converter->convert must be given a source directory" unless $source_dir;
25              
26 10 100       201 die "Data::RecordStore::Converter->convert : cannot find source directory '$source_dir'" unless -d $source_dir;
27            
28 9 100 100     189 if( -d $dest_dir && -e "$dest_dir/VERSION" ) {
29 1         15 die "Data::RecordStore::Converter->convert : Destination directory '$dest_dir' already exists";
30             }
31              
32 8         54 my $source_version = Data::RecordStore->detect_version( $source_dir );
33              
34 8         20 my $rs_pkg;
35 8 100       33 if( ! defined $source_version ) {
36 1         15 die "No store found in $source_dir";
37             }
38 7 100       70 if ( $source_version < 2 ) {
    100          
    100          
    100          
    100          
39 1         5 $rs_pkg = 'RS_1';
40             }
41             elsif ( $source_version < 3 ) {
42 1         4 $rs_pkg = 'RS_2';
43             }
44             elsif ( $source_version < 3.1 ) {
45 1         3 $rs_pkg = 'RS_3';
46             }
47             elsif( $source_version < 4 ) {
48 1         4 $rs_pkg = 'RS_3_1';
49             }
50             elsif( $source_version < 5 ) {
51 1         4 $rs_pkg = 'RS_4';
52             }
53             else { #elsif( $source_version < 6 ) {
54 2         6 $rs_pkg = 'RS_5';
55             }
56              
57 7         59 my $old_rs = $rs_pkg->open( $source_dir );
58              
59 7         35 $args{BASE_PATH} = $dest_dir;
60              
61 7         57 my $new_rs = Data::RecordStore->open_store( %args );
62              
63 7         33 my $entries = $old_rs->entry_count;
64 7         29 for my $id (1..$entries) {
65 314         871 my $val = $old_rs->fetch( $id );
66 314         1258 $new_rs->stow( $val );
67             }
68            
69             } #convert
70              
71             package RS_1;
72              
73             sub open {
74 1     1   4 my( $cls, $dir ) = @_;
75 1         8 my $store_idx = SiloPre3->open( "$dir/STORE_INDEX", "I" );
76 1         379 my $count = $store_idx->entry_count;
77             return bless {
78             count => $count,
79             index_silo => SiloPre3->open( "$dir/OBJ_INDEX", "IL" ),
80 1         8 entry_silos => [ map { SiloPre3->open( "$dir/${_}_OBJSTORE", "A*", $store_idx->get_record($_)->[0] ) } (1..$count) ]
  3         15  
81             }, $cls;
82             }
83             sub entry_count {
84 1     1   15 return shift->{index_silo}->entry_count;
85             }
86             sub fetch {
87 24     24   51 my( $self, $idx ) = @_;
88 24         64 my $lookup = $self->{index_silo}->get_record( $idx );
89 24         55 my( $silo_id, $idx_in_silo ) = @$lookup;
90 24         54 my $silo = $self->{entry_silos}[$silo_id - 1];
91 24         52 my $result = $silo->get_record( $idx_in_silo );
92 24         138 return $result->[0];
93             }
94              
95             package RS_2;
96              
97             sub open {
98 1     1   4 my( $cls, $dir ) = @_;
99              
100 1         28 opendir( my $dh, "$dir/stores/" );
101 1         31 my( @silo_ids ) = map { s/_OBJSTORE//; $_ } grep { /\d+_OBJSTORE/ } readdir( $dh );
  12         25  
  12         21  
  14         45  
102 1         21 closedir $dh;
103            
104             return bless {
105             index_silo => SiloPre3->open( "$dir/OBJ_INDEX", "IL" ),
106 1         13 entry_silos => { map { $_ => SiloPre3->open( "$dir/stores/${_}_OBJSTORE", "LA*", int( exp( $_) ) ) } (@silo_ids)}
  12         55  
107             }, $cls;
108             }
109             sub entry_count {
110 1     1   15 return shift->{index_silo}->entry_count;
111             }
112             sub fetch {
113 49     49   97 my( $self, $idx ) = @_;
114 49         130 my $lookup = $self->{index_silo}->get_record( $idx );
115 49         126 my( $silo_id, $id_in_silo ) = @$lookup;
116 49 100       120 return undef unless $silo_id;
117 37         78 my $silo = $self->{entry_silos}{$silo_id};
118 37         127 my $result = $silo->get_record( $id_in_silo );
119 37         242 return $result->[1];
120             }
121              
122             package RS_3;
123              
124             sub open {
125 1     1   3 my( $cls, $dir ) = @_;
126              
127 1         28 opendir( my $dh, "$dir/silos/" );
128 1         26 my( @silo_ids ) = map { s/_OBJSTORE//; $_ } grep { /\d+_OBJSTORE/ } readdir( $dh );
  12         25  
  12         25  
  14         44  
129 1         12 closedir $dh;
130            
131             return bless {
132             index_silo => Silo3_and_later->open_silo( "IL", "$dir/OBJ_INDEX" ),
133 1         14 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LZ*", "$dir/silos/${_}_OBJSTORE", int( exp( $_) ) ) } (@silo_ids) }
  12         57  
134             }, $cls;
135             }
136             sub entry_count {
137 1     1   13 return shift->{index_silo}->entry_count;
138             }
139             sub fetch {
140 49     49   96 my( $self, $idx ) = @_;
141 49         145 my $lookup = $self->{index_silo}->get_record( $idx );
142 49         119 my( $silo_id, $id_in_silo ) = @$lookup;
143 49 100       503 return undef unless $silo_id;
144 37         84 my $silo = $self->{entry_silos}{$silo_id};
145 37         105 my $result = $silo->get_record( $id_in_silo );
146 37         154 return $result->[1];
147             }
148              
149             package RS_3_1;
150              
151             sub open {
152 1     1   4 my( $cls, $dir ) = @_;
153              
154 1         28 opendir( my $dh, "$dir/silos/" );
155 1         27 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  11         24  
  11         22  
  13         44  
156 1         14 closedir $dh;
157            
158             return bless {
159             index_silo => Silo3_and_later->open_silo( "IL", "$dir/RECORD_INDEX_SILO" ),
160 1         20 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LIA*", "$dir/silos/${_}_RECSTORE", int( exp( $_) ) ) } (@silo_ids) }
  11         48  
161             }, $cls;
162             }
163             sub entry_count {
164 1     1   14 return shift->{index_silo}->entry_count;
165             }
166             sub fetch {
167 45     45   96 my( $self, $idx ) = @_;
168 45         117 my $lookup = $self->{index_silo}->get_record( $idx );
169 45         116 my( $silo_id, $id_in_silo ) = @$lookup;
170 45 100       118 return undef unless $silo_id;
171 34         82 my $silo = $self->{entry_silos}{$silo_id};
172 34         82 my $result = $silo->get_record( $id_in_silo );
173 34 100       111 if( $result->[1] ) {
174 9         2526 my $ret = unpack 'u', $result->[2];
175 9         25 chop $ret; #seems that included an extra byte :(
176 9         200 return $ret;
177             }
178 25         118 return $result->[2];
179             }
180              
181             package RS_4;
182              
183             sub open {
184 1     1   5 my( $cls, $dir ) = @_;
185              
186 1         27 opendir( my $dh, "$dir/silos/" );
187 1         21 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  5         13  
  5         12  
  7         26  
188 1         12 closedir $dh;
189              
190             return bless {
191             index_silo => Silo3_and_later->open_silo( "IL", "$dir/RECORD_INDEX_SILO" ),
192 1         14 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LA*", "$dir/silos/${_}_RECSTORE", 2 ** $_ ) } (@silo_ids) }
  5         22  
193             }, $cls;
194             }
195             sub entry_count {
196 1     1   16 return shift->{index_silo}->entry_count;
197             }
198             sub fetch {
199 49     49   99 my( $self, $idx ) = @_;
200 49         136 my $lookup = $self->{index_silo}->get_record( $idx );
201 49         125 my( $silo_id, $id_in_silo ) = @$lookup;
202 49 100       123 return undef unless $silo_id;
203 37         89 my $silo = $self->{entry_silos}{$silo_id};
204 37         86 my $result = $silo->get_record( $id_in_silo );
205 37         189 return $result->[1];
206             }
207              
208              
209             package RS_5;
210              
211             sub open {
212 2     2   7 my( $cls, $dir ) = @_;
213            
214 2         52 opendir( my $dh, "$dir/silos/" );
215 2         42 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  10         25  
  10         25  
  14         55  
216 2         24 closedir $dh;
217            
218             return bless {
219             index_silo => Silo3_and_later->open_silo( "ILL", "$dir/RECORD_INDEX_SILO" ),
220 2         24 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LA*", "$dir/silos/${_}_RECSTORE", 2 ** $_ ) } (@silo_ids) },
  10         37  
221             }, $cls;
222             }
223             sub entry_count {
224 2     2   20 return shift->{index_silo}->entry_count;
225             }
226             sub fetch {
227 98     98   198 my( $self, $idx ) = @_;
228 98         260 my $lookup = $self->{index_silo}->get_record( $idx );
229 98         245 my( $silo_id, $id_in_silo ) = @$lookup;
230 98 100       239 return undef unless $silo_id;
231 74         180 my $silo = $self->{entry_silos}{$silo_id};
232 74         173 my $result = $silo->get_record( $id_in_silo );
233 74         321 return $result->[1];
234             }
235              
236             package RS_6;
237              
238              
239              
240             package Silo3_and_later;
241              
242 1     1   8 use strict;
  1         2  
  1         30  
243 1     1   5 use warnings;
  1         3  
  1         40  
244 1     1   6 no warnings 'uninitialized';
  1         3  
  1         38  
245 1     1   6 no warnings 'numeric';
  1         2  
  1         43  
246              
247 1     1   6 use Fcntl qw( SEEK_SET );
  1         5  
  1         62  
248 1     1   6 use File::Path qw(make_path remove_tree);
  1         2  
  1         63  
249              
250             use constant {
251 1         156 DIRECTORY => 0,
252             RECORD_SIZE => 1,
253             FILE_SIZE => 2,
254             FILE_MAX_RECORDS => 3,
255             TMPL => 4,
256 1     1   7 };
  1         2  
257              
258             # this really isn't much of a limit anymore, but...
259             # keeping it for now
260             $Silo3_and_later::MAX_SIZE = 2_000_000_000;
261              
262              
263             sub open_silo {
264 43     43   99 my( $class, $template, $directory, $size ) = @_;
265              
266 43         68 my $record_size = $size;
267 43 100       89 if( $record_size == 0 ) {
268 1     1   8 $record_size = do { use bytes; length( pack( $template ) ) };
  1         4  
  1         8  
  5         13  
  5         41  
269             }
270 43         82 my $file_max_records = int( $Silo3_and_later::MAX_SIZE / $record_size );
271 43         66 my $file_max_size = $file_max_records * $record_size;
272              
273 43         126 my $silo = bless [
274             $directory,
275             $record_size,
276             $file_max_size,
277             $file_max_records,
278             $template,
279             ], $class;
280              
281 43         177 return $silo;
282             } #open_silo
283              
284             sub entry_count {
285             # return how many entries this silo has
286 5     5   14 my $self = shift;
287 5         21 my @files = $self->_files;
288 5         12 my $filesize;
289 5         15 for my $file (@files) {
290 5         79 $filesize += -s "$self->[DIRECTORY]/$file";
291             }
292 5         34 return int( $filesize / $self->[RECORD_SIZE] );
293             } #entry_count
294              
295             sub get_record {
296 423     423   748 my( $self, $id ) = @_;
297              
298 423         879 my( $f_idx, $fh, $file, $file_id ) = $self->_fh( $id, 'readonly' );
299              
300 423         2284 sysseek( $fh, $self->[RECORD_SIZE] * $f_idx, SEEK_SET );
301 423         7296 my $srv = sysread $fh, my $data, $self->[RECORD_SIZE];
302 423         3444 close $fh;
303              
304 423         9045 return [unpack( $self->[TMPL], $data )];
305             } #get_record
306              
307             #
308             # Takes an insertion id and returns
309             # an insertion index for in the file
310             # filehandle.
311             # filepath/filename
312             # which number file this is (0 is the first)
313             #
314             sub _fh {
315 423     423   732 my( $self, $id ) = @_;
316 423         792 my @files = $self->_files;
317              
318 423         1354 my $f_idx = int( ($id-1) / $self->[FILE_MAX_RECORDS] );
319              
320 423         705 my $file = $files[$f_idx];
321 423         558 my $fh;
322 423         11898 open( $fh, "<", "$self->[DIRECTORY]/$file" );
323 423         2727 return (($id - ($f_idx*$self->[FILE_MAX_RECORDS])) - 1,$fh,"$self->[DIRECTORY]/$file",$f_idx);
324             } #_fh
325              
326             #
327             # Returns the list of filenames of the 'silos' of this store. They are numbers starting with 0
328             #
329             sub _files {
330 428     428   674 my $self = shift;
331 428         9844 opendir( my $dh, $self->[DIRECTORY] );
332             my( @files ) = (
333 0         0 sort { $a <=> $b }
334 428         6619 grep { /\d+/ }
  1371         5404  
335             readdir( $dh ) );
336 428         4040 closedir $dh;
337 428         2514 return @files;
338             } #_files
339              
340             # ----------- end Silo3_and_later
341              
342             package SiloPre3;
343              
344             #
345             # This package is a helper one that simulates
346             # older silo formats before version 3.1
347             #
348              
349 1     1   536 use strict;
  1         2  
  1         28  
350 1     1   5 use warnings;
  1         2  
  1         24  
351 1     1   6 no warnings 'uninitialized';
  1         17  
  1         44  
352              
353 1     1   6 use Fcntl qw( SEEK_SET );
  1         2  
  1         72  
354              
355             sub open {
356 18     18   42 my( $class, $filename, $template, $size ) = @_;
357 18         27 my $useSize = $size;
358 18 100       38 if( ! $useSize ) {
359 1     1   7 $useSize = do { use bytes; length( pack( $template ) ) };
  1         2  
  1         4  
  3         6  
  3         18  
360             }
361 18         103 bless { TMPL => $template,
362             RECORD_SIZE => $useSize,
363             FILENAME => $filename,
364             }, $class;
365             } #open
366              
367             sub entry_count {
368 3     3   10 my $self = shift;
369 3         42 my $filesize = -s $self->{FILENAME};
370 3         17 return int( $filesize / $self->{RECORD_SIZE} );
371             }
372              
373             sub get_record {
374 137     137   248 my( $self, $id ) = @_;
375              
376 137         258 my $fh = $self->_filehandle;
377 137         734 sysseek $fh, $self->{RECORD_SIZE} * ($id-1), SEEK_SET;
378 137         2367 sysread $fh, my $data, $self->{RECORD_SIZE};
379 137         1069 close $fh;
380 137         2741 return [unpack( $self->{TMPL}, $data )];
381             } #get_record
382              
383             sub _filehandle {
384 137     137   212 my $self = shift;
385 137         3728 CORE::open( my $fh, "<", $self->{FILENAME} );
386 137         537 return $fh;
387             }
388              
389              
390              
391              
392             "You can't work in a steel mill and think small. Giant converters hundreds of feet high. Every night, the sky looked enormous. It was a torrent of flames - of fire. The place that Pittsburgh used to be had such scale - Jack Gilbert";
393              
394             __END__