Null out size if a blob field.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / MySQL.pm
1 package SQL::Translator::Producer::MySQL;
2
3 # -------------------------------------------------------------------
4 # $Id: MySQL.pm,v 1.29 2004-01-25 18:11:42 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.29 $ =~ /(\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 my %translate  = (
57     #
58     # Oracle types
59     #
60     varchar2   => 'varchar',
61     long       => 'text',
62     CLOB       => 'longtext',
63
64     #
65     # Sybase types
66     #
67     int        => 'integer',
68     money      => 'float',
69     real       => 'double',
70     comment    => 'text',
71     bit        => 'tinyint',
72 );
73
74 sub produce {
75     my $translator     = shift;
76     local $DEBUG       = $translator->debug;
77     my $no_comments    = $translator->no_comments;
78     my $add_drop_table = $translator->add_drop_table;
79     my $schema         = $translator->schema;
80
81     debug("PKG: Beginning production\n");
82
83     my $create; 
84     $create .= header_comment unless ($no_comments);
85     # \todo Don't set if MySQL 3.x is set on command line
86     $create .= "SET foreign_key_checks=0;\n\n";
87
88     for my $table ( $schema->get_tables ) {
89         my $table_name = $table->name;
90         debug("PKG: Looking at table '$table_name'\n");
91
92         #
93         # Header.  Should this look like what mysqldump produces?
94         #
95         $create .= "--\n-- Table: $table_name\n--\n" unless $no_comments;
96         $create .= qq[DROP TABLE IF EXISTS $table_name;\n] if $add_drop_table;
97         $create .= "CREATE TABLE $table_name (\n";
98
99         #
100         # Fields
101         #
102         my @field_defs;
103         for my $field ( $table->get_fields ) {
104             my $field_name = $field->name;
105             debug("PKG: Looking at field '$field_name'\n");
106             my $field_def = $field_name;
107
108             # data type and size
109             my $data_type = $field->data_type;
110             my @size      = $field->size;
111             my %extra     = $field->extra;
112             my $list      = $extra{'list'} || [];
113             # \todo deal with embedded quotes
114             my $commalist = join( ', ', map { qq['$_'] } @$list );
115
116             #
117             # Oracle "number" type -- figure best MySQL type
118             #
119             if ( lc $data_type eq 'number' ) {
120                 # not an integer
121                 if ( scalar @size > 1 ) {
122                     $data_type = 'double';
123                 }
124                 elsif ( $size[0] >= 12 ) {
125                     $data_type = 'bigint';
126                 }
127                 elsif ( $size[0] <= 1 ) {
128                     $data_type = 'tinyint';
129                 }
130                 else {
131                     $data_type = 'int';
132                 }
133             }
134             elsif ( exists $translate{ $data_type } ) {
135                 $data_type = $translate{ $data_type };
136             }
137
138             @size = () if $data_type =~ /(text|blob)/i;
139
140             $field_def .= " $data_type";
141             
142             if ( lc $data_type eq 'enum' ) {
143                 $field_def .= '(' . $commalist . ')';
144                         } 
145             elsif ( defined $size[0] && $size[0] > 0 ) {
146                 $field_def .= '(' . join( ', ', @size ) . ')';
147             }
148
149             # MySQL qualifiers
150             for my $qual ( qw[ binary unsigned zerofill ] ) {
151                 my $val = $extra{ $qual || uc $qual } or next;
152                 $field_def .= " $qual";
153             }
154
155             # Null?
156             $field_def .= ' NOT NULL' unless $field->is_nullable;
157
158             # Default?  XXX Need better quoting!
159             my $default = $field->default_value;
160             if ( defined $default ) {
161                 if ( uc $default eq 'NULL') {
162                     $field_def .= ' DEFAULT NULL';
163                 } else {
164                     $field_def .= " DEFAULT '$default'";
165                 }
166             }
167
168             # auto_increment?
169             $field_def .= " auto_increment" if $field->is_auto_increment;
170             push @field_defs, $field_def;
171                 }
172
173         #
174         # Indices
175         #
176         my @index_defs;
177         for my $index ( $table->get_indices ) {
178             push @index_defs, join( ' ', 
179                 lc $index->type eq 'normal' ? 'INDEX' : $index->type,
180                 $index->name,
181                 '(' . join( ', ', $index->fields ) . ')'
182             );
183         }
184
185         #
186         # Constraints -- need to handle more than just FK. -ky
187         #
188         my @constraint_defs;
189         for my $c ( $table->get_constraints ) {
190             my @fields = $c->fields or next;
191
192             if ( $c->type eq PRIMARY_KEY ) {
193                 push @constraint_defs,
194                     'PRIMARY KEY (' . join(', ', @fields). ')';
195             }
196             elsif ( $c->type eq UNIQUE ) {
197                 push @constraint_defs,
198                     'UNIQUE (' . join(', ', @fields). ')';
199             }
200             elsif ( $c->type eq FOREIGN_KEY ) {
201                 my $def = join(' ', 
202                     map { $_ || () } 'FOREIGN KEY', $c->name 
203                 );
204
205                 $def .= ' (' . join( ', ', @fields ) . ')';
206
207                 $def .= ' REFERENCES ' . $c->reference_table;
208
209                 if ( my @rfields = $c->reference_fields ) {
210                     $def .= ' (' . join( ', ', @rfields ) . ')';
211                 }
212
213                 if ( $c->match_type ) {
214                     $def .= ' MATCH ' . 
215                         ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
216                 }
217
218                 if ( $c->on_delete ) {
219                     $def .= ' ON DELETE '.join( ' ', $c->on_delete );
220                 }
221
222                 if ( $c->on_update ) {
223                     $def .= ' ON UPDATE '.join( ' ', $c->on_update );
224                 }
225
226                 push @constraint_defs, $def;
227             }
228         }
229
230         $create .= join(",\n", map { "  $_" } 
231             @field_defs, @index_defs, @constraint_defs
232         );
233
234         #
235         # Footer
236         #
237         $create .= "\n)";
238 #        while ( 
239 #            my ( $key, $val ) = each %{ $table->options }
240 #        ) {
241 #            $create .= " $key=$val" 
242 #        }
243         $create .= ";\n\n";
244     }
245
246     return $create;
247 }
248
249 1;
250
251 # -------------------------------------------------------------------
252
253 =pod
254
255 =head1 SEE ALSO
256
257 SQL::Translator, http://www.mysql.com/.
258
259 =head1 AUTHORS
260
261 darren chamberlain E<lt>darren@cpan.orgE<gt>,
262 Ken Y. Clark E<lt>kclark@cpan.orgE<gt>.
263
264 =cut