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