package SQL::Translator::Producer::Oracle;
# -------------------------------------------------------------------
-# $Id: Oracle.pm,v 1.19 2003-08-17 07:51:33 rossta Exp $
+# $Id: Oracle.pm,v 1.27 2003-10-15 20:39:15 kycl4rk Exp $
# -------------------------------------------------------------------
# Copyright (C) 2003 Ken Y. Clark <kclark@cpan.org>,
# darren chamberlain <darren@cpan.org>,
# 02111-1307 USA
# -------------------------------------------------------------------
+=head1 NAME
+
+SQL::Translator::Producer::Oracle - Oracle SQL producer
+
+=head1 SYNOPSIS
+
+ use SQL::Translator;
+
+ my $t = SQL::Translator->new( parser => '...', producer => 'Oracle' );
+ print $translator->translate( $file );
+
+=head1 DESCRIPTION
+
+Creates an SQL DDL suitable for Oracle.
+
+=cut
+
use strict;
use vars qw[ $VERSION $DEBUG $WARN ];
-$VERSION = sprintf "%d.%02d", q$Revision: 1.19 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.27 $ =~ /(\d+)\.(\d+)/;
$DEBUG = 0 unless defined $DEBUG;
use SQL::Translator::Schema::Constants;
blob => 'blob',
mediumblob => 'blob',
longblob => 'blob',
+ tinytext => 'varchar2',
+ text => 'clob',
longtext => 'clob',
mediumtext => 'clob',
- text => 'clob',
- tinytext => 'clob',
enum => 'varchar2',
set => 'varchar2',
date => 'date',
macaddr => 'varchar2',
bit => 'number',
'bit varying' => 'number',
+
+ #
+ # Oracle types
+ #
+ number => 'number',
+ varchar2 => 'varchar2',
+ long => 'clob',
);
#
if ( $translator->parser_type =~ /mysql/i ) {
$output .=
- "-- We assume that default NLS_DATE_FORMAT has been changed\n".
- "-- but we set it here anyway to be self-consistent.\n".
+ "-- We assume that default NLS_DATE_FORMAT has been changed\n".
+ "-- but we set it here anyway to be self-consistent.\n"
+ unless $no_comments;
+
+ $output .=
"ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';\n\n";
}
my %extra = $field->extra;
my $list = $extra{'list'} || [];
# \todo deal with embedded quotes
- my $commalist = "'" . (join "', '", @$list) . "'";
+ my $commalist = join( ', ', map { qq['$_'] } @$list );
if ( $data_type eq 'enum' ) {
$check = "CHECK ($field_name_ur IN ($commalist))";
die "Unknown datatype: $data_type\n";
}
- # Fixes ORA-02329: column of datatype LOB cannot be unique or a primary key
+ #
+ # Fixes ORA-02329: column of datatype LOB cannot be
+ # unique or a primary key
+ #
if ( $data_type eq 'clob' && $field->is_primary_key ) {
$data_type = 'varchar2';
- $size[0] = 4000;
+ $size[0] = 4000;
+ warn "CLOB cannot be a primary key, changing to VARCHAR2\n"
+ if $WARN;
}
+ #
# Fixes ORA-00907: missing right parenthesis
- if ($data_type eq 'date') {
+ #
+ if ( $data_type =~ /(date|clob)/i ) {
undef @size;
}
#
my $default = $field->default_value;
if ( defined $default ) {
- $field_def .= sprintf(
- ' DEFAULT %s',
- $default =~ m/null/i ? 'NULL' : "'$default'"
- );
+ #
+ # Wherein we try to catch a string being used as
+ # a default value for a numerical field. If "true/false,"
+ # then sub "1/0," otherwise just test the truthity of the
+ # argument and use that (naive?).
+ #
+ if (
+ $data_type =~ /^number$/i &&
+ $default !~ /^\d+$/ &&
+ $default !~ m/null/i
+ ) {
+ if ( $default =~ /^true$/i ) {
+ $default = "'1'";
+ }
+ elsif ( $default =~ /^false$/i ) {
+ $default = "'0'";
+ }
+ else {
+ $default = $default ? "'1'" : "'0'";
+ }
+ }
+ elsif (
+ $data_type =~ /date/ && $default eq 'current_timestamp'
+ ) {
+ $default = 'SYSDATE';
+ }
+ else {
+ $default = $default =~ m/null/i ? 'NULL' : "'$default'"
+ }
+
+ $field_def .= " DEFAULT $default",
}
#
# Not null constraint
#
unless ( $field->is_nullable ) {
- my $constraint_name = mk_name($field_name_ur, 'nn');
- $field_def .= ' CONSTRAINT ' . $constraint_name . ' NOT NULL';
+# my $constraint_name = mk_name(
+# join('_', $table_name_ur, $field_name_ur ), 'nn'
+# );
+# $field_def .= ' CONSTRAINT ' . $constraint_name . ' NOT NULL';
+ $field_def .= ' NOT NULL';
}
$field_def .= " $check" if $check;
if ( my $comment = $field->comments ) {
push @field_comments,
"COMMENT ON COLUMN $table_name.$field_name_ur is\n '".
- $comment."';";
+ $comment."';" unless $no_comments;
+ }
+ }
+
+ #
+ # Table options
+ #
+ my @table_options;
+ for my $opt ( $table->options ) {
+ if ( ref $opt eq 'HASH' ) {
+ my ( $key, $value ) = each %$opt;
+ if ( ref $value eq 'ARRAY' ) {
+ push @table_options, "$key\n(\n". join ("\n",
+ map { " $_->[0]\t$_->[1]" }
+ map { [ each %$_ ] }
+ @$value
+ )."\n)";
+ }
+ elsif ( !defined $value ) {
+ push @table_options, $key;
+ }
+ else {
+ push @table_options, "$key $value";
+ }
}
}
#
# Table constraints
#
- my $constraint_name_default;
for my $c ( $table->get_constraints ) {
my $name = $c->name || '';
my @fields = map { unreserve( $_, $table_name ) } $c->fields;
my @rfields = map { unreserve( $_, $table_name ) }
$c->reference_fields;
- next unless @fields;
+ next if !@fields && $c->type ne CHECK_C;
if ( $c->type eq PRIMARY_KEY ) {
$name ||= mk_name( $table_name, 'pk' );
'(' . join( ', ', @fields ) . ')';
}
elsif ( $c->type eq UNIQUE ) {
- $name ||= mk_name( $table_name, ++$constraint_name_default );
+ $name ||= mk_name( $table_name, 'u' );
push @constraint_defs, "CONSTRAINT $name UNIQUE " .
'(' . join( ', ', @fields ) . ')';
}
+ elsif ( $c->type eq CHECK_C ) {
+ $name ||= mk_name( $table_name, 'ck' );
+ my $expression = $c->expression || '';
+ push @constraint_defs, "CONSTRAINT $name CHECK ($expression)";
+ }
elsif ( $c->type eq FOREIGN_KEY ) {
- $name ||= mk_name( $table_name, ++$constraint_name_default );
+ $name ||= mk_name( join('_', $table_name, $c->fields), 'fk' );
my $def = "CONSTRAINT $name FOREIGN KEY ";
if ( @fields ) {
- $def .= join( ', ', @fields );
+ $def .= '(' . join( ', ', @fields ) . ')';
}
- $def .= ' REFERENCES ' . $c->reference_table;
+ my $ref_table = unreserve($c->reference_table);
+
+ $def .= " REFERENCES $ref_table";
if ( @rfields ) {
$def .= ' (' . join( ', ', @rfields ) . ')';
# Index Declarations
#
my @index_defs = ();
- my $idx_name_default;
for my $index ( $table->get_indices ) {
my $index_name = $index->name || '';
my $index_type = $index->type || NORMAL;
next unless @fields;
if ( $index_type eq PRIMARY_KEY ) {
- $index_name = mk_name( $table_name, 'pk' );
+ $index_name ||= mk_name( $table_name, 'pk' );
push @field_defs, 'CONSTRAINT '.$index_name.' PRIMARY KEY '.
'(' . join( ', ', @fields ) . ')';
}
- elsif ( $index_type eq UNIQUE ) {
- $index_name = mk_name(
- $table_name, $index_name || ++$idx_name_default
- );
- push @field_defs, 'CONSTRAINT ' . $index_name . ' UNIQUE ' .
- '(' . join( ', ', @fields ) . ')';
- }
-
elsif ( $index_type eq NORMAL ) {
- $index_name = mk_name(
- $table_name, $index_name || ++$idx_name_default
- );
+ $index_name ||= mk_name( $table_name, $index_name || 'i' );
push @index_defs,
"CREATE INDEX $index_name on $table_name_ur (".
join( ', ', @fields ).
my $create_statement;
$create_statement = "DROP TABLE $table_name_ur;\n" if $add_drop_table;
- $create_statement .=
- join( ",\n", map { "-- $_" } $table->comments ) .
- "CREATE TABLE $table_name_ur (\n" .
+
+ if ( my @table_comments = $table->comments ) {
+ for my $comment ( @table_comments ) {
+ next unless $comment;
+ push @field_comments, "COMMENT ON TABLE $table_name is\n '".
+ $comment."';" unless $no_comments
+ ;
+ }
+ }
+
+ my $table_options = @table_options
+ ? "\n".join("\n", @table_options) : '';
+ $create_statement .= "CREATE TABLE $table_name_ur (\n" .
join( ",\n", map { " $_" } @field_defs, @constraint_defs ) .
- "\n);"
+ "\n)$table_options;"
;
$output .= join( "\n\n",
sub mk_name {
my $basename = shift || '';
my $type = shift || '';
+ $type = '' if $type =~ /^\d/;
my $scope = shift || '';
my $critical = shift || '';
my $basename_orig = $basename;
# Oscar Wilde
# -------------------------------------------------------------------
-=head1 NAME
-
-SQL::Translator::Producer::Oracle - Oracle SQL producer
-
-=head1 SYNOPSIS
-
- use SQL::Translator::Parser::MySQL;
- use SQL::Translator::Producer::Oracle;
-
- my $original_create = ""; # get this from somewhere...
- my $translator = SQL::Translator->new;
-
- $translator->parser("SQL::Translator::Parser::MySQL");
- $translator->producer("SQL::Translator::Producer::Oracle");
-
- my $new_create = $translator->translate($original_create);
-
-=head1 DESCRIPTION
-
-SQL::Translator::Producer::Oracle takes a parsed data structure,
-created by a SQL::Translator::Parser subclass, and turns it into a
-create string suitable for use with an Oracle database.
+=pod
=head1 CREDITS
-A hearty "thank-you" to Tim Bunce for much of the logic stolen from
-his "mysql2ora" script.
+Mad props to Tim Bunce for much of the logic stolen from his "mysql2ora"
+script.
=head1 AUTHOR
-Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>.
=head1 SEE ALSO
-perl(1).
+SQL::Translator, DDL::Oracle, mysql2ora.
=cut