return (
'CONSTRAINT ' . $self->enum_constraint_name($field_name) .
' CHECK (' . $self->quote($field_name) .
- ' IN (' . join( ',', map qq('$_'), @$vals ) . '))'
+ ' IN (' . join( ',', map $self->quote_string($_), @$vals ) . '))'
)
}
requires '_build_unquoted_defaults';
requires '_build_sizeless_types';
requires 'quote';
+requires 'quote_string';
has type_map => (
is => 'lazy',
if (ref $default) {
$default = $$default;
} elsif (!($self->numeric_types->{lc($field->data_type)} && Scalar::Util::looks_like_number ($default))) {
- $default = "'$default'";
+ $default = $self->quote_string($default);
}
return ( "DEFAULT $default" )
}
join $sep, map { (my $n = $_) =~ s/\Q$r/$esc$r/g; "$l$n$r" } ( $sep ? split (/\Q$sep\E/, $label ) : $label )
}
+sub quote_string {
+ my ($self, $string) = @_;
+
+ return $string unless defined $string;
+ $string =~ s/'/''/g;
+ return qq{'$string'};
+}
+
1;
=head1 AUTHORS
## They are special per Producer, and provide support for the old 'now()'
## default value exceptions
sub _apply_default_value {
- my (undef, $field, $field_ref, $exceptions) = @_;
+ my ($self, $field, $field_ref, $exceptions) = @_;
my $default = $field->default_value;
return if !defined $default;
# we need to check the data itself in addition to the datatype, for basic safety
$$field_ref .= " DEFAULT $default";
} else {
- $$field_ref .= " DEFAULT '$default'";
+ $default = $self->_quote_string($default);
+ $$field_ref .= " DEFAULT $default";
}
}
+sub _quote_string {
+ my ($self, $string) = @_;
+ $string =~ s/'/''/g;
+ return qq{'$string'};
+}
+
1;
# -------------------------------------------------------------------
# http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
my $DEFAULT_MAX_ID_LENGTH = 64;
+use base qw(SQL::Translator::Producer);
use Data::Dumper;
use SQL::Translator::Schema::Constants;
use SQL::Translator::Generator::DDL::MySQL;
my @size = $field->size;
my %extra = $field->extra;
my $list = $extra{'list'} || [];
- # \todo deal with embedded quotes
- my $commalist = join( ', ', map { qq['$_'] } @$list );
+ my $commalist = join( ', ', map { __PACKAGE__->_quote_string($_) } @$list );
my $charset = $extra{'mysql_charset'};
my $collate = $extra{'mysql_collate'};
}
# Default?
- SQL::Translator::Producer->_apply_default_value(
+ __PACKAGE__->_apply_default_value(
$field,
\$field_def,
[
);
if ( my $comments = $field->comments ) {
- $field_def .= qq[ comment '$comments'];
+ $comments = __PACKAGE__->_quote_string($comments);
+ $field_def .= qq[ comment $comments];
}
# auto_increment?
return $field_def;
}
+sub _quote_string {
+ my ($self, $string) = @_;
+
+ $string =~ s/([\\'])/$1$1/g;
+ return qq{'$string'};
+}
+
sub alter_create_index
{
my ($index, $options) = @_;
our $VERSION = '1.59';
$DEBUG = 0 unless defined $DEBUG;
+use base 'SQL::Translator::Producer';
use SQL::Translator::Schema::Constants;
use SQL::Translator::Utils qw(header_comment);
if ( my @table_comments = $table->comments ) {
for my $comment ( @table_comments ) {
next unless $comment;
- $comment =~ s/'/''/g;
- push @field_comments, "COMMENT ON TABLE $table_name_q is\n '".
- $comment . "'" unless $options->{no_comments}
- ;
+ $comment = __PACKAGE__->_quote_string($comment);
+ push @field_comments, "COMMENT ON TABLE $table_name_q is\n $comment"
+ unless $options->{no_comments};
}
}
my @size = $field->size;
my %extra = $field->extra;
my $list = $extra{'list'} || [];
- # \todo deal with embedded quotes
- my $commalist = join( ', ', map { qq['$_'] } @$list );
+ my $commalist = join( ', ', map { __PACKAGE__->_quote_string($_) } @$list );
if ( $data_type eq 'enum' ) {
$check = "CHECK ($field_name_q IN ($commalist))";
) {
$default = 'SYSDATE';
} else {
- $default = $default =~ m/null/i ? 'NULL' : "'$default'"
+ $default = $default =~ m/null/i ? 'NULL' : __PACKAGE__->_quote_string($default);
}
$field_def .= " DEFAULT $default",
push @field_defs, $field_def;
if ( my $comment = $field->comments ) {
- $comment =~ s/'/''/g;
+ $comment =~ __PACKAGE__->_quote_string($comment);
push @field_comments,
- "COMMENT ON COLUMN $table_name_q.$field_name_q is\n '" .
- $comment . "';" unless $options->{no_comments};
+ "COMMENT ON COLUMN $table_name_q.$field_name_q is\n $comment;"
+ unless $options->{no_comments};
}
return \@create, \@field_defs, \@trigger_defs, \@field_comments;
my $data_type = lc $field->data_type;
my %extra = $field->extra;
my $list = $extra{'list'} || [];
- # todo deal with embedded quotes
- my $commalist = join( ', ', map { qq['$_'] } @$list );
+ my $commalist = join( ', ', map { __PACKAGE__->_quote_string($_) } @$list );
if ($postgres_version >= 8.003 && $field->data_type eq 'enum') {
my $type_name = $extra{'custom_type_name'} || $field->table->name . '_' . $field->name . '_type';
#
# Default value
#
- SQL::Translator::Producer->_apply_default_value(
+ __PACKAGE__->_apply_default_value(
$field,
\$field_def,
[
if(ref $default_value eq "SCALAR" ) {
$default_value = $$default_value;
} elsif( defined $default_value && $to_dt =~ /^(character|text)/xsmi ) {
- $default_value =~ s/'/''/xsmg;
- $default_value = q(') . $default_value . q(');
+ $default_value = __PACKAGE__->_quote_string($default_value);
}
push @out, sprintf('ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s',
s1 set('a','b','c') default 'b',
e1 enum("a","b","c") default "c",
name varchar(30) default NULL,
- foo_type enum('vk','ck') NOT NULL default 'vk',
+ foo_type enum('vk','c''k') NOT NULL default 'vk',
date timestamp,
time_stamp2 timestamp,
foo_enabled bit(1) default b'0',
my $f8 = shift @fields;
is( $f8->name, 'foo_type', 'Eighth field name is "foo_type"' );
is( $f8->data_type, 'enum', 'Type is "enum"' );
- is( $f8->size, 2, 'Size is "2"' );
+ is( $f8->size, 3, 'Size is "2"' );
is( $f8->is_nullable, 0, 'Field cannot be null' );
is( $f8->default_value, 'vk', 'Default value is "vk"' );
is( $f8->is_primary_key, 0, 'Field is not PK' );
my %f8extra = $f8->extra;
- is( join(',', @{ $f8extra{'list'} || [] }), 'vk,ck', 'List is "vk,ck"' );
+ is( join(',', @{ $f8extra{'list'} || [] }), 'vk,c\'k', 'List is "vk,c\'k"' );
my $f9 = shift @fields;
is( $f9->name, 'date', 'Ninth field name is "date"' );
CREATE TABLE random (
id int auto_increment PRIMARY KEY,
foo varchar(255) not null default '',
+ bar enum('wibble','wo''bble'),
updated timestamp
);
CREATE UNIQUE INDEX random_foo_update ON random(foo,updated);
|;
BEGIN {
- maybe_plan(3,
+ maybe_plan(undef,
'SQL::Translator::Parser::MySQL',
'SQL::Translator::Producer::Oracle');
}
ok( $output, 'Translate MySQL to Oracle' );
ok( $output =~ /CREATE INDEX random_foo /, 'Normal index definition translated.');
ok( $output =~ /CREATE UNIQUE INDEX random_foo_update /, 'Unique index definition translated.');
+ok( $output =~ /\QCHECK (bar IN ('wibble', 'wo''bble'))\E/, 'Enum translated and escaped.');
+
+done_testing;
list:
- foo
- bar
- - baz
+ - ba'z
indices:
- type: NORMAL
fields:
list:
- foo
- bar
- - baz
+ - ba'z
indices:
- type: NORMAL
fields:
`id` integer NOT NULL,
`foo` integer NOT NULL,
`foo2` integer NULL,
- `bar_set` set('foo', 'bar', 'baz') NULL,
+ `bar_set` set('foo', 'bar', 'ba''z') NULL,
INDEX `index_1` (`id`),
INDEX `really_long_name_bigger_than_64_chars_aaaaaaaaaaaaaaaaa_aed44c47` (`id`),
INDEX (`foo`),
`id` integer NOT NULL,
`foo` integer NOT NULL,
`foo2` integer NULL,
- `bar_set` set('foo', 'bar', 'baz') NULL,
+ `bar_set` set('foo', 'bar', 'ba''z') NULL,
INDEX `index_1` (`id`),
INDEX `really_long_name_bigger_than_64_chars_aaaaaaaaaaaaaaaaa_aed44c47` (`id`),
INDEX (`foo`),
#=============================================================================
BEGIN {
- maybe_plan(57,
+ maybe_plan(undef,
'SQL::Translator::Producer::PostgreSQL',
'Test::Differences',
)
my $field5 = SQL::Translator::Schema::Field->new( name => 'enum_field',
table => $table,
data_type => 'enum',
- extra => { list => [ 'Foo', 'Bar' ] },
+ extra => { list => [ 'Foo', 'Bar', 'Ba\'z' ] },
is_auto_increment => 0,
is_nullable => 0,
is_foreign_key => 0,
is_unique => 0 );
-my $field5_sql = SQL::Translator::Producer::PostgreSQL::create_field($field5,{ postgres_version => 8.3 });
+my $field5_types = {};
+my $field5_sql = SQL::Translator::Producer::PostgreSQL::create_field(
+ $field5,
+ {
+ postgres_version => 8.3,
+ type_defs => $field5_types,
+ }
+);
is($field5_sql, 'enum_field mytable_enum_field_type NOT NULL', 'Create real enum field works');
-
-
-
+is_deeply(
+ $field5_types,
+ { mytable_enum_field_type =>
+ "DROP TYPE IF EXISTS mytable_enum_field_type CASCADE;\n" .
+ "CREATE TYPE mytable_enum_field_type AS ENUM ('Foo', 'Bar', 'Ba''z')"
+ },
+ 'Create real enum type works'
+);
my $field6 = SQL::Translator::Schema::Field->new(
name => 'character',
my $field13 = SQL::Translator::Schema::Field->new( name => 'enum_field_with_type_name',
table => $table,
data_type => 'enum',
- extra => { list => [ 'Foo', 'Bar' ],
+ extra => { list => [ 'Foo', 'Bar', 'Ba\'z' ],
custom_type_name => 'real_enum_type' },
is_auto_increment => 0,
is_nullable => 0,
is_foreign_key => 0,
is_unique => 0 );
-my $field13_sql = SQL::Translator::Producer::PostgreSQL::create_field($field13,{ postgres_version => 8.3 });
+my $field13_types = {};
+my $field13_sql = SQL::Translator::Producer::PostgreSQL::create_field(
+ $field13,
+ {
+ postgres_version => 8.3,
+ type_defs => $field13_types,
+ }
+);
is($field13_sql, 'enum_field_with_type_name real_enum_type NOT NULL', 'Create real enum field works');
+is_deeply(
+ $field13_types,
+ { real_enum_type =>
+ "DROP TYPE IF EXISTS real_enum_type CASCADE;\n" .
+ "CREATE TYPE real_enum_type AS ENUM ('Foo', 'Bar', 'Ba''z')"
+ },
+ 'Create real enum type works'
+);
{
";
is($drop_view_9_1_produced, $drop_view_9_1_expected, "My DROP VIEW statement for 9.1 is correct");
+
+done_testing;
use SQL::Translator::Generator::DDL::SQLServer;
use SQL::Translator::Schema::Field;
+use SQL::Translator::Schema::Table;
my $shim = SQL::Translator::Generator::DDL::SQLServer->new();
size => 10,
)), '[nice] varchar(10) NULL', 'sized field is generated correctly';
+my $table = SQL::Translator::Schema::Table->new(
+ name => 'mytable',
+);
+
+$table->add_field(
+ name => 'myenum',
+ data_type => 'enum',
+ extra => { list => [qw(foo ba'r)] },
+);
+
+like $shim->table($table),
+ qr/\b\QCONSTRAINT [myenum_chk] CHECK ([myenum] IN ('foo','ba''r'))\E/,
+ 'enum constraint is generated and escaped correctly';
+
done_testing;