File Coverage

blib/lib/SQL/Translator/Parser/xSV.pm
Criterion Covered Total %
statement 67 68 98.5
branch 22 28 78.5
condition 15 25 60.0
subroutine 9 9 100.0
pod 0 1 0.0
total 113 131 86.2


line stmt bran cond sub pod time code
1             package SQL::Translator::Parser::xSV;
2              
3             =head1 NAME
4              
5             SQL::Translator::Parser::xSV - parser for arbitrarily delimited text files
6              
7             =head1 SYNOPSIS
8              
9             use SQL::Translator;
10             use SQL::Translator::Parser::xSV;
11              
12             my $translator = SQL::Translator->new(
13             parser => 'xSV',
14             parser_args => { field_separator => "\t" },
15             );
16              
17             =head1 DESCRIPTION
18              
19             Parses arbitrarily delimited text files. See the
20             Text::RecordParser manpage for arguments on how to parse the file
21             (e.g., C, C). Other arguments
22             include:
23              
24             =head1 OPTIONS
25              
26             =over
27              
28             =item * scan_fields
29              
30             Indicates that the columns should be scanned to determine data types
31             and field sizes. True by default.
32              
33             =item * trim_fields
34              
35             A shortcut to sending filters to Text::RecordParser, will create
36             callbacks that trim leading and trailing spaces from fields and headers.
37             True by default.
38              
39             =back
40              
41             Field names will automatically be normalized by
42             C.
43              
44             =cut
45              
46 1     1   7 use strict;
  1         3  
  1         40  
47 1     1   6 use warnings;
  1         2  
  1         84  
48             our @EXPORT;
49             our $VERSION = '1.66';
50              
51 1     1   6 use Exporter;
  1         3  
  1         51  
52 1     1   593 use Text::ParseWords qw(quotewords);
  1         2109  
  1         80  
53 1     1   745 use Text::RecordParser;
  1         44831  
  1         65  
54 1     1   11 use SQL::Translator::Utils qw(debug normalize_name);
  1         4  
  1         89  
55              
56 1     1   8 use base qw(Exporter);
  1         2  
  1         1367  
57             @EXPORT = qw(parse);
58              
59             #
60             # Passed a SQL::Translator instance and a string containing the data
61             #
62             sub parse {
63 1     1 0 7 my ($tr, $data) = @_;
64 1         14 my $args = $tr->parser_args;
65             my $parser = Text::RecordParser->new(
66             field_separator => $args->{'field_separator'} || ',',
67 1   50     18 record_separator => $args->{'record_separator'} || "\n",
      50        
68             data => $data,
69             header_filter => \&normalize_name,
70             );
71              
72 21   50 21   1112 $parser->field_filter(sub { $_ = shift || ''; s/^\s+|\s+$//g; $_ })
  21         54  
  21         33  
73 1 50 33     239 unless defined $args->{'trim_fields'} && $args->{'trim_fields'} == 0;
74              
75 1         32 my $schema = $tr->schema;
76 1         60 my $table = $schema->add_table(name => 'table1');
77              
78             #
79             # Get the field names from the first row.
80             #
81 1         4 $parser->bind_header;
82 1         41 my @field_names = $parser->field_list;
83              
84 1         12 for (my $i = 0; $i < @field_names; $i++) {
85 7 50       47 my $field = $table->add_field(
86             name => $field_names[$i],
87             data_type => 'char',
88             default_value => '',
89             size => 255,
90             is_nullable => 1,
91             is_auto_increment => undef,
92             ) or die $table->error;
93              
94 7 100       111 if ($i == 0) {
95 1         14 $table->primary_key($field->name);
96 1         12 $field->is_primary_key(1);
97             }
98             }
99              
100             #
101             # If directed, look at every field's values to guess size and type.
102             #
103 1 50 33     7 unless (defined $args->{'scan_fields'}
104             && $args->{'scan_fields'} == 0) {
105 1         4 my %field_info = map { $_, {} } @field_names;
  7         13  
106 1         20 while (my $rec = $parser->fetchrow_hashref) {
107 2         184 for my $field (@field_names) {
108 14 50       49 my $data = defined $rec->{$field} ? $rec->{$field} : '';
109 14         20 my $size = [ length $data ];
110 14         19 my $type;
111              
112 14 100 66     71 if ($data =~ /^-?\d+$/) {
    100 100        
113 2         4 $type = 'integer';
114             } elsif ($data =~ /^-?[,\d]+\.[\d+]?$/
115             || $data =~ /^-?[,\d]+?\.\d+$/
116             || $data =~ /^-?\.\d+$/) {
117 2         14 $type = 'float';
118             my ($w, $d)
119 2 100       7 = map { s/,//g; length $_ || 1 } split(/\./, $data);
  4         8  
  4         17  
120 2         5 $size = [ $w + $d, $d ];
121             } else {
122 10         13 $type = 'char';
123             }
124              
125 14         18 for my $i (0, 1) {
126 28 100       47 next unless defined $size->[$i];
127 16   100     35 my $fsize = $field_info{$field}{'size'}[$i] || 0;
128 16 100       26 if ($size->[$i] > $fsize) {
129 11         16 $field_info{$field}{'size'}[$i] = $size->[$i];
130             }
131             }
132              
133 14         51 $field_info{$field}{$type}++;
134             }
135             }
136              
137 1         61 for my $field (keys %field_info) {
138 7   50     17 my $size = $field_info{$field}{'size'} || [1];
139             my $data_type
140             = $field_info{$field}{'char'} ? 'char'
141             : $field_info{$field}{'float'} ? 'float'
142 7 50       19 : $field_info{$field}{'integer'} ? 'integer'
    100          
    100          
143             : 'char';
144              
145 7 50 66     31 if ($data_type eq 'char' && scalar @$size == 2) {
146 0         0 $size = [ $size->[0] + $size->[1] ];
147             }
148              
149 7         19 my $field = $table->get_field($field);
150 7         114 $field->size($size);
151 7         65 $field->data_type($data_type);
152             }
153             }
154              
155 1         26 return 1;
156             }
157              
158             1;
159              
160             =pod
161              
162             =head1 AUTHORS
163              
164             Darren Chamberlain Edarren@cpan.orgE,
165             Ken Y. Clark Ekclark@cpan.orgE.
166              
167             =head1 SEE ALSO
168              
169             Text::RecordParser, SQL::Translator.
170              
171             =cut