Being case-insensitive on datatype translations.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / MySQL.pm
1 package SQL::Translator::Producer::MySQL;
2
3 # -------------------------------------------------------------------
4 # $Id: MySQL.pm,v 1.30 2004-02-04 21:09:54 kycl4rk Exp $
5 # -------------------------------------------------------------------
6 # Copyright (C) 2003 Ken Y. Clark <kclark@cpan.org>,
7 #                    darren chamberlain <darren@cpan.org>,
8 #                    Chris Mungall <cjm@fruitfly.org>
9 #
10 # This program is free software; you can redistribute it and/or
11 # modify it under the terms of the GNU General Public License as
12 # published by the Free Software Foundation; version 2.
13 #
14 # This program is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22 # 02111-1307  USA
23 # -------------------------------------------------------------------
24
25 =head1 NAME
26
27 SQL::Translator::Producer::MySQL - MySQL-specific producer for SQL::Translator
28
29 =head1 SYNOPSIS
30
31 Use via SQL::Translator:
32
33   use SQL::Translator;
34
35   my $t = SQL::Translator->new( parser => '...', producer => 'MySQL', '...' );
36   $t->translate;
37
38 =head1 DESCRIPTION
39
40 This module will produce text output of the schema suitable for MySQL.
41 There are still some issues to be worked out with syntax differences 
42 between MySQL versions 3 and 4 ("SET foreign_key_checks," character sets
43 for fields, etc.).
44
45 =cut
46
47 use strict;
48 use vars qw[ $VERSION $DEBUG ];
49 $VERSION = sprintf "%d.%02d", q$Revision: 1.30 $ =~ /(\d+)\.(\d+)/;
50 $DEBUG   = 0 unless defined $DEBUG;
51
52 use Data::Dumper;
53 use SQL::Translator::Schema::Constants;
54 use SQL::Translator::Utils qw(debug header_comment);
55
56 #
57 # Use only lowercase for the keys (e.g. "long" and not "LONG")
58 #
59 my %translate  = (
60     #
61     # Oracle types
62     #
63     varchar2   => 'varchar',
64     long       => 'text',
65     clob       => 'longtext',
66
67     #
68     # Sybase types
69     #
70     int        => 'integer',
71     money      => 'float',
72     real       => 'double',
73     comment    => 'text',
74     bit        => 'tinyint',
75 );
76
77 sub produce {
78     my $translator     = shift;
79     local $DEBUG       = $translator->debug;
80     my $no_comments    = $translator->no_comments;
81     my $add_drop_table = $translator->add_drop_table;
82     my $schema         = $translator->schema;
83
84     debug("PKG: Beginning production\n");
85
86     my $create; 
87     $create .= header_comment unless ($no_comments);
88     # \todo Don't set if MySQL 3.x is set on command line
89     $create .= "SET foreign_key_checks=0;\n\n";
90
91     for my $table ( $schema->get_tables ) {
92         my $table_name = $table->name;
93         debug("PKG: Looking at table '$table_name'\n");
94
95         #
96         # Header.  Should this look like what mysqldump produces?
97         #
98         $create .= "--\n-- Table: $table_name\n--\n" unless $no_comments;
99         $create .= qq[DROP TABLE IF EXISTS $table_name;\n] if $add_drop_table;
100         $create .= "CREATE TABLE $table_name (\n";
101
102         #
103         # Fields
104         #
105         my @field_defs;
106         for my $field ( $table->get_fields ) {
107             my $field_name = $field->name;
108             debug("PKG: Looking at field '$field_name'\n");
109             my $field_def = $field_name;
110
111             # data type and size
112             my $data_type = $field->data_type;
113             my @size      = $field->size;
114             my %extra     = $field->extra;
115             my $list      = $extra{'list'} || [];
116             # \todo deal with embedded quotes
117             my $commalist = join( ', ', map { qq['$_'] } @$list );
118
119             #
120             # Oracle "number" type -- figure best MySQL type
121             #
122             if ( lc $data_type eq 'number' ) {
123                 # not an integer
124                 if ( scalar @size > 1 ) {
125                     $data_type = 'double';
126                 }
127                 elsif ( $size[0] >= 12 ) {
128                     $data_type = 'bigint';
129                 }
130                 elsif ( $size[0] <= 1 ) {
131                     $data_type = 'tinyint';
132                 }
133                 else {
134                     $data_type = 'int';
135                 }
136             }
137             elsif ( exists $translate{ lc $data_type } ) {
138                 $data_type = $translate{ lc $data_type };
139             }
140
141             @size = () if $data_type =~ /(text|blob)/i;
142
143             $field_def .= " $data_type";
144             
145             if ( lc $data_type eq 'enum' ) {
146                 $field_def .= '(' . $commalist . ')';
147                         } 
148             elsif ( defined $size[0] && $size[0] > 0 ) {
149                 $field_def .= '(' . join( ', ', @size ) . ')';
150             }
151
152             # MySQL qualifiers
153             for my $qual ( qw[ binary unsigned zerofill ] ) {
154                 my $val = $extra{ $qual || uc $qual } or next;
155                 $field_def .= " $qual";
156             }
157
158             # Null?
159             $field_def .= ' NOT NULL' unless $field->is_nullable;
160
161             # Default?  XXX Need better quoting!
162             my $default = $field->default_value;
163             if ( defined $default ) {
164                 if ( uc $default eq 'NULL') {
165                     $field_def .= ' DEFAULT NULL';
166                 } else {
167                     $field_def .= " DEFAULT '$default'";
168                 }
169             }
170
171             # auto_increment?
172             $field_def .= " auto_increment" if $field->is_auto_increment;
173             push @field_defs, $field_def;
174                 }
175
176         #
177         # Indices
178         #
179         my @index_defs;
180         for my $index ( $table->get_indices ) {
181             push @index_defs, join( ' ', 
182                 lc $index->type eq 'normal' ? 'INDEX' : $index->type,
183                 $index->name,
184                 '(' . join( ', ', $index->fields ) . ')'
185             );
186         }
187
188         #
189         # Constraints -- need to handle more than just FK. -ky
190         #
191         my @constraint_defs;
192         for my $c ( $table->get_constraints ) {
193             my @fields = $c->fields or next;
194
195             if ( $c->type eq PRIMARY_KEY ) {
196                 push @constraint_defs,
197                     'PRIMARY KEY (' . join(', ', @fields). ')';
198             }
199             elsif ( $c->type eq UNIQUE ) {
200                 push @constraint_defs,
201                     'UNIQUE (' . join(', ', @fields). ')';
202             }
203             elsif ( $c->type eq FOREIGN_KEY ) {
204                 my $def = join(' ', 
205                     map { $_ || () } 'FOREIGN KEY', $c->name 
206                 );
207
208                 $def .= ' (' . join( ', ', @fields ) . ')';
209
210                 $def .= ' REFERENCES ' . $c->reference_table;
211
212                 if ( my @rfields = $c->reference_fields ) {
213                     $def .= ' (' . join( ', ', @rfields ) . ')';
214                 }
215
216                 if ( $c->match_type ) {
217                     $def .= ' MATCH ' . 
218                         ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
219                 }
220
221                 if ( $c->on_delete ) {
222                     $def .= ' ON DELETE '.join( ' ', $c->on_delete );
223                 }
224
225                 if ( $c->on_update ) {
226                     $def .= ' ON UPDATE '.join( ' ', $c->on_update );
227                 }
228
229                 push @constraint_defs, $def;
230             }
231         }
232
233         $create .= join(",\n", map { "  $_" } 
234             @field_defs, @index_defs, @constraint_defs
235         );
236
237         #
238         # Footer
239         #
240         $create .= "\n)";
241 #        while ( 
242 #            my ( $key, $val ) = each %{ $table->options }
243 #        ) {
244 #            $create .= " $key=$val" 
245 #        }
246         $create .= ";\n\n";
247     }
248
249     return $create;
250 }
251
252 1;
253
254 # -------------------------------------------------------------------
255
256 =pod
257
258 =head1 SEE ALSO
259
260 SQL::Translator, http://www.mysql.com/.
261
262 =head1 AUTHORS
263
264 darren chamberlain E<lt>darren@cpan.orgE<gt>,
265 Ken Y. Clark E<lt>kclark@cpan.orgE<gt>.
266
267 =cut