minor improvement for MySQL
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / DBI / mysql.pm
1 package DBIx::Class::Schema::Loader::DBI::mysql;
2
3 use strict;
4 use warnings;
5 use base 'DBIx::Class::Schema::Loader::DBI';
6 use Carp::Clan qw/^DBIx::Class/;
7 use Class::C3;
8
9 our $VERSION = '0.06001';
10
11 =head1 NAME
12
13 DBIx::Class::Schema::Loader::DBI::mysql - DBIx::Class::Schema::Loader::DBI mysql Implementation.
14
15 =head1 SYNOPSIS
16
17   package My::Schema;
18   use base qw/DBIx::Class::Schema::Loader/;
19
20   __PACKAGE__->loader_options( debug => 1 );
21
22   1;
23
24 =head1 DESCRIPTION
25
26 See L<DBIx::Class::Schema::Loader::Base>.
27
28 =cut
29
30 sub _tables_list { 
31     my ($self, $opts) = @_;
32
33     return $self->next::method($opts, undef, undef);
34 }
35
36 sub _table_fk_info {
37     my ($self, $table) = @_;
38
39     my $dbh = $self->schema->storage->dbh;
40
41     my $table_def_ref = eval { $dbh->selectrow_arrayref("SHOW CREATE TABLE `$table`") };
42     my $table_def = $table_def_ref->[1];
43
44     return [] if not $table_def;
45
46     my $qt = qr/["`]/;
47
48     my (@reldata) = ($table_def =~
49         /CONSTRAINT $qt.*$qt FOREIGN KEY \($qt(.*)$qt\) REFERENCES $qt(.*)$qt \($qt(.*)$qt\)/ig
50     );
51
52     my @rels;
53     while (scalar @reldata > 0) {
54         my $cols = shift @reldata;
55         my $f_table = shift @reldata;
56         my $f_cols = shift @reldata;
57
58         my @cols   = map { s/(?: \Q$self->{_quoter}\E | $qt )//x; lc $_ }
59             split(/\s*,\s*/, $cols);
60
61         my @f_cols = map { s/(?: \Q$self->{_quoter}\E | $qt )//x; lc $_ }
62             split(/\s*,\s*/, $f_cols);
63
64         push(@rels, {
65             local_columns => \@cols,
66             remote_columns => \@f_cols,
67             remote_table => $f_table
68         });
69     }
70
71     return \@rels;
72 }
73
74 # primary and unique info comes from the same sql statement,
75 #   so cache it here for both routines to use
76 sub _mysql_table_get_keys {
77     my ($self, $table) = @_;
78
79     if(!exists($self->{_cache}->{_mysql_keys}->{$table})) {
80         my %keydata;
81         my $dbh = $self->schema->storage->dbh;
82         my $sth = $dbh->prepare('SHOW INDEX FROM '.$self->_table_as_sql($table));
83         $sth->execute;
84         while(my $row = $sth->fetchrow_hashref) {
85             next if $row->{Non_unique};
86             push(@{$keydata{$row->{Key_name}}},
87                 [ $row->{Seq_in_index}, lc $row->{Column_name} ]
88             );
89         }
90         foreach my $keyname (keys %keydata) {
91             my @ordered_cols = map { $_->[1] } sort { $a->[0] <=> $b->[0] }
92                 @{$keydata{$keyname}};
93             $keydata{$keyname} = \@ordered_cols;
94         }
95         $self->{_cache}->{_mysql_keys}->{$table} = \%keydata;
96     }
97
98     return $self->{_cache}->{_mysql_keys}->{$table};
99 }
100
101 sub _table_pk_info {
102     my ( $self, $table ) = @_;
103
104     return $self->_mysql_table_get_keys($table)->{PRIMARY};
105 }
106
107 sub _table_uniq_info {
108     my ( $self, $table ) = @_;
109
110     my @uniqs;
111     my $keydata = $self->_mysql_table_get_keys($table);
112     foreach my $keyname (keys %$keydata) {
113         next if $keyname eq 'PRIMARY';
114         push(@uniqs, [ $keyname => $keydata->{$keyname} ]);
115     }
116
117     return \@uniqs;
118 }
119
120 sub _columns_info_for {
121     my $self = shift;
122     my ($table) = @_;
123
124     my $result = $self->next::method(@_);
125
126     my $dbh = $self->schema->storage->dbh;
127
128     while (my ($col, $info) = each %$result) {
129         delete $info->{size}
130             unless $info->{data_type} =~ /^(?: (?:var)?(?:char(?:acter)?|binary) | bit | year)\z/ix;
131
132         if ($info->{data_type} eq 'int') {
133             $info->{data_type} = 'integer';
134         }
135         elsif ($info->{data_type} eq 'double') {
136             $info->{data_type} = 'double precision';
137         }
138
139         my ($precision, $scale, $column_type) = eval { $dbh->selectrow_array(<<'EOF', {}, lc $table, lc $col) };
140 SELECT numeric_precision, numeric_scale, column_type
141 FROM information_schema.columns
142 WHERE lower(table_name) = ? AND lower(column_name) = ?
143 EOF
144         $column_type = '' if not defined $column_type;
145
146         if ($info->{data_type} eq 'bit' && (not exists $info->{size})) {
147             $info->{size} = $precision if defined $precision;
148         }
149         elsif ($info->{data_type} =~ /^(?:float|double precision|decimal)\z/) {
150             if (defined $precision && defined $scale) {
151                 if ($precision == 10 && $scale == 0) {
152                     delete $info->{size};
153                 }
154                 else {
155                     $info->{size} = [$precision,$scale];
156                 }
157             }
158         }
159         elsif ($info->{data_type} eq 'year') {
160             if ($column_type =~ /\(2\)/) {
161                 $info->{size} = 2;
162             }
163             elsif ($column_type =~ /\(4\)/ || $info->{size} == 4) {
164                 delete $info->{size};
165             }
166         }
167     }
168
169     return $result;
170 }
171
172 sub _extra_column_info {
173     no warnings 'uninitialized';
174     my ($self, $table, $col, $info, $dbi_info) = @_;
175     my %extra_info;
176
177     if ($dbi_info->{mysql_is_auto_increment}) {
178         $extra_info{is_auto_increment} = 1
179     }
180     if ($dbi_info->{mysql_type_name} =~ /\bunsigned\b/i) {
181         $extra_info{extra}{unsigned} = 1;
182     }
183     if ($dbi_info->{mysql_values}) {
184         $extra_info{extra}{list} = $dbi_info->{mysql_values};
185     }
186     if (   lc($dbi_info->{COLUMN_DEF})      eq 'current_timestamp'
187         && lc($dbi_info->{mysql_type_name}) eq 'timestamp') {
188
189         $extra_info{default_value} = \'CURRENT_TIMESTAMP';
190     }
191
192     return \%extra_info;
193 }
194
195 =head1 SEE ALSO
196
197 L<DBIx::Class::Schema::Loader>, L<DBIx::Class::Schema::Loader::Base>,
198 L<DBIx::Class::Schema::Loader::DBI>
199
200 =head1 AUTHOR
201
202 See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
203
204 =head1 LICENSE
205
206 This library is free software; you can redistribute it and/or modify it under
207 the same terms as Perl itself.
208
209 =cut
210
211 1;
212 # vim:et sw=4 sts=4 tw=0: