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