| 1 | # Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd. |
|---|
| 2 | # This program is distributed under the terms of the |
|---|
| 3 | # GNU General Public License, version 2. |
|---|
| 4 | # |
|---|
| 5 | # $Id$ |
|---|
| 6 | |
|---|
| 7 | package MT::ObjectDriver::DDL::mysql; |
|---|
| 8 | |
|---|
| 9 | use strict; |
|---|
| 10 | use warnings; |
|---|
| 11 | use base qw( MT::ObjectDriver::DDL ); |
|---|
| 12 | |
|---|
| 13 | sub can_add_column { 1 } |
|---|
| 14 | sub can_drop_column { 1 } |
|---|
| 15 | sub can_alter_column { 1 } |
|---|
| 16 | |
|---|
| 17 | sub index_defs { |
|---|
| 18 | my $ddl = shift; |
|---|
| 19 | my ($class) = @_; |
|---|
| 20 | my $driver = $class->driver; |
|---|
| 21 | my $dbh = $driver->r_handle; |
|---|
| 22 | my $field_prefix = $class->datasource; |
|---|
| 23 | my $table_name = $class->table_name; |
|---|
| 24 | local $dbh->{RaiseError} = 0; |
|---|
| 25 | my $sth = $dbh->prepare('SHOW INDEX FROM ' . $table_name) |
|---|
| 26 | or return undef; |
|---|
| 27 | $sth->execute or return undef; |
|---|
| 28 | |
|---|
| 29 | my $bags = {}; |
|---|
| 30 | my $unique = {}; |
|---|
| 31 | while (my $row = $sth->fetchrow_hashref) { |
|---|
| 32 | my $key = $row->{'Key_name'}; |
|---|
| 33 | next unless $key =~ m/^(mt_)?\Q$field_prefix\E_/; |
|---|
| 34 | $key = 'mt_' . $key unless $key =~ m/^mt_/; |
|---|
| 35 | |
|---|
| 36 | my $type = $row->{'Index_type'}; |
|---|
| 37 | |
|---|
| 38 | # ignore fulltext or other unrecognized indexes for now |
|---|
| 39 | next unless $type eq 'BTREE'; |
|---|
| 40 | |
|---|
| 41 | my $seq = $row->{'Seq_in_index'}; |
|---|
| 42 | my $col = $row->{'Column_name'}; |
|---|
| 43 | my $non_unique = $row->{'Non_unique'}; |
|---|
| 44 | my $null = $row->{'Null'}; |
|---|
| 45 | $key =~ s/^mt_\Q$field_prefix\E_//; |
|---|
| 46 | $col =~ s/^\Q$field_prefix\E_//; |
|---|
| 47 | $unique->{$key} = 1 unless $non_unique; |
|---|
| 48 | my $idx_bag = $bags->{$key} ||= []; |
|---|
| 49 | $idx_bag->[$seq - 1] = $col; |
|---|
| 50 | } |
|---|
| 51 | $sth->finish; |
|---|
| 52 | if (!%$bags) { |
|---|
| 53 | return undef; |
|---|
| 54 | } |
|---|
| 55 | |
|---|
| 56 | my $defs = {}; |
|---|
| 57 | foreach my $key (keys %$bags) { |
|---|
| 58 | my $cols = $bags->{$key}; |
|---|
| 59 | if ($unique->{$key}) { |
|---|
| 60 | $defs->{$key} = { columns => $cols, unique => 1 }; |
|---|
| 61 | } |
|---|
| 62 | else { |
|---|
| 63 | if ((@$cols == 1) && ($key eq $cols->[0])) { |
|---|
| 64 | $defs->{$key} = 1; |
|---|
| 65 | } else { |
|---|
| 66 | $defs->{$key} = { columns => $cols }; |
|---|
| 67 | } |
|---|
| 68 | } |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | return $defs; |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | sub column_defs { |
|---|
| 75 | my $ddl = shift; |
|---|
| 76 | my ($class) = @_; |
|---|
| 77 | |
|---|
| 78 | my $driver = $class->driver; |
|---|
| 79 | my $dbh = $driver->r_handle; |
|---|
| 80 | my $field_prefix = $class->datasource; |
|---|
| 81 | my $table_name = $class->table_name; |
|---|
| 82 | |
|---|
| 83 | # Disable RaiseError if set, since the table we're about to describe |
|---|
| 84 | # may not actually exist (in which case, the return value is undef, |
|---|
| 85 | # signalling an nonexistent table to the caller). |
|---|
| 86 | local $dbh->{RaiseError} = 0; |
|---|
| 87 | my $sth = $dbh->prepare('describe ' . $table_name) or return undef; |
|---|
| 88 | $sth->execute or return undef; |
|---|
| 89 | my $defs = {}; |
|---|
| 90 | while (my $row = $sth->fetchrow_hashref) { |
|---|
| 91 | my $colname = lc $row->{Field}; |
|---|
| 92 | next if $colname !~ m/^\Q$field_prefix\E_/i; |
|---|
| 93 | $colname =~ s/^\Q$field_prefix\E_//i; |
|---|
| 94 | my $coltype = $row->{Type}; |
|---|
| 95 | my ($size) = ($coltype =~ m/(?:var)?char\((\d+)\)/i ? $1 : undef); |
|---|
| 96 | $coltype = $ddl->db2type($coltype); |
|---|
| 97 | $defs->{$colname}{type} = $coltype; |
|---|
| 98 | $defs->{$colname}{auto} = ($row->{Extra} =~ m/auto_increment/i) ? 1 : 0; |
|---|
| 99 | if (($coltype eq 'string') && $size) { |
|---|
| 100 | $defs->{$colname}{size} = $size; |
|---|
| 101 | } |
|---|
| 102 | if ( !$row->{Null} || $row->{Null} eq 'NO' || ($coltype eq 'timestamp') ) { |
|---|
| 103 | $defs->{$colname}{not_null} = 1; |
|---|
| 104 | } else { |
|---|
| 105 | $defs->{$colname}{not_null} = 0; |
|---|
| 106 | } |
|---|
| 107 | } |
|---|
| 108 | $sth->finish; |
|---|
| 109 | if (!%$defs) { |
|---|
| 110 | return undef; |
|---|
| 111 | } |
|---|
| 112 | return $defs; |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | sub db2type { |
|---|
| 116 | my $ddl = shift; |
|---|
| 117 | my ($type) = @_; |
|---|
| 118 | $type = lc $type; |
|---|
| 119 | $type =~ s/\(.+//; |
|---|
| 120 | if ($type eq 'int') { |
|---|
| 121 | return 'integer'; |
|---|
| 122 | } elsif ($type eq 'smallint') { |
|---|
| 123 | return 'smallint'; |
|---|
| 124 | } elsif ($type eq 'bigint') { |
|---|
| 125 | return 'bigint'; |
|---|
| 126 | } elsif ($type eq 'mediumint') { |
|---|
| 127 | return 'integer'; |
|---|
| 128 | } elsif ($type eq 'bigint') { |
|---|
| 129 | return 'integer'; |
|---|
| 130 | } elsif ($type eq 'varchar') { |
|---|
| 131 | return 'string'; |
|---|
| 132 | } elsif ($type eq 'varbinary') { |
|---|
| 133 | return 'string'; |
|---|
| 134 | } elsif ($type eq 'char') { |
|---|
| 135 | return 'string'; |
|---|
| 136 | } elsif ($type eq 'mediumtext') { |
|---|
| 137 | return 'text'; |
|---|
| 138 | } elsif ($type eq 'blob') { |
|---|
| 139 | return 'blob'; |
|---|
| 140 | } elsif ($type eq 'mediumblob') { |
|---|
| 141 | return 'blob'; |
|---|
| 142 | } elsif ($type eq 'tinyint') { |
|---|
| 143 | return 'boolean'; |
|---|
| 144 | } elsif ($type eq 'datetime') { |
|---|
| 145 | return 'datetime'; |
|---|
| 146 | } elsif ($type eq 'timestamp') { |
|---|
| 147 | return 'timestamp'; |
|---|
| 148 | } elsif ($type eq 'text') { |
|---|
| 149 | return 'text'; |
|---|
| 150 | } elsif ($type eq 'float') { |
|---|
| 151 | return 'float'; |
|---|
| 152 | } |
|---|
| 153 | Carp::croak("undefined type: " . $type); |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | sub type2db { |
|---|
| 157 | my $ddl = shift; |
|---|
| 158 | my ($def) = @_; |
|---|
| 159 | return undef if !defined $def; |
|---|
| 160 | my $type = $def->{type}; |
|---|
| 161 | if ($type eq 'string') { |
|---|
| 162 | return 'varchar(' . $def->{size} . ')'; |
|---|
| 163 | } elsif ($type eq 'smallint' ) { |
|---|
| 164 | return 'smallint'; |
|---|
| 165 | } elsif ($type eq 'bigint' ) { |
|---|
| 166 | return 'bigint'; |
|---|
| 167 | } elsif ($type eq 'boolean') { |
|---|
| 168 | return 'tinyint'; |
|---|
| 169 | } elsif ($type eq 'datetime') { |
|---|
| 170 | return 'datetime'; |
|---|
| 171 | } elsif ($type eq 'timestamp') { |
|---|
| 172 | return 'timestamp'; |
|---|
| 173 | } elsif ($type eq 'integer') { |
|---|
| 174 | return 'integer'; |
|---|
| 175 | } elsif ($type eq 'blob') { |
|---|
| 176 | return 'mediumblob'; |
|---|
| 177 | } elsif ($type eq 'text') { |
|---|
| 178 | return 'mediumtext'; |
|---|
| 179 | } elsif ($type eq 'float') { |
|---|
| 180 | return 'float'; |
|---|
| 181 | } |
|---|
| 182 | Carp::croak("undefined type: ". $type); |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | sub column_sql { |
|---|
| 186 | my $ddl = shift; |
|---|
| 187 | my ($class, $name) = @_; |
|---|
| 188 | my $sql = $ddl->SUPER::column_sql($class, $name); |
|---|
| 189 | my $def = $class->column_def($name); |
|---|
| 190 | $sql .= ' auto_increment' if $def->{auto}; |
|---|
| 191 | return $sql; |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| 194 | sub cast_column_sql { |
|---|
| 195 | my $ddl = shift; |
|---|
| 196 | my ($class, $name, $from_def) = @_; |
|---|
| 197 | |
|---|
| 198 | my $def = $class->column_def($name); |
|---|
| 199 | my $field_prefix = $class->datasource; |
|---|
| 200 | my %cast_type = ( |
|---|
| 201 | 'string' => 'char', |
|---|
| 202 | 'smallint' => 'signed', |
|---|
| 203 | 'bigint' => 'signed', |
|---|
| 204 | 'integer' => 'signed', |
|---|
| 205 | 'blob' => 'binary', |
|---|
| 206 | 'text' => 'char', |
|---|
| 207 | 'datetime' => 'datetime', |
|---|
| 208 | 'timestamp' => 'timestamp', |
|---|
| 209 | 'boolean' => 'signed', |
|---|
| 210 | 'float' => 'signed', |
|---|
| 211 | ); |
|---|
| 212 | return "CAST(${field_prefix}_$name AS " . $cast_type{$def->{type}} . ')'; |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | sub drop_table_sql { |
|---|
| 216 | my $ddl = shift; |
|---|
| 217 | my ($class) = @_; |
|---|
| 218 | my $table_name = $class->table_name; |
|---|
| 219 | return "DROP TABLE IF EXISTS $table_name"; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | 1; |
|---|