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