take out duplicate docs
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / Oracle.pm
CommitLineData
16dc9970 1package SQL::Translator::Producer::Oracle;
2
44659089 3# -------------------------------------------------------------------
4# Copyright (C) 2002-2009 SQLFairy Authors
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; version 2.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18# 02111-1307 USA
19# -------------------------------------------------------------------
20
ba096dc4 21=head1 NAME
22
23SQL::Translator::Producer::Oracle - Oracle SQL producer
24
25=head1 SYNOPSIS
26
27 use SQL::Translator;
28
29 my $t = SQL::Translator->new( parser => '...', producer => 'Oracle' );
30 print $translator->translate( $file );
31
32=head1 DESCRIPTION
33
34Creates an SQL DDL suitable for Oracle.
35
662258cd 36=head1 producer_args
37
38=over
39
40=item delay_constraints
41
42This option remove the primary key and other key constraints from the
43CREATE TABLE statement and adds ALTER TABLEs at the end with it.
44
38b019a3 45=item quote_field_names
46
47Controls whether quotes are being used around column names in generated DDL.
48
49=item quote_table_names
50
51Controls whether quotes are being used around table, sequence and trigger names in
52generated DDL.
53
662258cd 54=back
55
2c5e6626 56=head1 NOTES
57
58=head2 Autoincremental primary keys
59
60This producer uses sequences and triggers to autoincrement primary key
61columns, if necessary. SQLPlus and DBI expect a slightly different syntax
62of CREATE TRIGGER statement. You might have noticed that this
63producer returns a scalar containing all statements concatenated by
64newlines or an array of single statements depending on the context
65(scalar, array) it has been called in.
66
67SQLPlus expects following trigger syntax:
68
69 CREATE OR REPLACE TRIGGER ai_person_id
70 BEFORE INSERT ON person
71 FOR EACH ROW WHEN (
72 new.id IS NULL OR new.id = 0
73 )
74 BEGIN
75 SELECT sq_person_id.nextval
76 INTO :new.id
77 FROM dual;
78 END;
79 /
80
81Whereas if you want to create the same trigger using L<DBI/do>, you need
82to omit the last slash:
83
84 my $dbh = DBI->connect('dbi:Oracle:mysid', 'scott', 'tiger');
85 $dbh->do("
86 CREATE OR REPLACE TRIGGER ai_person_id
87 BEFORE INSERT ON person
88 FOR EACH ROW WHEN (
89 new.id IS NULL OR new.id = 0
90 )
91 BEGIN
92 SELECT sq_person_id.nextval
93 INTO :new.id
94 FROM dual;
95 END;
96 ");
97
98If you call this producer in array context, we expect you want to process
99the returned array of statements using L<DBI> like
100L<DBIx::Class::Schema/deploy> does.
101
102To get this working we removed the slash in those statements in version
1030.09002 of L<SQL::Translator> when called in array context. In scalar
104context the slash will be still there to ensure compatibility with SQLPlus.
105
ba096dc4 106=cut
107
16dc9970 108use strict;
da06ac74 109use vars qw[ $VERSION $DEBUG $WARN ];
11ad2df9 110$VERSION = '1.59';
d529894e 111$DEBUG = 0 unless defined $DEBUG;
16dc9970 112
57f77285 113use SQL::Translator::Schema::Constants;
5ee19df8 114use SQL::Translator::Utils qw(header_comment);
115
16dc9970 116my %translate = (
d529894e 117 #
118 # MySQL types
119 #
16dc9970 120 bigint => 'number',
07720cf1 121 double => 'float',
16dc9970 122 decimal => 'number',
3c8c1129 123 float => 'float',
16dc9970 124 int => 'number',
25966689 125 integer => 'number',
16dc9970 126 mediumint => 'number',
127 smallint => 'number',
128 tinyint => 'number',
16dc9970 129 char => 'char',
16dc9970 130 varchar => 'varchar2',
1f58ba76 131 tinyblob => 'blob',
132 blob => 'blob',
133 mediumblob => 'blob',
134 longblob => 'blob',
9fc9bfb1 135 tinytext => 'varchar2',
d570aec7 136 text => 'clob',
1f58ba76 137 longtext => 'clob',
138 mediumtext => 'clob',
16dc9970 139 enum => 'varchar2',
140 set => 'varchar2',
16dc9970 141 date => 'date',
142 datetime => 'date',
143 time => 'date',
144 timestamp => 'date',
145 year => 'date',
d529894e 146
147 #
148 # PostgreSQL types
149 #
57f77285 150 numeric => 'number',
151 'double precision' => 'number',
152 serial => 'number',
153 bigserial => 'number',
154 money => 'number',
155 character => 'char',
156 'character varying' => 'varchar2',
157 bytea => 'BLOB',
158 interval => 'number',
159 boolean => 'number',
160 point => 'number',
161 line => 'number',
162 lseg => 'number',
163 box => 'number',
164 path => 'number',
165 polygon => 'number',
166 circle => 'number',
167 cidr => 'number',
168 inet => 'varchar2',
169 macaddr => 'varchar2',
170 bit => 'number',
171 'bit varying' => 'number',
ab8802d7 172
173 #
174 # Oracle types
175 #
176 number => 'number',
177 varchar2 => 'varchar2',
178 long => 'clob',
d529894e 179);
180
181#
e3aac687 182# Oracle 8/9 max size of data types from:
183# http://www.ss64.com/orasyntax/datatypes.html
184#
185my %max_size = (
186 char => 2000,
3c8c1129 187 float => 126,
e3aac687 188 nchar => 2000,
189 nvarchar2 => 4000,
190 number => [ 38, 127 ],
191 raw => 2000,
192 varchar => 4000, # only synonym for varchar2
193 varchar2 => 4000,
194);
195
96844cae 196my $max_id_length = 30;
197my %used_identifiers = ();
d529894e 198my %global_names;
d529894e 199my %truncated;
16dc9970 200
38b019a3 201# Quote used to escape table, field, sequence and trigger names
202my $quote_char = '"';
38b019a3 203
077ebf34 204sub produce {
a1d94525 205 my $translator = shift;
206 $DEBUG = $translator->debug;
e56dabb7 207 $WARN = $translator->show_warnings || 0;
a1d94525 208 my $no_comments = $translator->no_comments;
209 my $add_drop_table = $translator->add_drop_table;
210 my $schema = $translator->schema;
15861005 211 my $oracle_version = $translator->producer_args->{oracle_version} || 0;
65ffb46c 212 my $delay_constraints = $translator->producer_args->{delay_constraints};
213 my ($output, $create, @table_defs, @fk_defs, @trigger_defs, @index_defs, @constraint_defs);
44fcd0b5 214
e56dabb7 215 $create .= header_comment unless ($no_comments);
7769504d 216 my $qt = 1 if $translator->quote_table_names;
217 my $qf = 1 if $translator->quote_field_names;
077ebf34 218
d529894e 219 if ( $translator->parser_type =~ /mysql/i ) {
ea93df61 220 $create .=
ba3cb849 221 "-- We assume that default NLS_DATE_FORMAT has been changed\n".
222 "-- but we set it here anyway to be self-consistent.\n"
223 unless $no_comments;
224
ea93df61 225 $create .=
d529894e 226 "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';\n\n";
227 }
16dc9970 228
ea93df61 229 for my $table ( $schema->get_tables ) {
535e5c95 230 my ( $table_def, $fk_def, $trigger_def, $index_def, $constraint_def ) = create_table(
e56dabb7 231 $table,
232 {
535e5c95 233 add_drop_table => $add_drop_table,
234 show_warnings => $WARN,
235 no_comments => $no_comments,
236 delay_constraints => $delay_constraints,
7769504d 237 quote_table_names => $qt,
238 quote_field_names => $qf,
e56dabb7 239 }
240 );
241 push @table_defs, @$table_def;
242 push @fk_defs, @$fk_def;
243 push @trigger_defs, @$trigger_def;
041e659f 244 push @index_defs, @$index_def;
65ffb46c 245 push @constraint_defs, @$constraint_def;
e56dabb7 246 }
247
248 my (@view_defs);
249 foreach my $view ( $schema->get_views ) {
64ac5763 250 my ( $view_def ) = create_view(
251 $view,
252 {
253 add_drop_view => $add_drop_table,
7769504d 254 quote_table_names => $qt,
64ac5763 255 }
256 );
257 push @view_defs, @$view_def;
e56dabb7 258 }
259
f92d79aa 260 if (wantarray) {
261 return defined $create ? $create : (), @table_defs, @view_defs, @fk_defs, @trigger_defs, @index_defs, @constraint_defs;
262 }
263 else {
38b019a3 264 $create .= join (";\n\n", @table_defs, @view_defs, @fk_defs, @index_defs, @constraint_defs);
7769504d 265 $create .= ";\n\n";
64ac5763 266 # If wantarray is not set we have to add "/" in this statement
267 # DBI->do() needs them omitted
f92d79aa 268 # triggers may NOT end with a semicolon
64ac5763 269 $create .= join "/\n\n", @trigger_defs;
270 # for last trigger
271 $create .= "/\n\n";
f92d79aa 272 return $create;
273 }
e56dabb7 274}
275
276sub create_table {
277 my ($table, $options) = @_;
7769504d 278 my $qt = $options->{quote_table_names};
279 my $qf = $options->{quote_field_names};
e56dabb7 280 my $table_name = $table->name;
7769504d 281 my $table_name_q = quote($table_name,$qt);
64ac5763 282
e56dabb7 283 my $item = '';
284 my $drop;
285 my (@create, @field_defs, @constraint_defs, @fk_defs, @trigger_defs);
16dc9970 286
38b019a3 287 push @create, "--\n-- Table: $table_name\n--" unless $options->{no_comments};
288 push @create, qq[DROP TABLE $table_name_q CASCADE CONSTRAINTS] if $options->{add_drop_table};
a7f999aa 289
f6195129 290 my ( %field_name_scope, @field_comments );
57f77285 291 for my $field ( $table->get_fields ) {
017ac5a7 292 my ($field_create, $field_defs, $trigger_defs, $field_comments) =
293 create_field($field, $options, \%field_name_scope);
294 push @create, @$field_create if ref $field_create;
295 push @field_defs, @$field_defs if ref $field_defs;
296 push @trigger_defs, @$trigger_defs if ref $trigger_defs;
297 push @field_comments, @$field_comments if ref $field_comments;
57f77285 298 }
299
300 #
02c2af3b 301 # Table options
302 #
303 my @table_options;
304 for my $opt ( $table->options ) {
305 if ( ref $opt eq 'HASH' ) {
306 my ( $key, $value ) = each %$opt;
307 if ( ref $value eq 'ARRAY' ) {
308 push @table_options, "$key\n(\n". join ("\n",
ea93df61 309 map { " $_->[0]\t$_->[1]" }
02c2af3b 310 map { [ each %$_ ] }
311 @$value
312 )."\n)";
313 }
314 elsif ( !defined $value ) {
315 push @table_options, $key;
316 }
317 else {
318 push @table_options, "$key $value";
319 }
320 }
321 }
322
323 #
57f77285 324 # Table constraints
325 #
57f77285 326 for my $c ( $table->get_constraints ) {
327 my $name = $c->name || '';
38b019a3 328 my @fields = map { quote($_,$qf) } $c->fields;
b307a0db 329 my @rfields = map { quote($_,$qf) } $c->reference_fields;
330
4dfb0380 331 next if !@fields && $c->type ne CHECK_C;
57f77285 332
333 if ( $c->type eq PRIMARY_KEY ) {
d4977f1c 334 # create a name if delay_constraints
335 $name ||= mk_name( $table_name, 'pk' )
336 if $options->{delay_constraints};
7769504d 337 $name = quote($name,$qf);
54e61f1f 338 push @constraint_defs, ($name ? "CONSTRAINT $name " : '') .
7769504d 339 'PRIMARY KEY (' . join( ', ', @fields ) . ')';
57f77285 340 }
341 elsif ( $c->type eq UNIQUE ) {
7769504d 342 # Don't create UNIQUE constraints identical to the primary key
343 if ( my $pk = $table->primary_key ) {
344 my $u_fields = join(":", @fields);
345 my $pk_fields = join(":", $pk->fields);
346 next if $u_fields eq $pk_fields;
347 }
348
349 if ($name) {
350 # Force prepend of table_name as ORACLE doesn't allow duplicate
351 # CONSTRAINT names even for different tables (ORA-02264)
3b9249fb 352 $name = mk_name( "${table_name}_$name", 'u' ) unless $name =~ /^$table_name/;
7769504d 353 }
354 else {
38b019a3 355 $name = mk_name( $table_name, 'u' );
7769504d 356 }
357
358 $name = quote($name, $qf);
6d4ce9b6 359
b0c196d4 360 for my $f ( $c->fields ) {
361 my $field_def = $table->get_field( $f ) or next;
15861005 362 my $dtype = $translate{ ref $field_def->data_type eq "ARRAY" ? $field_def->data_type->[0] : $field_def->data_type} or next;
b0c196d4 363 if ( $WARN && $dtype =~ /clob/i ) {
364 warn "Oracle will not allow UNIQUE constraints on " .
365 "CLOB field '" . $field_def->table->name . '.' .
366 $field_def->name . ".'\n"
367 }
368 }
6d4ce9b6 369
57f77285 370 push @constraint_defs, "CONSTRAINT $name UNIQUE " .
371 '(' . join( ', ', @fields ) . ')';
372 }
4dfb0380 373 elsif ( $c->type eq CHECK_C ) {
54e61f1f 374 $name ||= mk_name( $name || $table_name, 'ck' );
7769504d 375 $name = quote($name, $qf);
4dfb0380 376 my $expression = $c->expression || '';
377 push @constraint_defs, "CONSTRAINT $name CHECK ($expression)";
378 }
57f77285 379 elsif ( $c->type eq FOREIGN_KEY ) {
541d6e24 380 $name = mk_name( join('_', $table_name, $c->fields). '_fk' );
7769504d 381 $name = quote($name, $qf);
0d3badf1 382 my $on_delete = uc ($c->on_delete || '');
383
cd617ba8 384 my $def = "CONSTRAINT $name FOREIGN KEY ";
385
386 if ( @fields ) {
1c899510 387 $def .= '(' . join( ', ', @fields ) . ')';
cd617ba8 388 }
389
38b019a3 390 my $ref_table = quote($c->reference_table,$qt);
1c899510 391
392 $def .= " REFERENCES $ref_table";
57f77285 393
394 if ( @rfields ) {
395 $def .= ' (' . join( ', ', @rfields ) . ')';
396 }
397
398 if ( $c->match_type ) {
ea93df61 399 $def .= ' MATCH ' .
57f77285 400 ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
401 }
402
0d3badf1 403 if ( $on_delete && $on_delete ne "RESTRICT") {
5863ad87 404 $def .= ' ON DELETE '.$c->on_delete;
57f77285 405 }
406
541d6e24 407 # disabled by plu 2007-12-29 - doesn't exist for oracle
408 #if ( $c->on_update ) {
5863ad87 409 # $def .= ' ON UPDATE '. $c->on_update;
541d6e24 410 #}
57f77285 411
38b019a3 412 push @fk_defs, sprintf("ALTER TABLE %s ADD %s", $table_name_q, $def);
57f77285 413 }
16dc9970 414 }
415
416 #
417 # Index Declarations
418 #
57f77285 419 my @index_defs = ();
57f77285 420 for my $index ( $table->get_indices ) {
421 my $index_name = $index->name || '';
422 my $index_type = $index->type || NORMAL;
38b019a3 423 my @fields = map { quote($_, $qf) } $index->fields;
44fcd0b5 424 next unless @fields;
16dc9970 425
bdd8e79a 426 my @index_options;
427 for my $opt ( $index->options ) {
428 if ( ref $opt eq 'HASH' ) {
429 my ( $key, $value ) = each %$opt;
430 if ( ref $value eq 'ARRAY' ) {
431 push @table_options, "$key\n(\n". join ("\n",
ea93df61 432 map { " $_->[0]\t$_->[1]" }
bdd8e79a 433 map { [ each %$_ ] }
434 @$value
435 )."\n)";
436 }
437 elsif ( !defined $value ) {
438 push @index_options, $key;
439 }
440 else {
441 push @index_options, "$key $value";
442 }
443 }
444 }
445 my $index_options = @index_options
446 ? "\n".join("\n", @index_options) : '';
447
57f77285 448 if ( $index_type eq PRIMARY_KEY ) {
ea93df61 449 $index_name = $index_name ? mk_name( $index_name )
6d4ce9b6 450 : mk_name( $table_name, 'pk' );
7769504d 451 $index_name = quote($index_name, $qf);
57f77285 452 push @field_defs, 'CONSTRAINT '.$index_name.' PRIMARY KEY '.
16dc9970 453 '(' . join( ', ', @fields ) . ')';
454 }
57f77285 455 elsif ( $index_type eq NORMAL ) {
ea93df61 456 $index_name = $index_name ? mk_name( $index_name )
6d4ce9b6 457 : mk_name( $table_name, $index_name || 'i' );
7769504d 458 $index_name = quote($index_name, $qf);
ea93df61 459 push @index_defs,
1ced2a25 460 "CREATE INDEX $index_name on $table_name_q (".
ea93df61 461 join( ', ', @fields ).
4dec2e49 462 ")$index_options";
16dc9970 463 }
041e659f 464 elsif ( $index_type eq UNIQUE ) {
ea93df61 465 $index_name = $index_name ? mk_name( $index_name )
041e659f 466 : mk_name( $table_name, $index_name || 'i' );
7769504d 467 $index_name = quote($index_name, $qf);
ea93df61 468 push @index_defs,
1ced2a25 469 "CREATE UNIQUE INDEX $index_name on $table_name_q (".
ea93df61 470 join( ', ', @fields ).
471 ")$index_options";
041e659f 472 }
16dc9970 473 else {
96844cae 474 warn "Unknown index type ($index_type) on table $table_name.\n"
475 if $WARN;
16dc9970 476 }
477 }
478
9fc9bfb1 479 if ( my @table_comments = $table->comments ) {
480 for my $comment ( @table_comments ) {
481 next unless $comment;
b89d5635 482 $comment =~ s/'/''/g;
38b019a3 483 push @field_comments, "COMMENT ON TABLE $table_name_q is\n '".
4dec2e49 484 $comment . "'" unless $options->{no_comments}
9fc9bfb1 485 ;
486 }
487 }
488
ea93df61 489 my $table_options = @table_options
02c2af3b 490 ? "\n".join("\n", @table_options) : '';
38b019a3 491 push @create, "CREATE TABLE $table_name_q (\n" .
65ffb46c 492 join( ",\n", map { " $_" } @field_defs,
493 ($options->{delay_constraints} ? () : @constraint_defs) ) .
4dec2e49 494 "\n)$table_options";
65ffb46c 495
38b019a3 496 @constraint_defs = map { "ALTER TABLE $table_name_q ADD $_" }
65ffb46c 497 @constraint_defs;
16dc9970 498
96844cae 499 if ( $WARN ) {
500 if ( %truncated ) {
501 warn "Truncated " . keys( %truncated ) . " names:\n";
502 warn "\t" . join( "\n\t", sort keys %truncated ) . "\n";
503 }
96844cae 504 }
505
65ffb46c 506 return \@create, \@fk_defs, \@trigger_defs, \@index_defs, ($options->{delay_constraints} ? \@constraint_defs : []);
e56dabb7 507}
508
017ac5a7 509sub alter_field {
510 my ($from_field, $to_field, $options) = @_;
511
7769504d 512 my $qt = $options->{quote_table_names};
017ac5a7 513 my ($field_create, $field_defs, $trigger_defs, $field_comments) =
514 create_field($to_field, $options, {});
515
d888d445 516 # Fix ORA-01442
517 if ($to_field->is_nullable && !$from_field->is_nullable) {
518 die 'Cannot remove NOT NULL from table field';
519 } elsif (!$from_field->is_nullable && !$to_field->is_nullable) {
520 @$field_defs = map { s/ NOT NULL//; $_} @$field_defs;
521 }
522
1ced2a25 523 my $table_name = quote($to_field->table->name,$qt);
017ac5a7 524
38b019a3 525 return 'ALTER TABLE '.$table_name.' MODIFY ( '.join('', @$field_defs).' )';
017ac5a7 526}
527
fe9f1470 528sub add_field {
529 my ($new_field, $options) = @_;
530
7769504d 531 my $qt = $options->{quote_table_names};
fe9f1470 532 my ($field_create, $field_defs, $trigger_defs, $field_comments) =
533 create_field($new_field, $options, {});
534
1ced2a25 535 my $table_name = quote($new_field->table->name,$qt);
fe9f1470 536
537 my $out = sprintf('ALTER TABLE %s ADD ( %s )',
38b019a3 538 $table_name,
fe9f1470 539 join('', @$field_defs));
540 return $out;
541}
542
017ac5a7 543sub create_field {
544 my ($field, $options, $field_name_scope) = @_;
7769504d 545 my $qf = $options->{quote_field_names};
546 my $qt = $options->{quote_table_names};
017ac5a7 547
548 my (@create, @field_defs, @trigger_defs, @field_comments);
549
550 my $table_name = $field->table->name;
38b019a3 551 my $table_name_q = quote($table_name, $qt);
017ac5a7 552
553 #
554 # Field name
555 #
556 my $field_name = mk_name(
557 $field->name, '', $field_name_scope, 1
558 );
7769504d 559 my $field_name_q = quote($field_name, $qf);
38b019a3 560 my $field_def = quote($field_name, $qf);
561 $field->name( $field_name );
017ac5a7 562
563 #
564 # Datatype
565 #
566 my $check;
567 my $data_type = lc $field->data_type;
568 my @size = $field->size;
569 my %extra = $field->extra;
570 my $list = $extra{'list'} || [];
571 # \todo deal with embedded quotes
572 my $commalist = join( ', ', map { qq['$_'] } @$list );
573
574 if ( $data_type eq 'enum' ) {
38b019a3 575 $check = "CHECK ($field_name_q IN ($commalist))";
017ac5a7 576 $data_type = 'varchar2';
577 }
578 elsif ( $data_type eq 'set' ) {
ea93df61 579 # XXX add a CHECK constraint maybe
017ac5a7 580 # (trickier and slower, than enum :)
581 $data_type = 'varchar2';
582 }
583 else {
7769504d 584 if (defined $translate{ $data_type }) {
585 if (ref $translate{ $data_type } eq "ARRAY") {
586 ($data_type,$size[0]) = @{$translate{ $data_type }};
587 } else {
588 $data_type = $translate{ $data_type };
589 }
590 }
15861005 591 $data_type ||= 'varchar2';
017ac5a7 592 }
7769504d 593
e3aac687 594 # ensure size is not bigger than max size oracle allows for data type
595 if ( defined $max_size{$data_type} ) {
596 for ( my $i = 0 ; $i < scalar @size ; $i++ ) {
597 my $max =
598 ref( $max_size{$data_type} ) eq 'ARRAY'
599 ? $max_size{$data_type}->[$i]
600 : $max_size{$data_type};
601 $size[$i] = $max if $size[$i] > $max;
602 }
603 }
017ac5a7 604
605 #
ea93df61 606 # Fixes ORA-02329: column of datatype LOB cannot be
017ac5a7 607 # unique or a primary key
608 #
609 if ( $data_type eq 'clob' && $field->is_primary_key ) {
610 $data_type = 'varchar2';
611 $size[0] = 4000;
612 warn "CLOB cannot be a primary key, changing to VARCHAR2\n"
613 if $WARN;
614 }
615
616 if ( $data_type eq 'clob' && $field->is_unique ) {
617 $data_type = 'varchar2';
618 $size[0] = 4000;
619 warn "CLOB cannot be a unique key, changing to VARCHAR2\n"
620 if $WARN;
621 }
622
623 #
624 # Fixes ORA-00907: missing right parenthesis
625 #
626 if ( $data_type =~ /(date|clob)/i ) {
627 undef @size;
628 }
629
e19efc15 630 #
631 # Fixes ORA-00906: missing right parenthesis
ea93df61 632 # if size is 0 or undefined
e19efc15 633 #
634 for (qw/varchar2/) {
635 if ( $data_type =~ /^($_)$/i ) {
636 $size[0] ||= $max_size{$_};
637 }
638 }
639
017ac5a7 640 $field_def .= " $data_type";
641 if ( defined $size[0] && $size[0] > 0 ) {
9190556b 642 $field_def .= '(' . join( ',', @size ) . ')';
017ac5a7 643 }
644
645 #
646 # Default value
647 #
648 my $default = $field->default_value;
649 if ( defined $default ) {
650 #
ea93df61 651 # Wherein we try to catch a string being used as
017ac5a7 652 # a default value for a numerical field. If "true/false,"
653 # then sub "1/0," otherwise just test the truthity of the
654 # argument and use that (naive?).
655 #
bc8e2aa1 656 if (ref $default and defined $$default) {
657 $default = $$default;
658 } elsif (ref $default) {
659 $default = 'NULL';
ea93df61 660 } elsif (
661 $data_type =~ /^number$/i &&
017ac5a7 662 $default !~ /^-?\d+$/ &&
663 $default !~ m/null/i
664 ) {
665 if ( $default =~ /^true$/i ) {
666 $default = "'1'";
667 } elsif ( $default =~ /^false$/i ) {
668 $default = "'0'";
669 } else {
670 $default = $default ? "'1'" : "'0'";
671 }
ea93df61 672 } elsif (
017ac5a7 673 $data_type =~ /date/ && (
ea93df61 674 $default eq 'current_timestamp'
017ac5a7 675 ||
ea93df61 676 $default eq 'now()'
017ac5a7 677 )
678 ) {
679 $default = 'SYSDATE';
680 } else {
681 $default = $default =~ m/null/i ? 'NULL' : "'$default'"
ea93df61 682 }
017ac5a7 683
684 $field_def .= " DEFAULT $default",
685 }
686
687 #
688 # Not null constraint
689 #
690 unless ( $field->is_nullable ) {
691 $field_def .= ' NOT NULL';
692 }
693
694 $field_def .= " $check" if $check;
695
696 #
697 # Auto_increment
698 #
699 if ( $field->is_auto_increment ) {
38b019a3 700 my $base_name = $table_name . "_". $field_name;
701 my $seq_name = quote(mk_name( $base_name, 'sq' ),$qt);
702 my $trigger_name = quote(mk_name( $base_name, 'ai' ),$qt);
017ac5a7 703
4dec2e49 704 push @create, qq[DROP SEQUENCE $seq_name] if $options->{add_drop_table};
705 push @create, "CREATE SEQUENCE $seq_name";
535e5c95 706 my $trigger =
017ac5a7 707 "CREATE OR REPLACE TRIGGER $trigger_name\n" .
38b019a3 708 "BEFORE INSERT ON $table_name_q\n" .
017ac5a7 709 "FOR EACH ROW WHEN (\n" .
38b019a3 710 " new.$field_name_q IS NULL".
711 " OR new.$field_name_q = 0\n".
017ac5a7 712 ")\n".
713 "BEGIN\n" .
714 " SELECT $seq_name.nextval\n" .
38b019a3 715 " INTO :new." . $field_name_q."\n" .
017ac5a7 716 " FROM dual;\n" .
535e5c95 717 "END;\n";
7769504d 718
535e5c95 719 push @trigger_defs, $trigger;
017ac5a7 720 }
721
722 if ( lc $field->data_type eq 'timestamp' ) {
38b019a3 723 my $base_name = $table_name . "_". $field_name;
724 my $trig_name = quote(mk_name( $base_name, 'ts' ), $qt);
ea93df61 725 my $trigger =
017ac5a7 726 "CREATE OR REPLACE TRIGGER $trig_name\n".
38b019a3 727 "BEFORE INSERT OR UPDATE ON $table_name_q\n".
728 "FOR EACH ROW WHEN (new.$field_name_q IS NULL)\n".
017ac5a7 729 "BEGIN \n".
38b019a3 730 " SELECT sysdate INTO :new.$field_name_q FROM dual;\n".
535e5c95 731 "END;\n";
732
535e5c95 733 push @trigger_defs, $trigger;
017ac5a7 734 }
735
736 push @field_defs, $field_def;
737
738 if ( my $comment = $field->comments ) {
739 $comment =~ s/'/''/g;
ea93df61 740 push @field_comments,
1ced2a25 741 "COMMENT ON COLUMN $table_name_q.$field_name_q is\n '" .
017ac5a7 742 $comment . "';" unless $options->{no_comments};
743 }
744
745 return \@create, \@field_defs, \@trigger_defs, \@field_comments;
746
747}
748
749
e56dabb7 750sub create_view {
64ac5763 751 my ($view, $options) = @_;
7769504d 752 my $qt = $options->{quote_table_names};
38b019a3 753 my $view_name = quote($view->name,$qt);
ea93df61 754
64ac5763 755 my @create;
756 push @create, qq[DROP VIEW $view_name]
757 if $options->{add_drop_view};
e56dabb7 758
64ac5763 759 push @create, sprintf("CREATE VIEW %s AS\n%s",
760 $view_name,
e56dabb7 761 $view->sql);
762
64ac5763 763 return \@create;
16dc9970 764}
765
d529894e 766sub mk_name {
ea93df61 767 my $basename = shift || '';
768 my $type = shift || '';
1c899510 769 $type = '' if $type =~ /^\d/;
ea93df61 770 my $scope = shift || '';
57f77285 771 my $critical = shift || '';
d529894e 772 my $basename_orig = $basename;
ea93df61 773 my $max_name = $type
774 ? $max_id_length - (length($type) + 1)
f5087552 775 : $max_id_length;
ea93df61 776 $basename = substr( $basename, 0, $max_name )
96844cae 777 if length( $basename ) > $max_name;
d529894e 778 my $name = $type ? "${type}_$basename" : $basename;
779
780 if ( $basename ne $basename_orig and $critical ) {
781 my $show_type = $type ? "+'$type'" : "";
782 warn "Truncating '$basename_orig'$show_type to $max_id_length ",
96844cae 783 "character limit to make '$name'\n" if $WARN;
784 $truncated{ $basename_orig } = $name;
d529894e 785 }
786
787 $scope ||= \%global_names;
96844cae 788 if ( my $prev = $scope->{ $name } ) {
789 my $name_orig = $name;
b89d5635 790 substr($name, $max_id_length - 2) = ""
791 if length( $name ) >= $max_id_length - 1;
792 $name .= sprintf( "%02d", $prev++ );
96844cae 793
794 warn "The name '$name_orig' has been changed to ",
795 "'$name' to make it unique.\n" if $WARN;
796
797 $scope->{ $name_orig }++;
798 }
799
800 $scope->{ $name }++;
d529894e 801 return $name;
802}
803
38b019a3 8041;
d529894e 805
38b019a3 806sub quote {
807 my ($name, $q) = @_;
7769504d 808 $q && $name ? "$quote_char$name$quote_char" : $name;
d529894e 809}
810
16dc9970 811
d529894e 812# -------------------------------------------------------------------
16dc9970 813# All bad art is the result of good intentions.
814# Oscar Wilde
d529894e 815# -------------------------------------------------------------------
16dc9970 816
ba096dc4 817=pod
16dc9970 818
d529894e 819=head1 CREDITS
820
ba096dc4 821Mad props to Tim Bunce for much of the logic stolen from his "mysql2ora"
822script.
16dc9970 823
42ef836a 824=head1 AUTHORS
16dc9970 825
42ef836a 826Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>,
38b019a3 827Alexander Hartmaier E<lt>abraxxa@cpan.orgE<gt>,
828Fabien Wernli E<lt>faxmodem@cpan.orgE<gt>.
16dc9970 829
830=head1 SEE ALSO
831
ba096dc4 832SQL::Translator, DDL::Oracle, mysql2ora.
16dc9970 833
834=cut