minimise max_n size
[tridge/junkcode.git] / solid_convert.pl
1 #!/usr/bin/perl -w
2 # convert a solid dump to a postgres dump
3 # tridge@samba.org
4
5 use strict;
6 use Getopt::Long;
7 use Data::Dumper;
8
9 my ($opt_output) = "";
10 my ($opt_help) = 0;
11
12
13 # Maps into postgres types
14 my(%TypeMap) = 
15     (
16      "varchar(254)" => "character varying",
17      "integer" => "integer",
18      "date" => "date",
19      "decimal(16,2)" => "real",
20      "timestamp" => "timestamp"
21      );
22
23 #####################################################################
24 # read a file into a string
25 sub FileLoad($)
26 {
27     my($filename) = shift;
28     local(*INPUTFILE);
29     open(INPUTFILE, $filename) || die "can't open $filename";    
30     my($saved_delim) = $/;
31     undef $/;
32     my($data) = <INPUTFILE>;
33     close(INPUTFILE);
34     $/ = $saved_delim;
35     return $data;
36 }
37
38 ########################################################################
39 # remove double quotes from a string
40 sub NoQuotes($)
41 {
42         my $n = shift;
43         if ($n =~ /^\"(\S+)\"$/) {
44                 return $1;
45         }
46         return $n;
47 }
48
49 ########################################################################
50 # change a table name of form "DAVID"."FOO" into foo
51 sub CvtTable($)
52 {
53         my $n = shift;
54         if ($n =~ /^\"(\w+)\"\.\"(\w+)\"$/) {
55                 return lc $2;
56         }
57         return lc NoQuotes($n);
58 }
59
60
61 ########################################################################
62 # fix up a data field on load
63 sub FixData($)
64 {
65         my $v = shift;
66         $v =~ s/[\r]/\\r/sg;
67         $v =~ s/[\n]//sg;
68         $v =~ s/[\t]/\\t/sg;
69         # fix some bad date fields
70         if ($v =~ /1(19\d\d-\d\d-0)/) {
71                 print "Fixed bad date $v as 1900-01-01\n";
72                 $v = "1900-01-01";
73         }
74         if ($v eq "NULL") {
75                 $v = "\\N";
76         }
77         if ($v eq "") {
78                 $v = "\\N";
79         }
80         return $v;
81 }
82
83
84
85 ########################################################################
86 # process a 'LOAD DATA' via COPY FROM
87 sub LoadData()
88 {
89         my $term = ",";
90         my $table = "";
91         my $fname = "";
92         my $cwd = `pwd`;
93         my $fields = "";
94
95         chomp($cwd);
96
97         while (<INPUT>) {
98                 my $line = $_;
99                 if ($line =~ /^\)$/) {
100                         last;
101                 }
102                 if ($line =~ /^INFILE '(.*)'/) {
103                         $fname = $1;
104                         next;
105                 }
106                 if ($line =~ /^INTO TABLE (\S+)/) {
107                         $table = CvtTable($1);
108                         next;
109                 }
110                 if ($line =~ /^FIELDS TERMINATED BY '(.*)'$/) {
111                         $term = $1;
112                         next;
113                 }
114                 if ($line =~ /^\t(\S+) /) {
115                         if ($fields) {
116                                 $fields .= ", " . lc NoQuotes($1);
117                         } else {
118                                 $fields .= lc NoQuotes($1);
119                         }
120                 }
121         }
122         print "Load data into $table from $fname\n";
123
124         my $data = FileLoad($fname);
125
126         my @lines = split /[^\r]$/ms, $data;
127
128         my $count = $#lines + 1;
129
130         $| = 1;
131
132         print OUTPUT "COPY $table FROM stdin;\n";
133
134         for (my $i=0; $i <= $#lines; $i++) {
135                 my $v;
136                 my $l = $lines[$i];
137                 if (! ($l =~ /\'/)) { next; }
138                 while ($l) {
139                         if ($l =~ /^'(.*?)',(.*)$/ms) {
140                                 $l = $2;
141                                 $v = FixData($1);
142                                 print OUTPUT "$v\t";
143                         } elsif ($l =~ /^(.*?),(.*)$/ms) {
144                                 $l = $2;
145                                 $v = FixData($1);
146                                 print OUTPUT "$v\t";
147                         } elsif ($l =~ /\'(.*)/) {
148                                 $v = FixData($1);
149                                 print OUTPUT "$v\n";
150                                 last;
151                         } elsif ($l =~ /^NUL$/) {
152                                 $v = FixData("NULL");
153                                 print OUTPUT "$v\n";
154                                 last;
155                         }
156                 }
157                 if ($i % 10 eq 0) {
158                         print "$i/$count\r";
159                 }
160         }
161         print OUTPUT "\\.\n\n";
162
163         print "$count/$count\n";
164 }
165
166
167 ########################################################################
168 # process a 'CREATE INDEX'
169 sub CreateIndex($)
170 {
171         my ($line) = shift;
172
173         if ($line =~ /(\S+) ON (\S+) \((\S+)\)/) {
174                 my $index = CvtTable($1);
175                 my $table = CvtTable($2);
176                 my @fields = split(',', $3);
177
178                 print "Create index $index\n";
179                 print OUTPUT "CREATE INDEX $index ON $table (";
180                 for (my $i=0; $i <= $#fields; $i++) {
181                         my $f = lc NoQuotes($fields[$i]);
182                         if ($i != 0) {
183                                 print OUTPUT ",";
184                         }
185                         print OUTPUT "$f";
186                 }
187                 print OUTPUT ");\n\n";
188         }
189         
190 }
191
192 ########################################################################
193 # process a 'CREATE VIEW'
194 sub CreateView($)
195 {
196         my ($line) = shift;
197
198         if ($line =~ /(\w+) AS SELECT (.*);$/) {
199                 my $view = lc $1;
200                 my $query = lc $2;
201
202                 print "Create view $view\n";
203                 print OUTPUT "CREATE VIEW $view AS SELECT $query;\n\n";
204         }
205         
206 }
207
208 ########################################################################
209 # process a 'CREATE TABLE'
210 sub CreateTable($)
211 {
212         my $table_name = $1;
213         my(@fields);
214         my $unique = "";
215
216         $table_name = CvtTable($table_name);
217
218         print "Creating table $table_name\n";
219
220         my $nfields = 0;
221
222         while (<INPUT>) {
223                 my($not_null) = 0;
224                 my($name) = "";
225                 my($type) = "";
226
227                 chomp $_;
228
229                 if ($_ =~ /^\);/) { last; }
230                 if ($_ =~ /^$/) { last; }
231
232                 if ($_ =~ /UNIQUE (.*)$/) { 
233                         $unique = lc $1;
234                         $unique =~ s/\"//sg;
235                         next;
236                 }
237
238                 my($line) = $_;
239                 
240                 if ($line =~ /(.*?),$/) {
241                         $line = $1;
242                 }
243
244                 if ($line =~ / NOT NULL/) {
245                         $not_null = 1;
246                 }
247
248                 if ($line =~ /(\S+) (\S+)/) {
249                         $name = lc NoQuotes($1);
250                         $type = lc $2;
251                         if ($type =~ /(.*)\);$/) {
252                                 $type = $1;
253                         }
254                 }
255
256                 if (! $TypeMap{$type}) {
257                         die "Unknown type $type for field $name in table $table_name\n";
258                 }
259                 
260                 $type = $TypeMap{$type};
261
262                 $fields[$nfields]->{'type'} = $type;
263                 $fields[$nfields]->{'name'} = $name;
264                 $nfields++;
265         }
266
267         print OUTPUT "CREATE TABLE $table_name (\n";
268         for (my $i=0; $i < $nfields; $i++) {
269                 if ($i gt 0) {
270                         print OUTPUT ",\n";
271                 }
272                 print OUTPUT "\t$fields[$i]->{'name'} $fields[$i]->{'type'}";
273         }
274
275         if ($unique) {
276                 print OUTPUT ",\n\tUNIQUE $unique";
277         }
278
279         print OUTPUT "\n);\n\n";
280 }
281
282
283 ########################################################################
284 # process one file
285 sub process_file($)
286 {
287         my($filename) = shift;
288         
289         print "Processing $filename\n";
290
291         open(INPUT,"<$filename") || die "Can't open $filename";
292
293         while (<INPUT>) {
294                 if ($_ =~ /^CREATE TABLE (\S+) \(/) {
295                         CreateTable($1);
296                         next;
297                 }
298                 if ($_ =~ /^CREATE INDEX (.*)/) {
299                         CreateIndex($1);
300                         next;
301                 }
302                 if ($_ =~ /^CREATE VIEW (.*)/) {
303                         CreateView($1);
304                         next;
305                 }
306                 if ($_ =~ /^LOAD DATA/) {
307                         LoadData();
308                         next;
309                 }
310         };
311
312         close(INPUT);
313 }
314
315 #########################################
316 # display help text
317 sub ShowHelp()
318 {
319     print "
320            solid to postgres converter
321            Copyright tridge\@samba.org 2002
322
323            Usage: solid_convert.pl [options] <files>
324
325            Options:
326              --help                this help page
327              --output FILE         file to output to
328            \n";
329     exit(0);
330 }
331
332
333 ##############
334 # main program
335
336 GetOptions (
337             'help|h|?' => \$opt_help, 
338             'output=s' => \$opt_output,
339             );
340
341 if ($opt_help) {
342     ShowHelp();
343     exit(0);
344 }
345
346 die "ERROR: You must specify an output file with --output" unless ($opt_output);
347
348 open(OUTPUT,">$opt_output") || die "ERROR: Failed to open $opt_output";
349
350 for (my $i=0; $i <= $#ARGV; $i++) {
351         process_file($ARGV[$i]);
352 }