[DBD::Pg] Fix client_encoding detection on pre-9.1 servers

[email protected] Sun, 8 Feb 2015 01:57:32 +0000
Newsgroups perl.dbd.pg.changes
Message-ID <[email protected]>
Committed by =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= <[email protected]>

Fix client_encoding detection on pre-9.1 servers

Before 9.1 the client_encoding provided in the connection parameter was
not normalised, so we need to do that ourselves.

This fixes the tests on SQL_ASCII databases, as well, so we can revert
the commits that disabled them (1f3ed13 and 53df98c).
---
 dbdimp.c           | 28 ++++++++++++++++++++++------
 t/12placeholders.t |  4 ++--
 t/30unicode.t      |  1 -
 3 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/dbdimp.c b/dbdimp.c
index 6a64487..2dee5d4 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -84,6 +84,7 @@ static int pg_st_deallocate_statement(pTHX_ SV *sth, imp_sth_t *imp_sth);
 static PGTransactionStatusType pg_db_txn_status (pTHX_ imp_dbh_t *imp_dbh);
 static int pg_db_start_txn (pTHX_ SV *dbh, imp_dbh_t *imp_dbh);
 static int handle_old_async(pTHX_ SV * handle, imp_dbh_t * imp_dbh, const int asyncflag);
+static void pg_db_detect_client_encoding_utf8(pTHX_ imp_dbh_t *imp_dbh);
 
 /* ================================================================== */
 void dbd_init (dbistate_t *dbistate)
@@ -224,9 +225,7 @@ int dbd_db_login6 (SV * dbh, imp_dbh_t * imp_dbh, char * dbname, char * uid, cha
 		}
 	}
 
-	imp_dbh->client_encoding_utf8 =
-		(0 == strncmp(PQparameterStatus(imp_dbh->conn, "client_encoding"), "UTF8", 4))
-		? DBDPG_TRUE : DBDPG_FALSE;
+	pg_db_detect_client_encoding_utf8(aTHX_ imp_dbh);
 
 	/* If the client_encoding is UTF8, flip the utf8 flag until convinced otherwise */
 	imp_dbh->pg_utf8_flag = imp_dbh->client_encoding_utf8;
@@ -928,9 +927,7 @@ int dbd_db_STORE_attrib (SV * dbh, imp_dbh_t * imp_dbh, SV * keysv, SV * valuesv
 			}
 			/* Do The Right Thing */
 			else if (-1 == imp_dbh->pg_enable_utf8) {
-				imp_dbh->client_encoding_utf8 =
-					(0 == strncmp(PQparameterStatus(imp_dbh->conn, "client_encoding"), "UTF8", 4))
-					? DBDPG_TRUE : DBDPG_FALSE;
+				pg_db_detect_client_encoding_utf8(aTHX_ imp_dbh);
 				imp_dbh->pg_enable_utf8 = -1;
 				imp_dbh->pg_utf8_flag = imp_dbh->client_encoding_utf8;
 			}
@@ -2952,6 +2949,25 @@ SV * pg_rightgraded_sv(pTHX_ SV *input, bool utf8) {
 	return utf8 ? pg_upgraded_sv(aTHX_ input) : pg_downgraded_sv(aTHX_ input);
 }
 
+static void pg_db_detect_client_encoding_utf8(pTHX_ imp_dbh_t *imp_dbh) {
+	char *clean_encoding;
+	int i, j;
+	const char * const client_encoding =
+		PQparameterStatus(imp_dbh->conn, "client_encoding");
+	STRLEN len = strlen(client_encoding);
+	Newx(clean_encoding, len + 1, char);
+	for (i = 0, j = 0; i < len; i++) {
+		const char c = toLOWER(client_encoding[i]);
+		if (isALPHANUMERIC_A(c))
+			clean_encoding[j++] = c;
+	};
+	clean_encoding[j] = '\0';
+	imp_dbh->client_encoding_utf8 =
+		(strnEQ(clean_encoding, "utf8", 4) || strnEQ(clean_encoding, "unicode", 8))
+		? DBDPG_TRUE : DBDPG_FALSE;
+	Safefree(clean_encoding);
+}
+
 /* ================================================================== */
 int pg_quickexec (SV * dbh, const char * sql, const int asyncflag)
 {
diff --git a/t/12placeholders.t b/t/12placeholders.t
index a470c76..548cd19 100644
--- a/t/12placeholders.t
+++ b/t/12placeholders.t
@@ -657,8 +657,8 @@ for my $char (qw{0 9 A Z a z}) { ## six letters
 
 SKIP: {
 	my $server_encoding = $dbh->selectrow_array('SHOW server_encoding');
-	skip "Cannot test non-ascii dollar quotes with server_encoding='$server_encoding' (need UTF8)", 3,
-		unless $server_encoding =~ /UTF8/;
+	skip "Cannot test non-ascii dollar quotes with server_encoding='$server_encoding' (need UTF8 or SQL_ASCII)", 3,
+		unless $server_encoding =~ /\A(?:UTF8|SQL_ASCII)\z/;
 
 	for my $ident (qq{\x{5317}}, qq{abc\x{5317}}, qq{_cde\x{5317}}) { ## hi-bit chars
 		eval {
diff --git a/t/30unicode.t b/t/30unicode.t
index 2d23c47..aa993b6 100644
--- a/t/30unicode.t
+++ b/t/30unicode.t
@@ -58,7 +58,6 @@ foreach (
 my %ranges = (
     UTF8 => qr/.*/,
     LATIN1 => qr/\A(?:ascii|latin 1 range)\z/,
-    SQL_ASCII => qr/nada/,
 );
 
 foreach (@tests) {
-- 
1.8.4