f131f5ba69d460750bd6539f8f7fb4590c69ae5d
[migration-tools.git] / kmig.d / bin / csv2mysql
1 #!/usr/bin/perl -w
2 use strict;
3 use Switch;
4 use Env qw(
5     HOME MYSQL_HOST MYSQL_TCP_PORT MYSQL_USER MYSQL_DATABASE MYSQL_PW
6     MIGSCHEMA MIGBASEWORKDIR MIGBASEGITDIR MIGGITDIR MIGWORKDIR
7 );
8 use DBI;
9 use Cwd 'abs_path';
10 use FindBin;
11 my $mig_bin = "$FindBin::Bin/";
12 use lib "$FindBin::Bin/";
13 use KMig;
14
15 use Getopt::Long;
16 use Text::CSV::Auto;
17 use Data::Dumper;
18 use File::Basename;
19
20 my $dbh;
21 my $cfg;
22 my $csv_config;
23 my $help;
24
25 sub init {
26     $dbh = KMig::db_connect();
27
28         $cfg = {
29                 schema => 'm_foo',
30                 auto_options => {
31                 }
32         };
33         our %CSV_options = (
34             binary => 1,
35             auto_diag => 1,
36             diag_verbose => 1,
37         );
38         $cfg->{auto_options}->{csv_options} = \%CSV_options;
39
40         GetOptions(
41                 'config=s' => \$csv_config,
42                 'no-legacy-prefix' => \($cfg->{no_legacy_prefix}),
43                 'use-no-headers-file' => \($cfg->{use_no_headers_file}),
44                 'add-x-migrate' => \($cfg->{add_x_migrate}),
45         'outfile=s' => \($cfg->{outfile}),
46                 'schema=s' => \($cfg->{schema}),
47                 'parent=s' => \($cfg->{parent}),
48                 'help|?' => \$help
49         );
50         if ($help || ((@ARGV == 0) && (-t STDIN))) {
51                 die qq^\n\t$0 [--config <CONFIG>] [--add-x-migrate] [--no-legacy-prefix] [--schema <schema>] [--parent <base table>] [--outfile <file to create>] <"clean" file from clean_csv script>\n\n^;
52         }
53         if ($csv_config && ! -e $csv_config) {
54                 die "$csv_config does not exist\n";
55         }
56         if ($csv_config && -e $csv_config) {
57                 eval `cat $csv_config`;
58         }
59         if (! -e $ARGV[0]) {
60                 die "$ARGV[0] does not exist\n";
61         }
62 }
63
64 sub write_sql_sample {
65         my $cfg = shift;
66         my $info = shift;
67         my $fn = $cfg->{outfile} || $cfg->{auto_options}->{file} . '.sql';
68
69         print "\twriting $fn\n";
70         local *SQL;
71         open SQL, ">$fn";
72         print SQL "-- $cfg->{auto_options}->{file}\n/*\n";
73         open IN, $cfg->{auto_options}->{file};
74         foreach (1..5) {
75                 my $line = <IN>;
76                 print SQL $line;
77         }
78         close IN;
79         print SQL "*/\n";
80         return *SQL;
81 }
82
83 sub write_sql_table {
84         my $sql = shift;
85         my $cfg = shift;
86         my $info = shift;
87         my $fn = $cfg->{auto_options}->{file};
88         my @indices = ();
89
90         print "\twriting table definition\n";
91     if ($cfg->{parent}) {
92         if ($cfg->{parent} !~ '^m_') {
93             die "parent table is not a m_ prefixed table; afraid to drop and recreate";
94         }
95         $cfg->{table_name} = $cfg->{parent};
96             print $sql "DROP TABLE IF EXISTS $cfg->{table_name};\n";
97         my $prod_table = $cfg->{parent}; $prod_table =~ s/^m_//;
98         print $sql "CREATE TABLE $cfg->{table_name} LIKE $prod_table;\n";
99         print $sql "ALTER TABLE $cfg->{table_name} ADD COLUMN x_migrate BOOLEAN;\n";
100     } else {
101             $cfg->{table_name} = 'm_' . lc(basename($fn)); $cfg->{table_name} =~ s/[\-\. ]/_/g;
102             print $sql "DROP TABLE IF EXISTS $cfg->{table_name};\n";
103             print $sql "CREATE TABLE $cfg->{table_name} (x_migrate BOOLEAN);\n";
104     }
105     push @indices, 'x_migrate';
106         foreach my $column (@{ $info }) {
107                 my $cn = $column->{'header'};
108                 if ($cn =~ /^x_/) {
109                         push @indices, $cn;
110                 }
111                 my $col_info = Dumper($column);
112                 $col_info =~ s/^\$VAR1 = //;
113                 print $sql "ALTER TABLE $cfg->{table_name} ADD COLUMN ";
114                 print $sql "l_" unless $cfg->{no_legacy_prefix} or $column->{'header'} =~ /^x_/ or $column->{'header'} =~ /^l_/;
115         print $sql "$cn TEXT; ";
116         print $sql " /*\n         $col_info   */\n";
117         }
118         foreach my $cn (@indices) {
119                 print $sql "CREATE INDEX " . $cn . "_idx ON $cfg->{table_name} ($cn);\n";
120         }
121 }
122
123 sub write_sql_loader {
124         my $sql = shift;
125         my $cfg = shift;
126         my $auto = shift;
127         my $info = shift;
128         my $fn = $cfg->{auto_options}->{file} . ($cfg->{use_no_headers_file} ? '.no_headers' : '');
129
130         print "\twriting copy statement\n";
131         print $sql "\nLOAD DATA LOCAL INFILE '$fn' INTO TABLE $cfg->{table_name} ";
132     if ($auto->csv->sep_char eq chr(9) && ! defined $auto->csv->quote_char && ! defined $auto->csv->escape_char) {
133         # true .tsv, don't treat as csv
134     } elsif ($auto->csv->sep_char eq chr(9)) {
135         # probably good enough .tsv, don't treat as csv
136     } else {
137         print $sql "FIELDS";
138         print $sql " TERMINATED BY " . $dbh->quote( $auto->csv->sep_char ) unless $dbh->quote( $auto->csv->sep_char ) eq 'NULL';
139         print $sql " OPTIONALLY ENCLOSED BY " . $dbh->quote( $auto->csv->quote_char ) unless $dbh->quote( $auto->csv->quote_char ) eq 'NULL';
140         print $sql " ESCAPED BY " . $dbh->quote( $auto->csv->escape_char ) unless $dbh->quote( $auto->csv->escape_char ) eq 'NULL';
141         if (!$cfg->{use_no_headers_file}) {
142             print $sql " IGNORE 1 LINES "
143         }
144     }
145     my $idx = 0;
146     print $sql "(";
147     foreach my $column (@{ $info }) {
148         print $sql ($idx++ ? ',' : '');
149         print $sql "l_" unless $cfg->{no_legacy_prefix} or $column->{'header'} =~ /^x_/ or $column->{'header'} =~ /^l_/;
150         print $sql $column->{'header'};
151     }
152     print $sql ");\n";
153 }
154
155 sub main {
156         init();
157         foreach my $fn (@ARGV) {
158                 print "processing $fn\n";
159                 $cfg->{auto_options}->{file} = $fn;
160                 my $auto = Text::CSV::Auto->new($cfg->{auto_options});
161
162                 my $info = $auto->analyze();
163                 my $sql = write_sql_sample($cfg,$info);
164                 write_sql_table($sql,$cfg,$info);
165                 write_sql_loader($sql,$cfg,$auto,$info);
166                 close $sql;
167
168                 print "\tdone.\n";
169         }
170 }
171
172 main();
173