File Coverage

blib/lib/Fsdb/Filter/dbcolmerge.pm
Criterion Covered Total %
statement 18 69 26.0
branch 0 16 0.0
condition n/a
subroutine 6 15 40.0
pod 5 5 100.0
total 29 105 27.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             #
4             # dbcolmerge.pm
5             # Copyright (C) 1991-2008 by John Heidemann
6             # $Id: 3dfd25a93416ff66eb1930699b1eacadcf68d9b3 $
7             #
8             # This program is distributed under terms of the GNU general
9             # public license, version 2. See the file COPYING
10             # in $dblibdir for details.
11             #
12              
13             package Fsdb::Filter::dbcolmerge;
14              
15             =head1 NAME
16              
17             dbcolmerge - merge multiple columns into one
18              
19             =head1 SYNOPSIS
20              
21             dbcolmerge [-C ElementSeparator] [columns...]
22              
23             =head1 DESCRIPTION
24              
25             For each row, merge multiple columns down to a single column,
26             joining elements with ElementSeparator (defaults to a single underscore).
27              
28             =head1 OPTIONS
29              
30             =over 4
31              
32             =item B<-C S> or B<--element-separator S>
33              
34             Specify the separator used to join columns.
35             (Defaults to a single underscore.)
36              
37             =item B<-e E> or B<--empty E>
38              
39             give value E as the value for empty (null) records
40              
41             =back
42              
43             =for comment
44             begin_standard_fsdb_options
45              
46             This module also supports the standard fsdb options:
47              
48             =over 4
49              
50             =item B<-d>
51              
52             Enable debugging output.
53              
54             =item B<-i> or B<--input> InputSource
55              
56             Read from InputSource, typically a file name, or C<-> for standard input,
57             or (if in Perl) a IO::Handle, Fsdb::IO or Fsdb::BoundedQueue objects.
58              
59             =item B<-o> or B<--output> OutputDestination
60              
61             Write to OutputDestination, typically a file name, or C<-> for standard output,
62             or (if in Perl) a IO::Handle, Fsdb::IO or Fsdb::BoundedQueue objects.
63              
64             =item B<--autorun> or B<--noautorun>
65              
66             By default, programs process automatically,
67             but Fsdb::Filter objects in Perl do not run until you invoke
68             the run() method.
69             The C<--(no)autorun> option controls that behavior within Perl.
70              
71             =item B<--help>
72              
73             Show help.
74              
75             =item B<--man>
76              
77             Show full manual.
78              
79             =back
80              
81             =for comment
82             end_standard_fsdb_options
83              
84              
85             =head1 SAMPLE USAGE
86              
87             =head2 Input:
88              
89             #fsdb first last
90             John Heidemann
91             Greg Johnson
92             Root -
93             # this is a simple database
94             # | /home/johnh/BIN/DB/dbcol fullname
95             # | dbcolrename fullname first_last
96             # | /home/johnh/BIN/DB/dbcolsplit -C _ first_last
97             # | /home/johnh/BIN/DB/dbcol first last
98              
99             =head2 Command:
100              
101             cat data.fsdb | dbcolmerge -C _ first last
102              
103             =head2 Output:
104              
105             #fsdb first last first_last
106             John Heidemann John_Heidemann
107             Greg Johnson Greg_Johnson
108             Root - Root_
109             # this is a simple database
110             # | /home/johnh/BIN/DB/dbcol fullname
111             # | dbcolrename fullname first_last
112             # | /home/johnh/BIN/DB/dbcolsplit first_last
113             # | /home/johnh/BIN/DB/dbcol first last
114             # | /home/johnh/BIN/DB/dbcolmerge -C _ first last
115              
116              
117             =head1 SEE ALSO
118              
119             L.
120             L.
121             L.
122             L.
123              
124              
125             =head1 CLASS FUNCTIONS
126              
127             =cut
128              
129             @ISA = qw(Fsdb::Filter);
130             $VERSION = 2.0;
131              
132 1     1   5668 use strict;
  1         3  
  1         35  
133 1     1   4 use Pod::Usage;
  1         2  
  1         127  
134 1     1   6 use Carp;
  1         2  
  1         58  
135              
136 1     1   4 use Fsdb::Filter;
  1         2  
  1         18  
137 1     1   5 use Fsdb::IO::Reader;
  1         1  
  1         22  
138 1     1   3 use Fsdb::IO::Writer;
  1         2  
  1         792  
139              
140              
141             =head2 new
142              
143             $filter = new Fsdb::Filter::dbcolmerge(@arguments);
144              
145             Create a new dbcolmerge object, taking command-line arguments.
146              
147             =cut
148              
149             sub new ($@) {
150 0     0 1   my $class = shift @_;
151 0           my $self = $class->SUPER::new(@_);
152 0           bless $self, $class;
153 0           $self->set_defaults;
154 0           $self->parse_options(@_);
155 0           $self->SUPER::post_new();
156 0           return $self;
157             }
158              
159              
160             =head2 set_defaults
161              
162             $filter->set_defaults();
163              
164             Internal: set up defaults.
165              
166             =cut
167              
168             sub set_defaults ($) {
169 0     0 1   my($self) = @_;
170 0           $self->SUPER::set_defaults();
171 0           $self->{_elem_separator} = '_';
172 0           $self->{_merge_columns} = [];
173             }
174              
175             =head2 parse_options
176              
177             $filter->parse_options(@ARGV);
178              
179             Internal: parse command-line arguments.
180              
181             =cut
182              
183             sub parse_options ($@) {
184 0     0 1   my $self = shift @_;
185              
186 0           my(@argv) = @_;
187             $self->get_options(
188             \@argv,
189 0     0     'help|?' => sub { pod2usage(1); },
190 0     0     'man' => sub { pod2usage(-verbose => 2); },
191             'autorun!' => \$self->{_autorun},
192             'close!' => \$self->{_close},
193             'C|element-separator=s' => \$self->{_elem_separator},
194             'd|debug+' => \$self->{_debug},
195             'e|empty=s' => \$self->{_empty},
196 0     0     'i|input=s' => sub { $self->parse_io_option('input', @_); },
197             'log!' => \$self->{_logprog},
198 0     0     'o|output=s' => sub { $self->parse_io_option('output', @_); },
199 0 0         ) or pod2usage(2);
200 0           push (@{$self->{_merge_columns}}, @argv);
  0            
201             }
202              
203             =head2 setup
204              
205             $filter->setup();
206              
207             Internal: setup, parse headers.
208              
209             =cut
210              
211             sub setup ($) {
212 0     0 1   my($self) = @_;
213              
214             # Sanity check user's input to avoid injection attacks.
215             croak $self->{_prog} . ": bad element separator.\n"
216 0 0         if ($self->{_elem_separator} =~ /\'/);
217              
218             croak $self->{_prog} . ": no columns to merge selected.\n"
219 0 0         if ($#{$self->{_merge_columns}} == -1);
  0            
220              
221 0           $self->finish_io_option('input', -comment_handler => $self->create_pass_comments_sub);
222              
223 0           $self->finish_io_option('output', -clone => $self->{_in}, -outputheader => 'delay');
224 0           $self->{_merged_colname} = join($self->{_elem_separator}, @{$self->{_merge_columns}});
  0            
225             $self->{_out}->col_create($self->{_merged_colname})
226 0 0         or croak $self->{_prog} . ": cannot create column " . $self->{_merged_colname} . " (maybe it already existed?)\n";
227 0           $self->{_merged_coli} = $self->{_out}->col_to_i($self->{_merged_colname});
228              
229             #
230             # Write the code to do the merge, and check stuff on the way.
231             #
232 0           my $code = '';
233 0           my $joiner = '';
234 0           my $empty = $self->{_empty};
235 0 0         croak "bad empty value: $empty\n" if ($empty eq "'");
236 0           $code = '$fref->[' . $self->{_merged_coli} . '] = ';
237 0           foreach (@{$self->{_merge_columns}}) {
  0            
238 0           my $i = $self->{_in}->col_to_i($_);
239 0 0         croak ($self->{_prog} . ": unknown column ``$_''.\n")
240             if (!defined($i));
241 0           my $f = '$fref->[' . $i . ']';
242 0           $code .= "$joiner ($f eq '$empty' ? '' : $f) ";
243 0           $joiner = " . '" . $self->{_elem_separator} . "' . ";
244             };
245 0           $code .= ';';
246 0           $self->{_join_code} = $code;
247             }
248              
249             =head2 run
250              
251             $filter->run();
252              
253             Internal: run over each rows.
254              
255             =cut
256             sub run ($) {
257 0     0 1   my($self) = @_;
258              
259 0           my $read_fastpath_sub = $self->{_in}->fastpath_sub();
260 0           my $write_fastpath_sub = $self->{_out}->fastpath_sub();
261              
262             my($loop) = q'{
263             my $fref;
264             while ($fref = &$read_fastpath_sub()) {
265 0           ' . $self->{_join_code} . q'
266             &$write_fastpath_sub($fref);
267             };
268             }';
269 0 0         print $loop if ($self->{_debug});
270 0           eval $loop;
271 0 0         $@ && croak $self->{_prog} . ": interal eval error: $@.\n";
272             }
273              
274             =head1 AUTHOR and COPYRIGHT
275              
276             Copyright (C) 1991-2008 by John Heidemann
277              
278             This program is distributed under terms of the GNU general
279             public license, version 2. See the file COPYING
280             with the distribution for details.
281              
282             =cut
283              
284             1;