primary key fix
[dbsrgits/SQL-Translator-2.0-ish.git] / lib / SQL / Translator / Parser / DDL / PostgreSQL.pm
1 use MooseX::Declare;
2 role SQL::Translator::Parser::DDL::PostgreSQL {
3     use MooseX::Types::Moose qw(Str);
4     use MooseX::MultiMethods;
5     use Moose::Autobox;
6     use SQL::Translator::Constants qw(:sqlt_types :sqlt_constants);
7     use SQL::Translator::Types qw(Schema);
8     use aliased 'SQL::Translator::Object::Column';
9     use aliased 'SQL::Translator::Object::Constraint';
10     use aliased 'SQL::Translator::Object::ForeignKey';
11     use aliased 'SQL::Translator::Object::Index';
12     use aliased 'SQL::Translator::Object::PrimaryKey';
13     use aliased 'SQL::Translator::Object::Table';
14     use aliased 'SQL::Translator::Object::View';
15
16     multi method parse(Schema $data) { $data }
17
18     multi method parse(Str $data) {
19         my $translator = $self->translator;
20         my $parser = Parse::RecDescent->new($self->grammar);
21     
22         unless (defined $parser) {
23             return $translator->error("Error instantiating Parse::RecDescent ".
24                 "instance: Bad grammar");
25         }
26     
27         my $result = $parser->startrule($data);
28         die "Parse failed.\n" unless defined $result;
29
30         my $schema = $translator->schema;
31         my @tables = 
32          sort { ( $result->{tables}{ $a }{'order'} || 0 ) <=> ( $result->{tables}{ $b }{'order'} || 0 ) }
33          keys %{ $result->{tables} };
34     
35         for my $table_name ( @tables ) {
36             my $tdata = $result->{tables}{ $table_name };
37             my $table = Table->new({ name => $tdata->{table_name}, schema => $schema });
38             $schema->add_table($table);
39     
40             $table->extra({ temporary => 1 }) if $tdata->{'temporary'};
41             $table->comments( [ $tdata->{'comments'}->flatten ]) if $tdata->{comments};
42     
43             my @fields = sort { $tdata->{'fields'}{ $a }{'order'} <=> $tdata->{'fields'}{ $b }{'order'} } keys %{ $tdata->{'fields'} };
44     
45             for my $fname ( @fields ) {
46                 my $fdata = $tdata->{'fields'}{ $fname };
47                 next if $fdata->{'drop'};
48                 my $field = Column->new({
49                     name              => $fdata->{'name'},
50                     data_type         => $fdata->{'data_type'},
51                     sql_data_type     => $self->data_type_mapping->{$fdata->{data_type}} || -999999,
52                     size              => $fdata->{'size'},
53                     default_value     => $fdata->{'default'},
54                     is_auto_increment => $fdata->{'is_auto_increment'}, 
55                     is_nullable       => $fdata->{'is_nullable'},
56                     table             => $table,
57                 });
58                 $field->comments($fdata->{comments});
59
60                 $table->add_column($field);
61                 $field->is_primary_key(1) if $fdata->{is_primary_key};
62     
63                 for my $cdata ( @{ $fdata->{constraints} } ) {
64                     next unless lc $cdata->{type} eq 'foreign_key';
65                     $cdata->{fields} ||= [ $field->name ];
66                     push @{ $tdata->{constraints} }, $cdata;
67                 }
68             }
69     
70             for my $idata ( @{ $tdata->{indices} || [] } ) {
71                 my $index = Index->new({
72                     name    => $idata->{name},
73                     type    => uc $idata->{type},
74                     table   => $table,
75                 });
76                 $index->add_column($table->get_column($_)) for @{$idata->{fields}};
77                 $table->add_index($index);
78             }
79     
80             for my $cdata ( @{ $tdata->{'constraints'} || [] } ) {
81                 my $constraint;
82                 $cdata->{type} =~ s/_/ /g;
83                 if (uc $cdata->{type} eq PRIMARY_KEY) {
84                     $constraint = PrimaryKey->new({ name => $cdata->{name} || '', table => $table });
85                     $table->get_column($_)->is_primary_key(1) for @{$cdata->{fields}};
86                 } elsif (uc $cdata->{type} eq FOREIGN_KEY) {
87                     $constraint = ForeignKey->new({ name => $cdata->{name} || '',
88                                                     table => $table,
89                                                     reference_table => $cdata->{reference_table},
90                                                     reference_columns => $cdata->{reference_fields},
91                                                     on_delete => $cdata->{on_delete} || $cdata->{on_delete_do},
92                                                     on_update => $cdata->{on_update} || $cdata->{on_update_do} });
93                     $table->get_column($_)->is_foreign_key(1) for @{$cdata->{fields}};
94                     $table->get_column($_)->foreign_key_reference($constraint) for @{$cdata->{fields}};
95                 } else {
96                     $constraint = Constraint->new({ name => $cdata->{name} || '', type => uc $cdata->{type}, table => $table });
97                 }
98                 $constraint->add_column($table->get_column($_)) for @{$cdata->{fields}};
99                 $table->add_constraint($constraint);
100             }
101         }
102     
103         for my $vinfo (@{$result->{views}}) {
104           my $sql = $vinfo->{sql};
105           $sql =~ s/\A\s+|\s+\z//g;
106           my $view = View->new({
107             name    => $vinfo->{view_name},
108             sql     => $sql,
109           });
110
111           $schema->add_view($view);
112     
113           $view->extra ( temporary => 1 ) if $vinfo->{is_temporary};
114         }
115     
116         return 1;
117     }
118 }