use SQL::Translator::Generator::DDL::PostgreSQL;
use Data::Dumper;
+use constant MAX_ID_LENGTH => 62;
+
{
my ($quoting_generator, $nonquoting_generator);
sub _generator {
}
}
-my ( %translate, %index_name );
-my $max_id_length;
+my ( %translate );
BEGIN {
#
# MySQL types
#
- bigint => 'bigint',
double => 'double precision',
decimal => 'numeric',
int => 'integer',
mediumint => 'integer',
- smallint => 'smallint',
tinyint => 'smallint',
char => 'character',
varchar => 'character varying',
longtext => 'text',
mediumtext => 'text',
- text => 'text',
tinytext => 'text',
tinyblob => 'bytea',
blob => 'bytea',
longblob => 'bytea',
enum => 'character varying',
set => 'character varying',
- date => 'date',
datetime => 'timestamp',
- time => 'time',
- timestamp => 'timestamp',
year => 'date',
#
# Oracle types
#
number => 'integer',
- char => 'character',
varchar2 => 'character varying',
long => 'text',
- CLOB => 'bytea',
- date => 'date',
+ clob => 'text',
#
# Sybase types
#
- int => 'integer',
- money => 'money',
- varchar => 'character varying',
- datetime => 'timestamp',
- text => 'text',
comment => 'text',
- bit => 'bit',
- tinyint => 'smallint',
-);
- $max_id_length = 62;
+ #
+ # MS Access types
+ #
+ memo => 'text',
+);
}
-my %reserved = map { $_, 1 } qw[
- ALL ANALYSE ANALYZE AND ANY AS ASC
- BETWEEN BINARY BOTH
- CASE CAST CHECK COLLATE COLUMN CONSTRAINT CROSS
- CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER
- DEFAULT DEFERRABLE DESC DISTINCT DO
- ELSE END EXCEPT
- FALSE FOR FOREIGN FREEZE FROM FULL
- GROUP HAVING
- ILIKE IN INITIALLY INNER INTERSECT INTO IS ISNULL
- JOIN LEADING LEFT LIKE LIMIT
- NATURAL NEW NOT NOTNULL NULL
- OFF OFFSET OLD ON ONLY OR ORDER OUTER OVERLAPS
- PRIMARY PUBLIC REFERENCES RIGHT
- SELECT SESSION_USER SOME TABLE THEN TO TRAILING TRUE
- UNION UNIQUE USER USING VERBOSE WHEN WHERE
-];
-
-# my $max_id_length = 62;
-my %used_identifiers = ();
-my %global_names;
my %truncated;
=pod
for my $trigger ( $schema->get_triggers ) {
push @table_defs, create_trigger( $trigger, {
add_drop_trigger => $add_drop_table,
+ generator => $generator,
no_comments => $no_comments,
});
}
: join ('', @output);
}
-sub mk_name {
- my $basename = shift || '';
- my $type = shift || '';
- my $scope = shift || '';
- my $critical = shift || '';
- my $basename_orig = $basename;
-# my $max_id_length = 62;
- my $max_name = $type
- ? $max_id_length - (length($type) + 1)
- : $max_id_length;
- $basename = substr( $basename, 0, $max_name )
- if length( $basename ) > $max_name;
- my $name = $type ? "${type}_$basename" : $basename;
-
- if ( $basename ne $basename_orig and $critical ) {
- my $show_type = $type ? "+'$type'" : "";
- warn "Truncating '$basename_orig'$show_type to $max_id_length ",
- "character limit to make '$name'\n" if $WARN;
- $truncated{ $basename_orig } = $name;
- }
+{
+ my %global_names;
+ sub mk_name {
+ my $basename = shift || '';
+ my $type = shift || '';
+ my $scope = shift || '';
+ my $critical = shift || '';
+ my $basename_orig = $basename;
+
+ my $max_name = $type
+ ? MAX_ID_LENGTH - (length($type) + 1)
+ : MAX_ID_LENGTH;
+ $basename = substr( $basename, 0, $max_name )
+ if length( $basename ) > $max_name;
+ my $name = $type ? "${type}_$basename" : $basename;
+
+ if ( $basename ne $basename_orig and $critical ) {
+ my $show_type = $type ? "+'$type'" : "";
+ warn "Truncating '$basename_orig'$show_type to ", MAX_ID_LENGTH,
+ " character limit to make '$name'\n" if $WARN;
+ $truncated{ $basename_orig } = $name;
+ }
- $scope ||= \%global_names;
- if ( my $prev = $scope->{ $name } ) {
- my $name_orig = $name;
- $name .= sprintf( "%02d", ++$prev );
- substr($name, $max_id_length - 3) = "00"
- if length( $name ) > $max_id_length;
+ $scope ||= \%global_names;
+ if ( my $prev = $scope->{ $name } ) {
+ my $name_orig = $name;
+ $name .= sprintf( "%02d", ++$prev );
+ substr($name, MAX_ID_LENGTH - 3) = "00"
+ if length( $name ) > MAX_ID_LENGTH;
- warn "The name '$name_orig' has been changed to ",
- "'$name' to make it unique.\n" if $WARN;
+ warn "The name '$name_orig' has been changed to ",
+ "'$name' to make it unique.\n" if $WARN;
- $scope->{ $name_orig }++;
- }
+ $scope->{ $name_orig }++;
+ }
- $scope->{ $name }++;
- return $name;
+ $scope->{ $name }++;
+ return $name;
+ }
}
sub is_geometry
my $table_name_qt = $generator->quote($table_name);
# print STDERR "$table_name table_name\n";
- my ( @comments, @field_defs, @sequence_defs, @constraint_defs, @fks );
+ my ( @comments, @field_defs, @index_defs, @sequence_defs, @constraint_defs, @fks );
push @comments, "--\n-- Table: $table_name\n--\n" unless $no_comments;
- if ( $table->comments and !$no_comments ){
- my $c = "-- Comments: \n-- ";
- $c .= join "\n-- ", $table->comments;
- $c .= "\n--\n";
- push @comments, $c;
+ if ( !$no_comments and my $comments = $table->comments ) {
+ $comments =~ s/^/-- /mg;
+ push @comments, "-- Comments:\n$comments\n--\n";
}
#
# Fields
#
- my %field_name_scope;
for my $field ( $table->get_fields ) {
push @field_defs, create_field($field, {
generator => $generator,
#
# Index Declarations
#
- my @index_defs = ();
- # my $idx_name_default;
for my $index ( $table->get_indices ) {
my ($idef, $constraints) = create_index($index, {
generator => $generator,
#
# Table constraints
#
- my $c_name_default;
for my $c ( $table->get_constraints ) {
my ($cdefs, $fks) = create_constraint($c, {
generator => $generator,
}
- my $temporary = "";
-
- if(exists $table->extra->{temporary}) {
- $temporary = $table->extra->{temporary} ? "TEMPORARY " : "";
- }
-
- my $create_statement;
- $create_statement = join("\n", @comments);
+ my $create_statement = join("\n", @comments);
if ($add_drop_table) {
if ($postgres_version >= 8.002) {
$create_statement .= "DROP TABLE IF EXISTS $table_name_qt CASCADE;\n";
$create_statement .= "DROP TABLE $table_name_qt CASCADE;\n";
}
}
+ my $temporary = $table->extra->{temporary} ? "TEMPORARY " : "";
$create_statement .= "CREATE ${temporary}TABLE $table_name_qt (\n" .
join( ",\n", map { " $_" } @field_defs, @constraint_defs ).
"\n)"
$field_name_scope{$table_name} ||= {};
my $field_name = $field->name;
- my $field_comments = $field->comments
- ? "-- " . $field->comments . "\n "
- : '';
+ my $field_comments = '';
+ if (my $comments = $field->comments) {
+ $comments =~ s/(?<!\A)^/ -- /mg;
+ $field_comments = "-- $comments\n ";
+ }
my $field_def = $field_comments . $generator->quote($field_name);
return @constraints;
}
-sub create_index
{
- my ($index, $options) = @_;
-
- my $generator = _generator($options);
- my $table_name = $index->table->name;
-
- my ($index_def, @constraint_defs);
+ my %index_name;
+ sub create_index
+ {
+ my ($index, $options) = @_;
- my $name
- = $index->name
- || join('_', $table_name, 'idx', ++$index_name{ $table_name });
+ my $generator = _generator($options);
+ my $table_name = $index->table->name;
+
+ my ($index_def, @constraint_defs);
+
+ my $name
+ = $index->name
+ || join('_', $table_name, 'idx', ++$index_name{ $table_name });
+
+ my $type = $index->type || NORMAL;
+ my @fields = $index->fields;
+ return unless @fields;
+
+ my $index_using;
+ my $index_where;
+ for my $opt ( $index->options ) {
+ if ( ref $opt eq 'HASH' ) {
+ foreach my $key (keys %$opt) {
+ my $value = $opt->{$key};
+ next unless defined $value;
+ if ( uc($key) eq 'USING' ) {
+ $index_using = "USING $value";
+ }
+ elsif ( uc($key) eq 'WHERE' ) {
+ $index_where = "WHERE $value";
+ }
+ }
+ }
+ }
- my $type = $index->type || NORMAL;
- my @fields = $index->fields;
- return unless @fields;
+ my $def_start = 'CONSTRAINT ' . $generator->quote($name) . ' ';
+ my $field_names = '(' . join(", ", (map { $_ =~ /\(.*\)/ ? $_ : ( $generator->quote($_) ) } @fields)) . ')';
+ if ( $type eq PRIMARY_KEY ) {
+ push @constraint_defs, "${def_start}PRIMARY KEY ".$field_names;
+ }
+ elsif ( $type eq UNIQUE ) {
+ push @constraint_defs, "${def_start}UNIQUE " .$field_names;
+ }
+ elsif ( $type eq NORMAL ) {
+ $index_def =
+ 'CREATE INDEX ' . $generator->quote($name) . ' on ' . $generator->quote($table_name) . ' ' .
+ join ' ', grep { defined } $index_using, $field_names, $index_where;
+ }
+ else {
+ warn "Unknown index type ($type) on table $table_name.\n"
+ if $WARN;
+ }
- my $def_start = 'CONSTRAINT ' . $generator->quote($name) . ' ';
- my $field_names = '(' . join(", ", (map { $_ =~ /\(.*\)/ ? $_ : ( $generator->quote($_) ) } @fields)) . ')';
- if ( $type eq PRIMARY_KEY ) {
- push @constraint_defs, "${def_start}PRIMARY KEY ".$field_names;
- }
- elsif ( $type eq UNIQUE ) {
- push @constraint_defs, "${def_start}UNIQUE " .$field_names;
- }
- elsif ( $type eq NORMAL ) {
- $index_def =
- 'CREATE INDEX ' . $generator->quote($name) . ' on ' . $generator->quote($table_name) . ' ' . $field_names
- ;
+ return $index_def, \@constraint_defs;
}
- else {
- warn "Unknown index type ($type) on table $table_name.\n"
- if $WARN;
- }
-
- return $index_def, \@constraint_defs;
}
sub create_constraint
sub create_trigger {
my ($trigger,$options) = @_;
+ my $generator = _generator($options);
my @statements;
- push @statements, sprintf( 'DROP TRIGGER IF EXISTS %s', $trigger->name )
+ push @statements, sprintf( 'DROP TRIGGER IF EXISTS %s', $generator->quote($trigger->name) )
if $options->{add_drop_trigger};
my $scope = $trigger->scope || '';
push @statements, sprintf(
'CREATE TRIGGER %s %s %s ON %s%s %s',
- $trigger->name,
+ $generator->quote($trigger->name),
$trigger->perform_action_when,
join( ' OR ', @{ $trigger->database_events } ),
- $trigger->on_table,
+ $generator->quote($trigger->on_table),
$scope,
$trigger->action,
);
undef @size;
}
else {
- $data_type = defined $translate{ $data_type } ?
- $translate{ $data_type } :
+ $data_type = defined $translate{ lc $data_type } ?
+ $translate{ lc $data_type } :
$data_type;
}