Skip to content

Commit

Permalink
Fix handling of escaped quote characters and numeric literals in MySQL
Browse files Browse the repository at this point in the history
  • Loading branch information
ilmari committed Sep 20, 2014
1 parent b40a030 commit 168ae5f
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 27 deletions.
44 changes: 22 additions & 22 deletions lib/SQL/Translator/Parser/MySQL.pm
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,9 @@ bit:
string :
# MySQL strings, unlike common SQL strings, can be double-quoted or
# single-quoted, and you can escape the delmiters by doubling (but only the
# delimiter) or by backslashing.
# single-quoted.
/'(\\.|''|[^\\\'])*'/ |
/"(\\.|""|[^\\\"])*"/
# For reference, std sql str: /(?:(?:\')(?:[^\']*(?:(?:\'\')[^\']*)*)(?:\'))//
SQSTRING | DQSTRING
nonstring : /[^;\'"]+/
Expand Down Expand Up @@ -681,9 +678,8 @@ default_val :
$return = $item[2];
}
|
/default/i string
/default/i VALUE
{
$item[2] =~ s/^\s*'|'\s*$//g or $item[2] =~ s/^\s*"|"\s*$//g;
$return = $item[2];
}
|
Expand Down Expand Up @@ -856,26 +852,30 @@ DOUBLE_QUOTE: '"'
SINGLE_QUOTE: "'"
QUOTED_NAME : BACKTICK /(?:[^`]|``)+/ BACKTICK
{ my $val = $item[2]; $val =~ s/``/`/g; $return = $val }
| DOUBLE_QUOTE /(?:[^"]|"")+/ DOUBLE_QUOTE
{ my $val = $item[2]; $val =~ s/""/"/g; $return = $val }
| SINGLE_QUOTE /(?:[^']|''|\\')+/ SINGLE_QUOTE
{ my $val = $item[2]; $val =~ s/''/'/g; $return = $val }
QUOTED_NAME : BQSTRING
| SQSTRING
| DQSTRING
# MySQL strings, unlike common SQL strings, can have the delmiters
# escaped either by doubling or by backslashing.
BQSTRING: BACKTICK /(?:[^\\`]|``|\\.)*/ BACKTICK
{ ($return = $item[2]) =~ s/(\\[\\`]|``)/substr($1,1)/ge }
DQSTRING: DOUBLE_QUOTE /(?:[^\\"]|""|\\.)*/ DOUBLE_QUOTE
{ ($return = $item[2]) =~ s/(\\[\\"]|"")/substr($1,1)/ge }
SQSTRING: SINGLE_QUOTE /(?:[^\\']|''|\\.)*/ SINGLE_QUOTE
{ ($return = $item[2]) =~ s/(\\[\\']|'')/substr($1,1)/ge }
NAME: QUOTED_NAME
| /\w+/
VALUE : /[-+]?\.?\d+(?:[eE]\d+)?/
VALUE : /[-+]?\d*\.?\d+(?:[eE]\d+)?/
{ $item[1] }
| QUOTED_NAME
{
# remove leading/trailing quotes
my $val = $item[1];
$val =~ s/^['"]|['"]$//g;
$return = $val;
}
| /NULL/
| SQSTRING
| DQSTRING
| /NULL/i
{ 'NULL' }
# always a scalar-ref, so that it is treated as a function and not quoted by consumers
Expand Down
5 changes: 3 additions & 2 deletions lib/SQL/Translator/Producer.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ sub produce { "" }
## 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 (undef, $field, $field_ref, $exceptions, $munge_default) = @_;
my $default = $field->default_value;
$munge_default ||= sub { s/'/''/g };
return if !defined $default;

if ($exceptions and ! ref $default) {
Expand All @@ -41,7 +42,7 @@ sub _apply_default_value {
# we need to check the data itself in addition to the datatype, for basic safety
$$field_ref .= " DEFAULT $default";
} else {
$default =~ s/'/''/g;
$munge_default->() for $default;
$$field_ref .= " DEFAULT '$default'";
}

Expand Down
1 change: 1 addition & 0 deletions lib/SQL/Translator/Producer/MySQL.pm
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@ sub create_field
[
'NULL' => \'NULL',
],
sub { s/([\\'])/$1$1/g },
);

if ( my $comments = $field->comments ) {
Expand Down
4 changes: 2 additions & 2 deletions t/02mysql-parser.t
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ ok ($@, 'Exception thrown on invalid version string');
is( $f2->data_type, 'varchar', 'Type is "varchar"' );
is( $f2->size, 12, 'Size is "12"' );
is( $f2->is_nullable, 0, 'Field can not be null' );
is( $f2->default_value, "test single quotes like in you''re", "Single quote in default value is escaped properly" );
is( $f2->default_value, "test single quotes like in you're", "Single quote in default value is unescaped properly" );
is( $f2->is_primary_key, 0, 'Field is not PK' );

# this is more of a sanity test because the original sqlt regex for default looked for an escaped quote represented as \'
Expand All @@ -909,7 +909,7 @@ ok ($@, 'Exception thrown on invalid version string');
is( $f3->data_type, 'varchar', 'Type is "varchar"' );
is( $f3->size, 20, 'Size is "20"' );
is( $f3->is_nullable, 0, 'Field can not be null' );
is( $f3->default_value, "test single quotes escaped like you\\'re", "Single quote in default value is escaped properly" );
is( $f3->default_value, "test single quotes escaped like you're", "Single quote in default value is unescaped properly" );
is( $f3->is_primary_key, 0, 'Field is not PK' );
}

Expand Down
2 changes: 1 addition & 1 deletion t/data/roundtrip.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Created on Fri Aug 15 15:08:18 2003
<field name="explicitemptystring" size="0"
data_type="varchar" order="6" default_value="" />
<field name="emptytagdef" size="0"
data_type="varchar" order="7" default_value="" >
data_type="varchar" order="7" default_value="backslash \ single-quote '" >
<comments>Hello emptytagdef</comments>
</field>
<field name="another_id" size="10"
Expand Down

0 comments on commit 168ae5f

Please sign in to comment.