Skip Menu |

This queue is for tickets about the DBD-Pg CPAN distribution.

Report information
The Basics
Id: 60200
Status: resolved
Priority: 0/
Queue: DBD-Pg

People
Owner: Nobody in particular
Requestors: ilmari [...] ilmari.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: [PATCH] Support dequoting PostgreSQL 9.0 "hex" BYTEA output format
Date: Tue, 10 Aug 2010 01:45:02 +0100
To: bug-DBD-Pg [...] rt.cpan.org
From: Dagfinn Ilmari Mannsåker <ilmari [...] ilmari.org>
In PostgteSQL 9.0, the default BYTEA output format has changed from octal escapes for non-printable-ascii characters to encoding the whole value in hex (with a leading "\x" to distinguish the two). --- quote.c | 54 ++++++++++++++++++++++++++++++++++++++++++- t/06bytea.t | 73 ++++++++++++++++++++++++++++++++++------------------------ 2 files changed, 96 insertions(+), 31 deletions(-) diff --git a/quote.c b/quote.c index 3fe44bc..e925730 100644 --- a/quote.c +++ b/quote.c @@ -442,7 +442,7 @@ void dequote_string(const char *string, STRLEN *retlen, int estring) -void dequote_bytea(char *string, STRLEN *retlen, int estring) +static void _dequote_bytea_escape(char *string, STRLEN *retlen, int estring) { dTHX; char *result; @@ -482,6 +482,58 @@ void dequote_bytea(char *string, STRLEN *retlen, int estring) return; } +static int _decode_hex_digit(char digit) +{ + dTHX; + if (digit >= '0' && digit <= '9') + return digit - '0'; + if (digit >= 'a' && digit <= 'f') + return 10 + digit - 'a'; + if (digit >= 'A' && digit <= 'F') + return 10 + digit - 'A'; + + return -1; +} + +static void _dequote_bytea_hex(char *string, STRLEN *retlen, int estring) +{ + dTHX; + char *result; + + (*retlen) = 0; + + if (NULL == string) + return; + + result = string; + + while (*string != '\0') { + int digit1, digit2; + digit1 = _decode_hex_digit(*string); + digit2 = _decode_hex_digit(*(string+1)); + if (digit1 >= 0 && digit2 >= 0) { + *result++ = 16 * digit1 + digit2; + (*retlen)++; + } + string += 2; + } + *result = '\0'; + return; +} + +void dequote_bytea(char *string, STRLEN *retlen, int estring) +{ + dTHX; + + if (NULL == string) + return; + + if ('\\' == *string && 'x' == *(string+1)) + return _dequote_bytea_hex(string, retlen, estring); + else + return _dequote_bytea_escape(string, retlen, estring); +} + /* This one is not used in PG, but since we have a quote_sql_binary, it might be nice to let people go the other way too. Say when talking diff --git a/t/06bytea.t b/t/06bytea.t index 8bf4828..8e5ac53 100644 --- a/t/06bytea.t +++ b/t/06bytea.t @@ -17,7 +17,7 @@ my $dbh = connect_database(); if (! defined $dbh) { plan skip_all => 'Connection to database failed, cannot continue testing'; } -plan tests => 11; +plan tests => 16; isnt ($dbh, undef, 'Connect to database for bytea testing'); @@ -26,10 +26,6 @@ if ($pgversion >= 80100) { $dbh->do('SET escape_string_warning = false'); } -if ($pgversion >= 90000) { - $dbh->do(q{SET bytea_output = 'escape'}); -} - my ($sth, $t); $sth = $dbh->prepare(q{INSERT INTO dbd_pg_test (id,bytetest) VALUES (?,?)}); @@ -52,31 +48,48 @@ ok ($sth->execute(403, $binary_out), $t); $sth->{pg_server_prepare} = 1; ok ($sth->execute(404, $binary_out), $t); -$t='Received correct text from BYTEA column with backslashes'; -$sth = $dbh->prepare(q{SELECT bytetest FROM dbd_pg_test WHERE id=?}); -$sth->execute(400); -my $byte = $sth->fetchall_arrayref()->[0][0]; -is ($byte, 'aa\bb\cc\\\0dd\\', $t); - -$t='Received correct text from BYTEA column with quote'; -$sth->execute(402); -$byte = $sth->fetchall_arrayref()->[0][0]; -is ($byte, '\'', $t); - -$t='Ensure proper handling of high bit characters'; -$sth->execute(403); -($binary_in) = $sth->fetchrow_array(); -ok ($binary_in eq $binary_out, $t); -$sth->execute(404); -($binary_in) = $sth->fetchrow_array(); -ok ($binary_in eq $binary_out, $t); - -$t='quote properly handles bytea strings'; -my $string = "abc\123\\def\0ghi"; -my $result = $dbh->quote($string, { pg_type => PG_BYTEA }); -my $E = $pgversion >= 80100 ? q{E} : q{}; -my $expected = qq{${E}'abc\123\\\\\\\\def\\\\000ghi'}; -is ($result, $expected, $t); +my @output; +if ($pgversion >= 90000) { + @output = qw(hex escape); +} +else { + @output = (undef); + SKIP: { skip 'No BYTEA output format setting before 9.0', 5 } +} + +for my $output (@output) { + $dbh->do(qq{SET bytea_output = '$output'}) if $output; + + $t='Received correct text from BYTEA column with backslashes'; + $t.=" ($output output)" if $output; + $sth = $dbh->prepare(q{SELECT bytetest FROM dbd_pg_test WHERE id=?}); + $sth->execute(400); + my $byte = $sth->fetchall_arrayref()->[0][0]; + is ($byte, 'aa\bb\cc\\\0dd\\', $t); + + $t='Received correct text from BYTEA column with quote'; + $t.=" ($output output)" if $output; + $sth->execute(402); + $byte = $sth->fetchall_arrayref()->[0][0]; + is ($byte, '\'', $t); + + $t='Ensure proper handling of high bit characters'; + $t.=" ($output output)" if $output; + $sth->execute(403); + ($binary_in) = $sth->fetchrow_array(); + ok ($binary_in eq $binary_out, $t); + $sth->execute(404); + ($binary_in) = $sth->fetchrow_array(); + ok ($binary_in eq $binary_out, $t); + + $t='quote properly handles bytea strings'; + $t.=" ($output output)" if $output; + my $string = "abc\123\\def\0ghi"; + my $result = $dbh->quote($string, { pg_type => PG_BYTEA }); + my $E = $pgversion >= 80100 ? q{E} : q{}; + my $expected = qq{${E}'abc\123\\\\\\\\def\\\\000ghi'}; + is ($result, $expected, $t); +} $sth->finish(); -- 1.7.0.4
Subject: [PATCH] Support dequoting PostgreSQL 9.0 "hex" BYTEA output format
Date: Wed, 1 Sep 2010 00:23:34 +0100
To: bug-DBD-Pg [...] rt.cpan.org
From: Dagfinn Ilmari Mannsåker <ilmari [...] ilmari.org>
In PostgteSQL 9.0, the default BYTEA output format has changed from octal escapes for non-printable-ascii characters to encoding the whole value in hex (with a leading "\x" to distinguish the two). This also removes papering-over of the lack of dequoting from t/03dbmethod.t and extends t/06bytea.t to cover both output formats. --- quote.c | 54 ++++++++++++++++++++++++++++++++++++++++- t/03dbmethod.t | 16 +++++------- t/06bytea.t | 73 +++++++++++++++++++++++++++++++++----------------------- 3 files changed, 103 insertions(+), 40 deletions(-) diff --git a/quote.c b/quote.c index 3fe44bc..e925730 100644 --- a/quote.c +++ b/quote.c @@ -442,7 +442,7 @@ void dequote_string(const char *string, STRLEN *retlen, int estring) -void dequote_bytea(char *string, STRLEN *retlen, int estring) +static void _dequote_bytea_escape(char *string, STRLEN *retlen, int estring) { dTHX; char *result; @@ -482,6 +482,58 @@ void dequote_bytea(char *string, STRLEN *retlen, int estring) return; } +static int _decode_hex_digit(char digit) +{ + dTHX; + if (digit >= '0' && digit <= '9') + return digit - '0'; + if (digit >= 'a' && digit <= 'f') + return 10 + digit - 'a'; + if (digit >= 'A' && digit <= 'F') + return 10 + digit - 'A'; + + return -1; +} + +static void _dequote_bytea_hex(char *string, STRLEN *retlen, int estring) +{ + dTHX; + char *result; + + (*retlen) = 0; + + if (NULL == string) + return; + + result = string; + + while (*string != '\0') { + int digit1, digit2; + digit1 = _decode_hex_digit(*string); + digit2 = _decode_hex_digit(*(string+1)); + if (digit1 >= 0 && digit2 >= 0) { + *result++ = 16 * digit1 + digit2; + (*retlen)++; + } + string += 2; + } + *result = '\0'; + return; +} + +void dequote_bytea(char *string, STRLEN *retlen, int estring) +{ + dTHX; + + if (NULL == string) + return; + + if ('\\' == *string && 'x' == *(string+1)) + return _dequote_bytea_hex(string, retlen, estring); + else + return _dequote_bytea_escape(string, retlen, estring); +} + /* This one is not used in PG, but since we have a quote_sql_binary, it might be nice to let people go the other way too. Say when talking diff --git a/t/03dbmethod.t b/t/03dbmethod.t index 4ba5d3d..1d44289 100644 --- a/t/03dbmethod.t +++ b/t/03dbmethod.t @@ -1403,8 +1403,6 @@ $result = $dbh->pg_lo_unlink($object); ok (!$result, $t); $dbh->rollback(); -my $abctext = $pgversion >= 80500 ? 'x6162630a646566' : "abc\ndef"; - SKIP: { eval { @@ -1423,7 +1421,7 @@ SKIP: { $t='DB handle method "pg_lo_import" inserts correct data'; $SQL = "SELECT data FROM pg_largeobject where loid = $handle"; $info = $dbh->selectall_arrayref($SQL)->[0][0]; - is_deeply ($info, $abctext, $t); + is_deeply ($info, "abc\ndef", $t); $dbh->commit(); SKIP: { @@ -1445,7 +1443,7 @@ SKIP: { $t='DB handle method "pg_lo_import_with_oid" inserts correct data'; $SQL = "SELECT data FROM pg_largeobject where loid = $thandle"; $info = $dbh->selectall_arrayref($SQL)->[0][0]; - is_deeply ($info, $abctext, $t); + is_deeply ($info, "abc\ndef", $t); $t='DB handle method "pg_lo_import_with_oid" fails when given already used number'; eval { @@ -1559,7 +1557,7 @@ SKIP: { $sth = $dbh->prepare($SQL); $sth->execute($handle); $info = $sth->fetchall_arrayref()->[0][0]; - is_deeply ($info, $abctext, $t); + is_deeply ($info, "abc\ndef", $t); $t='DB handle method "pg_lo_import" works (AutoCommit on, begin_work called, no command)'; $dbh->begin_work(); @@ -1567,7 +1565,7 @@ SKIP: { ok ($handle, $t); $sth->execute($handle); $info = $sth->fetchall_arrayref()->[0][0]; - is_deeply ($info, $abctext, $t); + is_deeply ($info, "abc\ndef", $t); $dbh->rollback(); $t='DB handle method "pg_lo_import" works (AutoCommit on, begin_work called, no command, rollback)'; @@ -1586,7 +1584,7 @@ SKIP: { ok ($handle, $t); $sth->execute($handle); $info = $sth->fetchall_arrayref()->[0][0]; - is_deeply ($info, $abctext, $t); + is_deeply ($info, "abc\ndef", $t); $dbh->rollback(); $t='DB handle method "pg_lo_import" works (AutoCommit on, begin_work called, second command, rollback)'; @@ -1606,7 +1604,7 @@ SKIP: { ok ($handle, $t); $sth->execute($handle); $info = $sth->fetchall_arrayref()->[0][0]; - is_deeply ($info, $abctext, $t); + is_deeply ($info, "abc\ndef", $t); $t='DB handle method "pg_lo_import" works (AutoCommit not on, second command)'; $dbh->rollback(); @@ -1615,7 +1613,7 @@ SKIP: { ok ($handle, $t); $sth->execute($handle); $info = $sth->fetchall_arrayref()->[0][0]; - is_deeply ($info, $abctext, $t); + is_deeply ($info, "abc\ndef", $t); unlink $filename; $dbh->{AutoCommit} = 1; diff --git a/t/06bytea.t b/t/06bytea.t index 8bf4828..8e5ac53 100644 --- a/t/06bytea.t +++ b/t/06bytea.t @@ -17,7 +17,7 @@ my $dbh = connect_database(); if (! defined $dbh) { plan skip_all => 'Connection to database failed, cannot continue testing'; } -plan tests => 11; +plan tests => 16; isnt ($dbh, undef, 'Connect to database for bytea testing'); @@ -26,10 +26,6 @@ if ($pgversion >= 80100) { $dbh->do('SET escape_string_warning = false'); } -if ($pgversion >= 90000) { - $dbh->do(q{SET bytea_output = 'escape'}); -} - my ($sth, $t); $sth = $dbh->prepare(q{INSERT INTO dbd_pg_test (id,bytetest) VALUES (?,?)}); @@ -52,31 +48,48 @@ ok ($sth->execute(403, $binary_out), $t); $sth->{pg_server_prepare} = 1; ok ($sth->execute(404, $binary_out), $t); -$t='Received correct text from BYTEA column with backslashes'; -$sth = $dbh->prepare(q{SELECT bytetest FROM dbd_pg_test WHERE id=?}); -$sth->execute(400); -my $byte = $sth->fetchall_arrayref()->[0][0]; -is ($byte, 'aa\bb\cc\\\0dd\\', $t); - -$t='Received correct text from BYTEA column with quote'; -$sth->execute(402); -$byte = $sth->fetchall_arrayref()->[0][0]; -is ($byte, '\'', $t); - -$t='Ensure proper handling of high bit characters'; -$sth->execute(403); -($binary_in) = $sth->fetchrow_array(); -ok ($binary_in eq $binary_out, $t); -$sth->execute(404); -($binary_in) = $sth->fetchrow_array(); -ok ($binary_in eq $binary_out, $t); - -$t='quote properly handles bytea strings'; -my $string = "abc\123\\def\0ghi"; -my $result = $dbh->quote($string, { pg_type => PG_BYTEA }); -my $E = $pgversion >= 80100 ? q{E} : q{}; -my $expected = qq{${E}'abc\123\\\\\\\\def\\\\000ghi'}; -is ($result, $expected, $t); +my @output; +if ($pgversion >= 90000) { + @output = qw(hex escape); +} +else { + @output = (undef); + SKIP: { skip 'No BYTEA output format setting before 9.0', 5 } +} + +for my $output (@output) { + $dbh->do(qq{SET bytea_output = '$output'}) if $output; + + $t='Received correct text from BYTEA column with backslashes'; + $t.=" ($output output)" if $output; + $sth = $dbh->prepare(q{SELECT bytetest FROM dbd_pg_test WHERE id=?}); + $sth->execute(400); + my $byte = $sth->fetchall_arrayref()->[0][0]; + is ($byte, 'aa\bb\cc\\\0dd\\', $t); + + $t='Received correct text from BYTEA column with quote'; + $t.=" ($output output)" if $output; + $sth->execute(402); + $byte = $sth->fetchall_arrayref()->[0][0]; + is ($byte, '\'', $t); + + $t='Ensure proper handling of high bit characters'; + $t.=" ($output output)" if $output; + $sth->execute(403); + ($binary_in) = $sth->fetchrow_array(); + ok ($binary_in eq $binary_out, $t); + $sth->execute(404); + ($binary_in) = $sth->fetchrow_array(); + ok ($binary_in eq $binary_out, $t); + + $t='quote properly handles bytea strings'; + $t.=" ($output output)" if $output; + my $string = "abc\123\\def\0ghi"; + my $result = $dbh->quote($string, { pg_type => PG_BYTEA }); + my $E = $pgversion >= 80100 ? q{E} : q{}; + my $expected = qq{${E}'abc\123\\\\\\\\def\\\\000ghi'}; + is ($result, $expected, $t); +} $sth->finish(); -- 1.7.0.4
This is now part of DBD::Pg 2.17.2