File Coverage

blib/lib/DBIx/Class/Schema/Loader/DBI/mysql.pm
Criterion Covered Total %
statement 36 192 18.7
branch 0 80 0.0
condition 0 43 0.0
subroutine 12 33 36.3
pod n/a
total 48 348 13.7


line stmt bran cond sub pod time code
1             package DBIx::Class::Schema::Loader::DBI::mysql;
2              
3 1     1   1014 use strict;
  1         3  
  1         31  
4 1     1   5 use warnings;
  1         1  
  1         26  
5 1     1   5 use base 'DBIx::Class::Schema::Loader::DBI';
  1         2  
  1         95  
6 1     1   6 use mro 'c3';
  1         2  
  1         5  
7 1     1   37 use Carp::Clan qw/^DBIx::Class/;
  1         7  
  1         6  
8 1     1   103 use List::Util qw/any first/;
  1         2  
  1         85  
9 1     1   7 use Try::Tiny;
  1         2  
  1         79  
10 1     1   7 use Scalar::Util 'blessed';
  1         2  
  1         58  
11 1     1   7 use DBIx::Class::Schema::Loader::Utils qw/sigwarn_silencer/;
  1         2  
  1         45  
12 1     1   6 use namespace::clean;
  1         2  
  1         6  
13 1     1   370 use DBIx::Class::Schema::Loader::Table ();
  1         3  
  1         2340  
14              
15             our $VERSION = '0.07051';
16              
17             =head1 NAME
18              
19             DBIx::Class::Schema::Loader::DBI::mysql - DBIx::Class::Schema::Loader::DBI mysql Implementation.
20              
21             =head1 DESCRIPTION
22              
23             See L and L.
24              
25             =cut
26              
27             sub _setup {
28 0     0     my $self = shift;
29              
30 0           $self->schema->storage->sql_maker->quote_char("`");
31 0           $self->schema->storage->sql_maker->name_sep(".");
32              
33 0           $self->next::method(@_);
34              
35 0 0         if (not defined $self->preserve_case) {
36 0           $self->preserve_case(0);
37             }
38              
39 0 0 0       if ($self->db_schema && $self->db_schema->[0] eq '%') {
40             my @schemas = try {
41 0     0     $self->_show_databases;
42             }
43             catch {
44 0     0     croak "no SHOW DATABASES privileges: $_";
45 0           };
46              
47             @schemas = grep {
48 0           my $schema = $_;
  0            
49 0     0     not any { lc($schema) eq lc($_) } $self->_system_schemas
  0            
50             } @schemas;
51              
52 0           $self->db_schema(\@schemas);
53             }
54             }
55              
56             sub _show_databases {
57 0     0     my $self = shift;
58              
59 0           return map $_->[0], @{ $self->dbh->selectall_arrayref('SHOW DATABASES') };
  0            
60             }
61              
62             sub _system_schemas {
63 0     0     my $self = shift;
64              
65 0           return ($self->next::method(@_), 'mysql');
66             }
67              
68             sub _table_fk_info {
69 0     0     my ($self, $table) = @_;
70              
71 0           my $table_def_ref = eval { $self->dbh->selectrow_arrayref("SHOW CREATE TABLE ".$table->sql_name) };
  0            
72 0           my $table_def = $table_def_ref->[1];
73              
74 0 0         return [] if not $table_def;
75              
76 0           my $qt = qr/["`]/;
77 0           my $nqt = qr/[^"`]/;
78              
79 0           my (@reldata) = ($table_def =~
80             /CONSTRAINT ${qt}${nqt}+${qt} FOREIGN KEY \($qt(.*)$qt\) REFERENCES (?:$qt($nqt+)$qt\.)?$qt($nqt+)$qt \($qt(.+)$qt\)\s*(.*)/ig
81             );
82              
83 0           my @rels;
84 0           while (scalar @reldata > 0) {
85 0           my ($cols, $f_schema, $f_table, $f_cols, $rest) = splice @reldata, 0, 5;
86              
87 0           my @cols = map { s/$qt//g; $self->_lc($_) }
  0            
  0            
88             split(/$qt?\s*$qt?,$qt?\s*$qt?/, $cols);
89              
90 0           my @f_cols = map { s/$qt//g; $self->_lc($_) }
  0            
  0            
91             split(/$qt?\s*$qt?,$qt?\s*$qt?/, $f_cols);
92              
93             # Match case of remote schema to that in SHOW DATABASES, if it's there
94             # and we have permissions to run SHOW DATABASES.
95 0 0         if ($f_schema) {
96             my $matched = first {
97 0     0     lc($_) eq lc($f_schema)
98 0     0     } try { $self->_show_databases };
  0            
99              
100 0 0         $f_schema = $matched if $matched;
101             }
102              
103 0           my $remote_table = do {
104             # Get ->tables_list to return tables from the remote schema, in case it is not in the db_schema list.
105 0 0         local $self->{db_schema} = [ $f_schema ] if $f_schema;
106              
107             first {
108 0 0 0 0     lc($_->name) eq lc($f_table)
109             && ((not $f_schema) || lc($_->schema) eq lc($f_schema))
110 0           } $self->_tables_list;
111             };
112              
113             # The table may not be in any database, or it may not have been found by the previous code block for whatever reason.
114 0 0         if (not $remote_table) {
115 0   0       my $remote_schema = $f_schema || $self->db_schema && @{ $self->db_schema } == 1 && $self->db_schema->[0];
116              
117 0 0         $remote_table = DBIx::Class::Schema::Loader::Table->new(
118             loader => $self,
119             name => $f_table,
120             ($remote_schema ? (
121             schema => $remote_schema,
122             ) : ()),
123             );
124             }
125              
126 0           my %attrs;
127              
128 0 0         if ($rest) {
129 0           my @on_clauses = $rest =~ /(ON DELETE|ON UPDATE) (RESTRICT|CASCADE|SET NULL|NO ACTION) ?/ig;
130              
131 0           while (my ($clause, $value) = splice @on_clauses, 0, 2) {
132 0           $clause = lc $clause;
133 0           $clause =~ s/ /_/;
134              
135 0           $value = uc $value;
136              
137 0           $attrs{$clause} = $value;
138             }
139             }
140              
141             # The default behavior is RESTRICT. Specifying RESTRICT explicitly just removes
142             # that ON clause from the SHOW CREATE TABLE output. For this reason, even
143             # though the default for these clauses everywhere else in Schema::Loader is
144             # CASCADE, we change the default here to RESTRICT in order to reproduce the
145             # schema faithfully.
146 0   0       $attrs{on_delete} ||= 'RESTRICT';
147 0   0       $attrs{on_update} ||= 'RESTRICT';
148              
149             # MySQL does not have a DEFERRABLE attribute, but there is a way to defer FKs.
150 0           $attrs{is_deferrable} = 1;
151              
152 0           push(@rels, {
153             local_columns => \@cols,
154             remote_columns => \@f_cols,
155             remote_table => $remote_table,
156             attrs => \%attrs,
157             });
158             }
159              
160 0           return \@rels;
161             }
162              
163             # primary and unique info comes from the same sql statement,
164             # so cache it here for both routines to use
165             sub _mysql_table_get_keys {
166 0     0     my ($self, $table) = @_;
167              
168 0 0         if(!exists($self->{_cache}->{_mysql_keys}->{$table->sql_name})) {
169 0           my %keydata;
170 0           my $sth = $self->dbh->prepare('SHOW INDEX FROM '.$table->sql_name);
171 0           $sth->execute;
172 0           while(my $row = $sth->fetchrow_hashref) {
173 0 0         next if $row->{Non_unique};
174 0           push(@{$keydata{$row->{Key_name}}},
175 0           [ $row->{Seq_in_index}, $self->_lc($row->{Column_name}) ]
176             );
177             }
178 0           foreach my $keyname (keys %keydata) {
179 0           my @ordered_cols = map { $_->[1] } sort { $a->[0] <=> $b->[0] }
  0            
180 0           @{$keydata{$keyname}};
  0            
181 0           $keydata{$keyname} = \@ordered_cols;
182             }
183 0           $self->{_cache}->{_mysql_keys}->{$table->sql_name} = \%keydata;
184             }
185              
186 0           return $self->{_cache}->{_mysql_keys}->{$table->sql_name};
187             }
188              
189             sub _table_pk_info {
190 0     0     my ( $self, $table ) = @_;
191              
192 0           return $self->_mysql_table_get_keys($table)->{PRIMARY};
193             }
194              
195             sub _table_uniq_info {
196 0     0     my ( $self, $table ) = @_;
197              
198 0           my @uniqs;
199 0           my $keydata = $self->_mysql_table_get_keys($table);
200 0           foreach my $keyname (sort keys %$keydata) {
201 0 0         next if $keyname eq 'PRIMARY';
202 0           push(@uniqs, [ $keyname => $keydata->{$keyname} ]);
203             }
204              
205 0           return \@uniqs;
206             }
207              
208             sub _columns_info_for {
209 0     0     my $self = shift;
210 0           my ($table) = @_;
211              
212 0           my $result = $self->next::method(@_);
213              
214 0           while (my ($col, $info) = each %$result) {
215 0 0         if ($info->{data_type} eq 'int') {
    0          
216 0           $info->{data_type} = 'integer';
217             }
218             elsif ($info->{data_type} eq 'double') {
219 0           $info->{data_type} = 'double precision';
220             }
221 0           my $data_type = $info->{data_type};
222              
223 0 0         delete $info->{size} if $data_type !~ /^(?: (?:var)?(?:char(?:acter)?|binary) | bit | year)\z/ix;
224              
225             # information_schema is available in 5.0+
226 0           my ($precision, $scale, $column_type, $default) = eval { $self->dbh->selectrow_array(<<'EOF', {}, $table->name, lc($col)) };
  0            
227             SELECT numeric_precision, numeric_scale, column_type, column_default
228             FROM information_schema.columns
229             WHERE table_schema = schema() AND table_name = ? AND lower(column_name) = ?
230             EOF
231 0           my $has_information_schema = not $@;
232              
233 0 0         $column_type = '' if not defined $column_type;
234              
235 0 0 0       if ($data_type eq 'bit' && (not exists $info->{size})) {
    0 0        
    0 0        
    0          
    0          
236 0 0         $info->{size} = $precision if defined $precision;
237             }
238             elsif ($data_type =~ /^(?:float|double precision|decimal)\z/i) {
239 0 0 0       if (defined $precision && defined $scale) {
240 0 0 0       if ($precision == 10 && $scale == 0) {
241 0           delete $info->{size};
242             }
243             else {
244 0           $info->{size} = [$precision,$scale];
245             }
246             }
247             }
248             elsif ($data_type eq 'year') {
249 0 0 0       if ($column_type =~ /\(2\)/) {
    0          
250 0           $info->{size} = 2;
251             }
252             elsif ($column_type =~ /\(4\)/ || $info->{size} == 4) {
253 0           delete $info->{size};
254             }
255             }
256             elsif ($data_type =~ /^(?:date(?:time)?|timestamp)\z/) {
257 0 0 0       if (not (defined $self->datetime_undef_if_invalid && $self->datetime_undef_if_invalid == 0)) {
258 0           $info->{datetime_undef_if_invalid} = 1;
259             }
260             }
261             elsif ($data_type =~ /^(?:enum|set)\z/ && $has_information_schema
262             && $column_type =~ /^(?:enum|set)\(/) {
263              
264 0           delete $info->{extra}{list};
265              
266 0           while ($column_type =~ /'((?:[^']* (?:''|\\')* [^']*)* [^\\']?)',?/xg) {
267 0           my $el = $1;
268 0           $el =~ s/''/'/g;
269 0           push @{ $info->{extra}{list} }, $el;
  0            
270             }
271             }
272              
273             # Sometimes apparently there's a bug where default_value gets set to ''
274             # for things that don't actually have or support that default (like ints.)
275 0 0 0       if (exists $info->{default_value} && $info->{default_value} eq '') {
276 0 0         if ($has_information_schema) {
277 0 0         if (not defined $default) {
278 0           delete $info->{default_value};
279             }
280             }
281             else { # just check if it's a char/text type, otherwise remove
282 0 0         delete $info->{default_value} unless $data_type =~ /char|text/i;
283             }
284             }
285             }
286              
287 0           return $result;
288             }
289              
290             sub _extra_column_info {
291 1     1   9 no warnings 'uninitialized';
  1         2  
  1         741  
292 0     0     my ($self, $table, $col, $info, $dbi_info) = @_;
293 0           my %extra_info;
294              
295 0 0         if ($dbi_info->{mysql_is_auto_increment}) {
296 0           $extra_info{is_auto_increment} = 1
297             }
298 0 0         if ($dbi_info->{mysql_type_name} =~ /\bunsigned\b/i) {
299 0           $extra_info{extra}{unsigned} = 1;
300             }
301 0 0         if ($dbi_info->{mysql_values}) {
302 0           $extra_info{extra}{list} = $dbi_info->{mysql_values};
303             }
304 0 0 0       if ((not blessed $dbi_info) # isa $sth
      0        
305             && lc($dbi_info->{COLUMN_DEF}) eq 'current_timestamp'
306             && lc($dbi_info->{mysql_type_name}) eq 'timestamp') {
307              
308 0           my $current_timestamp = 'current_timestamp';
309 0           $extra_info{default_value} = \$current_timestamp;
310             }
311              
312 0           return \%extra_info;
313             }
314              
315             sub _dbh_column_info {
316 0     0     my $self = shift;
317              
318 0           local $SIG{__WARN__} = sigwarn_silencer(
319             qr/^column_info: unrecognized column type/
320             );
321              
322 0           $self->next::method(@_);
323             }
324              
325             sub _table_comment {
326 0     0     my ( $self, $table ) = @_;
327 0           my $comment = $self->next::method($table);
328 0 0         if (not $comment) {
329 0     0     ($comment) = try { $self->schema->storage->dbh->selectrow_array(
330             qq{SELECT table_comment
331             FROM information_schema.tables
332             WHERE table_schema = schema()
333             AND table_name = ?
334             }, undef, $table->name);
335 0           };
336             # InnoDB likes to auto-append crap.
337 0 0         if (not $comment) {
    0          
338             # Do nothing.
339             }
340             elsif ($comment =~ /^InnoDB free:/) {
341 0           $comment = undef;
342             }
343             else {
344 0           $comment =~ s/; InnoDB.*//;
345             }
346             }
347 0           return $comment;
348             }
349              
350             sub _column_comment {
351 0     0     my ( $self, $table, $column_number, $column_name ) = @_;
352 0           my $comment = $self->next::method($table, $column_number, $column_name);
353 0 0         if (not $comment) {
354 0     0     ($comment) = try { $self->schema->storage->dbh->selectrow_array(
355             qq{SELECT column_comment
356             FROM information_schema.columns
357             WHERE table_schema = schema()
358             AND table_name = ?
359             AND lower(column_name) = ?
360             }, undef, $table->name, lc($column_name));
361 0           };
362             }
363 0           return $comment;
364             }
365              
366             sub _view_definition {
367 0     0     my ($self, $view) = @_;
368              
369 0           return scalar $self->schema->storage->dbh->selectrow_array(
370             q{SELECT view_definition
371             FROM information_schema.views
372             WHERE table_schema = schema()
373             AND table_name = ?
374             }, undef, $view->name,
375             );
376             }
377              
378             =head1 SEE ALSO
379              
380             L, L,
381             L
382              
383             =head1 AUTHORS
384              
385             See L.
386              
387             =head1 LICENSE
388              
389             This library is free software; you can redistribute it and/or modify it under
390             the same terms as Perl itself.
391              
392             =cut
393              
394             1;
395             # vim:et sw=4 sts=4 tw=0: