Fix for 1087319: Quoter::serialize_list() doesn't handle multiple NULL values

This commit is contained in:
Brian Fraser
2012-12-12 16:16:14 -03:00
parent ea7fba8b8f
commit dcc66e8a32
2 changed files with 28 additions and 32 deletions

View File

@@ -161,16 +161,16 @@ sub serialize_list {
# the same. undef/NULL is a valid boundary value, however... # the same. undef/NULL is a valid boundary value, however...
return $args[0] if @args == 1 && !defined $args[0]; return $args[0] if @args == 1 && !defined $args[0];
# ... if there's an undef/NULL value and more than one value, return join ',', map {
# then we have no easy way to serialize the values into a list. my $c = $_;
# We can't convert undef to "NULL" because "NULL" is a valid if ( defined($c) ) {
# value itself, and we can't make it "" because a blank string $c =~ s/([^A-Za-z0-9])/\\$1/g;
# is also a valid value. In practice, a boundary value with $c
# two NULL values should be rare. }
die "Cannot serialize multiple values with undef/NULL" else {
if grep { !defined $_ } @args; '\\N'
}
return join ',', map { quotemeta } @args; } @args;
} }
sub deserialize_list { sub deserialize_list {
@@ -193,27 +193,16 @@ sub deserialize_list {
# the entire string represents the single element, so grab that. # the entire string represents the single element, so grab that.
push @escaped_parts, pos($string) ? substr( $string, pos($string) ) : $string; push @escaped_parts, pos($string) ? substr( $string, pos($string) ) : $string;
# Undo the quotemeta(). # Undo the escaping.
my @unescaped_parts = map { my @unescaped_parts = map {
my $part = $_; my $part = $_;
# Here be weirdness. Unfortunately quotemeta() is broken, and exposes if ($part eq '\\N') {
# the internal representation of scalars. Namely, the latin-1 range, undef
# \128-\377 (\p{Latin1} in newer Perls) is all escaped in downgraded }
# strings, but left alone in UTF-8 strings. Thus, this. else {
$part =~ s/\\([^A-Za-z0-9])/$1/g;
# TODO: quotemeta() might change in 5.16 to mean
# qr/(?=\p{ASCII})\W|\p{Pattern_Syntax}/
# And also fix this whole weird behavior under
# use feature 'unicode_strings' -- If/once that's
# implemented, this will have to change.
my $char_class = utf8::is_utf8($part) # If it's a UTF-8 string,
? qr/(?=\p{ASCII})\W/ # We only care about non-word
# characters in the ASCII range
: qr/(?=\p{ASCII})\W|[\x{80}-\x{FF}]/; # Otherwise,
# same as above, but also
# unescape the latin-1 range.
$part =~ s/\\($char_class)/$1/g;
$part; $part;
}
} @escaped_parts; } @escaped_parts;
return @unescaped_parts; return @unescaped_parts;

View File

@@ -150,6 +150,12 @@ is(
"Deserialize undef string", "Deserialize undef string",
); );
is(
$q->serialize_list(undef, undef),
'\\N,\\N',
"Serialize two undefs",
);
my @serialize_tests = ( my @serialize_tests = (
[ 'a', 'b', ], [ 'a', 'b', ],
[ 'a,', 'b', ], [ 'a,', 'b', ],
@@ -169,6 +175,8 @@ my @serialize_tests = (
[ '', '', '', ], [ '', '', '', ],
[ '' ], [ '' ],
[ undef ], [ undef ],
[ undef, undef, '', undef ],
[ '\\N', '\\\\N', undef ],
); );
use DSNParser; use DSNParser;
@@ -200,13 +208,12 @@ SKIP: {
# Bit of a hack, but we want to test both of Perl's internal encodings # Bit of a hack, but we want to test both of Perl's internal encodings
# for correctness. # for correctness.
local $dbh->{'mysql_enable_utf8'} = 1 if utf8::is_utf8($ser); local $dbh->{'mysql_enable_utf8'} = 1 if utf8::is_utf8($ser);
$sth->execute($test_index, $ser); $sth->execute($test_index, $ser);
$selsth->execute($test_index); $selsth->execute($test_index);
my $flat_string = "[" my $flat_string = "["
. join( "][", . join( "][",
map { defined($_) ? $_ : '' } @{$serialize_tests[$test_index]} map { defined($_) ? $_ : '<undef>' } @{$serialize_tests[$test_index]}
) )
. "]"; . "]";
$flat_string =~ s/\n/\\n/g; $flat_string =~ s/\n/\\n/g;