root/rita/tools/generate-migration-from-postgres-dump.pl

Revision 853, 5.6 KB (checked in by gcc, 5 months ago)

Fix index generation. Improve output formatting.

Line 
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6open FILE, "< $ARGV[0]" or die "Usage: $0 <file>\nFailed: $!";
7
8my %table_pk;
9my %table_fk;
10my %table_unique;
11
12while (my $line = <FILE>)
13{
14  next unless $line =~ /ALTER TABLE ONLY (\S+)/;
15  my $table = $1;
16 
17  my $constraint = <FILE>; # read next line
18
19  if ($constraint =~ /^ {4}ADD CONSTRAINT (\S+) (.*);$/)
20  {
21    my $name = $1;
22    my $body = $2;
23   
24    if ($body =~ /^UNIQUE \((.*)\)$/)
25    {
26      my @unique_fields = split m{, }, $1;
27      $table_unique{$table}{$name} = \@unique_fields;
28    }
29    elsif ($body =~ /PRIMARY KEY \((.*)\)$/)
30    {
31      my @pk_fields = split m{, }, $1;
32      $table_pk{$table} and die "duplicate pk for table $table";
33      $table_pk{$table} = { map {$_ => 1} @pk_fields };
34    }
35    elsif ($body =~ /FOREIGN KEY \(([^)]+)\) REFERENCES (.+)\(([^)]+)\)/)
36    {
37      # ON UPDATE RESTRICT ON DELETE RESTRICT;
38      $table_fk{$table}{$name} = [$1,$2,$3];
39    }
40    else
41    {
42      warn "Ignored unknown constraint on $table: $body";
43    }
44  }
45  else
46  {
47    warn "Ignored unknown ALTER TABLE on $table: $constraint";
48  }
49}
50
51close FILE;
52open FILE, "< $ARGV[0]" or die;     
53
54sub create_index ($$$)
55{
56  my ($index_name, $table, $fields) = @_;
57  return "addIndex(uniqueIndex(\"$index_name\", \"$table\", ".
58    join(",", map {"\"$_\""} @$fields) .
59    "));\n";
60}
61
62my ($table, $pk, $fk, $unique, $first_column);
63
64while (<FILE>)
65{ 
66  if (/CREATE TABLE (.*) \(/)
67  {
68    $table = $1;
69    print "createTable(table(\"$table\",\n";
70    $pk = $table_pk{$table};
71    $fk = $table_fk{$table};
72    $unique = $table_unique{$table};
73    $first_column = 1;
74  }
75  elsif (/CREATE UNIQUE INDEX (\S+) ON (\S+) .* \((.*)\);/)
76  {
77    my $name = $1;
78    my $table = $2;
79    my @unique_fields = split m{, }, $3;
80    print create_index($name, $table, \@unique_fields);
81  }
82  elsif (m{ # extended regular expression, whitespace and comments ignored
83    ^ # start of line
84    \s{4} # four space indent
85    (\S+) # one word, column name, group 1
86    \s # space
87    ( # start of group 2 (the entire type)
88      [^ ,(]+ # a word without a comma (not "SMALLINT,") or an open parenthesis
89      | # or
90      ( # start of group 3 (the entire type if it contains parentheses)
91        ([^(]+) # anything that doesn't contain parentheses (type name), group 4
92        \( # followed by an open para
93        (\d+) # followed by a number, group 5 (length or precision)
94        ( # optionally followed by, group 6
95          , # a comma
96          (\d+) # and another number, group 7 (scale)
97        )? # group 6 is optional
98        \) # end parenthesis
99      ) # end group 3 (the entire type if it contains parentheses)
100      | # or
101      timestamp \s without \s time \s zone # exactly "timestamp without time zone"
102    ) # end of group 2
103    (\s DEFAULT \s # group 8, optional DEFAULT clause
104      (\S+) # group 9, default value
105    )? # end of optional group 8
106    (\s NOT \s NULL)? # group 10, optional "NOT NULL" option
107    (,)? # optional comma at end of line (not present for last column), group 11
108    $ # end of line/newline
109  }x)
110  {
111    # start a column, include the quoted name
112    $_ = "\tcolumn(\"$1\", ";
113   
114    # convert type
115    if ($4 and lc($4) eq "character varying")
116    {
117      $_ .= "VARCHAR";
118    }
119    elsif ($4 and lc($4) eq "text")
120    {
121      $_ .= "CLOB";
122    }
123    elsif ($4 and lc($4) eq "character")
124    {
125      $_ .= "CHAR";
126    }
127    elsif (lc($2) eq "timestamp without time zone")
128    {
129      $_ .= "TIMESTAMP";
130    }
131    elsif ($4)
132    {
133      $_ .= uc($4); # wild guess
134    }
135    else
136    {
137      $_ .= uc($2); # wild guess
138    }
139   
140    # length/precision
141    if ($4 and ($4 eq "decimal" or $4 eq "numeric")) # only types that have precision?
142    {
143      $_ .= ", precision($5)";
144    }
145    elsif ($5)
146    {
147      $_ .= ", length($5)";
148    }
149   
150    # scale, optional
151    if ($7)
152    {
153      $_ .= ", scale($7)";
154    }
155   
156    # default option
157    if ($8)
158    {
159      $_ .= ", defaultValue($9)";
160    }
161   
162    # notnull option
163    if ($10)
164    {
165      $_ .= ", notnull()";
166    }
167   
168    if ($pk and $pk->{$1})
169    {
170      $_ .= ", primarykey()";
171    }
172   
173    # end of line and column definition
174    $_ .= ")";
175   
176    if ($first_column)
177    {
178      $first_column = 0;
179    }
180    else
181    {
182      print ",\n";
183    }
184   
185    print $_;
186  }
187  elsif (/^ {4}CONSTRAINT /)
188  {
189    # migrate4j doesn't support this, ignore it
190  }
191  elsif (/^ {4}ADD CONSTRAINT /)
192  {
193    # already parsed and processed
194  }
195  elsif (/^ {4}/) # looks like a column
196  {
197    warn "Ignored line: $_";
198  }
199  elsif (/^\);$/)
200  {
201    print "\n\t));\n";
202
203    if ($unique)
204    {
205      foreach my $index_name (keys %$unique)
206      {
207        my $fields = $unique->{$index_name};
208        print create_index($index_name, $table, $fields);
209      }
210    }
211
212    if ($fk)
213    {
214      foreach my $fk_name (keys %$fk)
215      {
216        my $fk_info = $fk->{$fk_name};
217        my ($parent_field, $child_table, $child_field) = @$fk_info;
218        my @parent_fields = split ', *', $parent_field;
219        my @child_fields  = split ', *', $child_field;
220       
221        if (@parent_fields > 1 or @child_fields > 1)
222        {
223          print "addForeignKey(new ForeignKey(\"$fk_name\",".
224            "\n\t\"$table\", ".
225            "\n\tnew String[]{".join(',', map {"\"$_\""} @parent_fields)."}, ".
226            "\n\t\"$child_table\", ".
227            "\n\tnew String[]{".join(',', map {"\"$_\""} @child_fields)."}".
228            "));\n";
229        }
230        else
231        {
232          print "addForeignKey(new ForeignKey(\"$fk_name\", ".
233            "\n\t\"$table\", \"$parent_field\"," .
234            "\n\t\"$child_table\", \"$child_field\"));\n";
235        }
236      }
237    }
238   
239    print "\n";
240  }
241}
242
243exit 0;
Note: See TracBrowser for help on using the browser.