Massaging of Oracle varchar2 field if larger than 255.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / MySQL.pm
1 package SQL::Translator::Producer::MySQL;
2
3 # -------------------------------------------------------------------
4 # $Id: MySQL.pm,v 1.32 2004-04-01 16:18:55 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.32 $ =~ /(\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             #
136             # Convert a large Oracle varchar to "text"
137             #
138             elsif ( lc $data_type eq 'varchar2' && $size[0] > 255 ) {
139                 $data_type = 'text';
140                 @size      = ();
141             }
142             elsif ( exists $translate{ lc $data_type } ) {
143                 $data_type = $translate{ lc $data_type };
144             }
145
146             @size = () if $data_type =~ /(text|blob)/i;
147
148             $field_def .= " $data_type";
149             
150             if ( lc $data_type eq 'enum' ) {
151                 $field_def .= '(' . $commalist . ')';
152                         } 
153             elsif ( defined $size[0] && $size[0] > 0 ) {
154                 $field_def .= '(' . join( ', ', @size ) . ')';
155             }
156
157             # MySQL qualifiers
158             for my $qual ( qw[ binary unsigned zerofill ] ) {
159                 my $val = $extra{ $qual || uc $qual } or next;
160                 $field_def .= " $qual";
161             }
162
163             # Null?
164             $field_def .= ' NOT NULL' unless $field->is_nullable;
165
166             # Default?  XXX Need better quoting!
167             my $default = $field->default_value;
168             if ( defined $default ) {
169                 if ( uc $default eq 'NULL') {
170                     $field_def .= ' DEFAULT NULL';
171                 } else {
172                     $field_def .= " DEFAULT '$default'";
173                 }
174             }
175
176             # auto_increment?
177             $field_def .= " auto_increment" if $field->is_auto_increment;
178             push @field_defs, $field_def;
179                 }
180
181         #
182         # Indices
183         #
184         my @index_defs;
185         for my $index ( $table->get_indices ) {
186             push @index_defs, join( ' ', 
187                 lc $index->type eq 'normal' ? 'INDEX' : $index->type,
188                 $index->name,
189                 '(' . join( ', ', $index->fields ) . ')'
190             );
191         }
192
193         #
194         # Constraints -- need to handle more than just FK. -ky
195         #
196         my @constraint_defs;
197         for my $c ( $table->get_constraints ) {
198             my @fields = $c->fields or next;
199
200             if ( $c->type eq PRIMARY_KEY ) {
201                 push @constraint_defs,
202                     'PRIMARY KEY (' . join(', ', @fields). ')';
203             }
204             elsif ( $c->type eq UNIQUE ) {
205                 push @constraint_defs,
206                     'UNIQUE (' . join(', ', @fields). ')';
207             }
208             elsif ( $c->type eq FOREIGN_KEY ) {
209                 my $def = join(' ', 
210                     map { $_ || () } 'FOREIGN KEY', $c->name 
211                 );
212
213                 $def .= ' (' . join( ', ', @fields ) . ')';
214
215                 $def .= ' REFERENCES ' . $c->reference_table;
216
217                 if ( my @rfields = $c->reference_fields ) {
218                     $def .= ' (' . join( ', ', @rfields ) . ')';
219                 }
220
221                 if ( $c->match_type ) {
222                     $def .= ' MATCH ' . 
223                         ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
224                 }
225
226                 if ( $c->on_delete ) {
227                     $def .= ' ON DELETE '.join( ' ', $c->on_delete );
228                 }
229
230                 if ( $c->on_update ) {
231                     $def .= ' ON UPDATE '.join( ' ', $c->on_update );
232                 }
233
234                 push @constraint_defs, $def;
235             }
236         }
237
238         $create .= join(",\n", map { "  $_" } 
239             @field_defs, @index_defs, @constraint_defs
240         );
241
242         #
243         # Footer
244         #
245         $create .= "\n)";
246 #        while ( 
247 #            my ( $key, $val ) = each %{ $table->options }
248 #        ) {
249 #            $create .= " $key=$val" 
250 #        }
251         $create .= ";\n\n";
252     }
253
254     return $create;
255 }
256
257 1;
258
259 # -------------------------------------------------------------------
260
261 =pod
262
263 =head1 SEE ALSO
264
265 SQL::Translator, http://www.mysql.com/.
266
267 =head1 AUTHORS
268
269 darren chamberlain E<lt>darren@cpan.orgE<gt>,
270 Ken Y. Clark E<lt>kclark@cpan.orgE<gt>.
271
272 =cut