[DBD::Pg] Another stab at the UTF-8 system, this time simplified as much as possible. See the pod for pg_enable_utf8 for an explanation. Note that this commit will probably be picked out later, as we want to release a new minor version before releasing such a big change
dbdpg-commits-gENoVmVU/[email protected] Tue, 2 Jul 2013 00:35:08 -0400
| Newsgroups | gmane.comp.db.postgresql.dbdpg.cvs |
|---|---|
| Message-ID | <[email protected]> |
Committed by Greg Sabino Mullane <greg-O9fzpki4YnJWk0Htik3J/[email protected]> Another stab at the UTF-8 system, this time simplified as much as possible. See the pod for pg_enable_utf8 for an explanation. Note that this commit will probably be picked out later, as we want to release a new minor version before releasing such a big change --- Pg.pm | 25 ++++++++++++++--- dbdimp.c | 88 +++++++++++++++++++++++++++++++++++-------------------------- dbdimp.h | 6 +++- 3 files changed, 75 insertions(+), 44 deletions(-) diff --git a/Pg.pm b/Pg.pm index f04b1b9..7faf481 100644 --- a/Pg.pm +++ b/Pg.pm @@ -1633,6 +1633,7 @@ use 5.006001; pg_db => undef, pg_default_port => undef, pg_enable_utf8 => undef, + pg_utf8_flag => undef, pg_errorlevel => undef, pg_expand_array => undef, pg_host => undef, @@ -3128,12 +3129,26 @@ DBD::Pg specific attribute. Defaults to false. When true, question marks inside are not treated as L<placeholders|/Placeholders>. Useful for statements that contain unquoted question marks, such as geometric operators. -=head3 B<pg_enable_utf8> (boolean) +=head3 B<pg_enable_utf8> (integer) -DBD::Pg specific attribute. If true, then the C<utf8> flag will be turned on -for returned character data (if the data is valid UTF-8). For details about -the C<utf8> flag, see the C<Encode> module. This attribute is only relevant under -perl 5.8 and later. +DBD::Pg specific attribute. The behavior of DBD::Pg with regards to this flag has +changed as of version xxx. The default value for this attribute, -1, indicates +that the internal C<utf8> flag will be turned on for all strings coming back +from the database if the client_encoding is set to 'UTF8'. Use of this default +is highly encouraged, and you should not need to use this attribute except +for the following two conditions: + +If this attribute is set to 0, then the internal C<utf8> flag will *never* be +turned on for returned data, regardless of the current client_encoding. + +If this attribute is set to -1, then the internal C<utf8> flag will *always* +be turned on for returned data, regardless of the current client_encoding +(with the exception of bytea data). + +Note that the value of client_encoding is only checked on connection time. If +you change the client_encoding to/from 'UTF8' after connecting, you can set +pg_enable_utf8 to -1 to force DBD::Pg to read in the new client_encoding and +act accordingly. =head3 B<pg_errorlevel> (integer) diff --git a/dbdimp.c b/dbdimp.c index 494033f..1f293e6 100644 --- a/dbdimp.c +++ b/dbdimp.c @@ -224,8 +224,15 @@ int dbd_db_login6 (SV * dbh, imp_dbh_t * imp_dbh, char * dbname, char * uid, cha } } - imp_dbh->pg_bool_tf = DBDPG_FALSE; - imp_dbh->pg_enable_utf8 = DBDPG_FALSE; + imp_dbh->client_encoding_utf8 = + (0 == strncmp(PQparameterStatus(imp_dbh->conn, "client_encoding"), "UTF8", 4)) + ? DBDPG_TRUE : DBDPG_FALSE; + + /* If the client_encoding is UTF8, flip the utf8 flag until convinced otherwise */ + imp_dbh->pg_utf8_flag = imp_dbh->client_encoding_utf8; + + imp_dbh->pg_enable_utf8 = -1; + imp_dbh->prepare_now = DBDPG_FALSE; imp_dbh->done_begin = DBDPG_FALSE; imp_dbh->dollaronly = DBDPG_FALSE; @@ -278,10 +285,8 @@ static void pg_error (pTHX_ SV * h, int error_num, const char * error_msg) sv_setpv(DBIc_STATE(imp_xxh), (char*)imp_dbh->sqlstate); /* Set as utf-8 */ -#ifdef is_utf8_string - if (imp_dbh->pg_enable_utf8) + if (imp_dbh->pg_utf8_flag) SvUTF8_on(DBIc_ERRSTR(imp_xxh)); -#endif if (TEND_slow) TRC(DBILOGFP, "%sEnd pg_error\n", THEADER_slow); @@ -725,10 +730,12 @@ SV * dbd_db_FETCH_attrib (SV * dbh, imp_dbh_t * imp_dbh, SV * keysv) retsv = newSViv((IV)imp_dbh->pg_protocol); break; - case 12: /* pg_INV_WRITE */ + case 12: /* pg_INV_WRITE pg_utf8_flag */ if (strEQ("pg_INV_WRITE", key)) retsv = newSViv((IV) INV_WRITE ); + else if (strEQ("pg_utf8_flag", key)) + retsv = newSViv((IV)imp_dbh->pg_utf8_flag); break; case 13: /* pg_errorlevel */ @@ -743,10 +750,8 @@ SV * dbd_db_FETCH_attrib (SV * dbh, imp_dbh_t * imp_dbh, SV * keysv) retsv = newSViv((IV) PGLIBVERSION ); else if (strEQ("pg_prepare_now", key)) retsv = newSViv((IV)imp_dbh->prepare_now); -#ifdef is_utf8_string else if (strEQ("pg_enable_utf8", key)) retsv = newSViv((IV)imp_dbh->pg_enable_utf8); -#endif break; case 15: /* pg_default_port pg_async_status pg_expand_array */ @@ -865,12 +870,33 @@ int dbd_db_STORE_attrib (SV * dbh, imp_dbh_t * imp_dbh, SV * keysv, SV * valuesv retval = 1; } -#ifdef is_utf8_string + /* + We don't want to check the client_encoding every single time we talk to the database, + so we only do it here, which allows people to signal DBD::Pg that something + may have changed, so could you please rescan client_encoding? + */ else if (strEQ("pg_enable_utf8", key)) { - imp_dbh->pg_enable_utf8 = newval!=0 ? DBDPG_TRUE : DBDPG_FALSE; + /* Technically, we only allow -1, 0, and 1 */ + imp_dbh->pg_enable_utf8 = newval; + + /* Never use the utf8 flag, no matter what */ + if (0 == imp_dbh->pg_enable_utf8) { + imp_dbh->pg_utf8_flag = DBDPG_FALSE; + } + /* Always use the flag, no matter what */ + else if (1 == imp_dbh->pg_enable_utf8) { + imp_dbh->pg_utf8_flag = DBDPG_TRUE; + } + /* Do The Right Thing */ + else { + imp_dbh->client_encoding_utf8 = + (0 == strncmp(PQparameterStatus(imp_dbh->conn, "client_encoding"), "UTF8", 4)) + ? DBDPG_TRUE : DBDPG_FALSE; + imp_dbh->pg_enable_utf8 = -1; + imp_dbh->pg_utf8_flag = imp_dbh->client_encoding_utf8; + } retval = 1; } -#endif break; case 15: /* pg_expand_array */ @@ -1084,10 +1110,8 @@ SV * dbd_st_FETCH_attrib (SV * sth, imp_sth_t * imp_sth, SV * keysv) TRACE_PQFNAME; fieldname = PQfname(imp_sth->result, fields); sv_fieldname = newSVpv(fieldname,0); -#ifdef is_utf8_string if (is_high_bit_set(aTHX_ (unsigned char *)fieldname, strlen(fieldname)) && is_utf8_string((unsigned char *)fieldname, strlen(fieldname))) SvUTF8_on(sv_fieldname); -#endif (void)av_store(av, fields, sv_fieldname); } } @@ -2677,14 +2701,9 @@ static SV * pg_destringify_array(pTHX_ imp_dbh_t *imp_dbh, unsigned char * input av_push(currentav, newSViv('t' == *string ? 1 : 0)); else { SV *sv = newSVpvn(string, section_size); -#ifdef is_utf8_string - if (imp_dbh->pg_enable_utf8) { - SvUTF8_off(sv); - if (is_high_bit_set(aTHX_ (unsigned char *)string, section_size) && is_utf8_string((unsigned char*)string, section_size)) { - SvUTF8_on(sv); - } + if (imp_dbh->pg_utf8_flag) { + SvUTF8_on(sv); } -#endif av_push(currentav, sv); } @@ -3490,23 +3509,18 @@ AV * dbd_st_fetch (SV * sth, imp_sth_t * imp_sth) } } } -#ifdef is_utf8_string - if (imp_dbh->pg_enable_utf8 && type_info) { - SvUTF8_off(sv); - switch (type_info->type_id) { - case PG_CHAR: - case PG_TEXT: - case PG_BPCHAR: - case PG_VARCHAR: - if (is_high_bit_set(aTHX_ value, value_len) && is_utf8_string((unsigned char*)value, value_len)) { - SvUTF8_on(sv); - } - break; - default: - break; + if (imp_dbh->pg_utf8_flag) { + /* + The only exception to our rule about setting utf8 if the client_encoding + is set to UTF8 is bytea. + */ + if (type_info && PG_BYTEA == type_info->type_id) { + SvUTF8_off(sv); + } + else { + SvUTF8_on(sv); } } -#endif } } @@ -3870,10 +3884,8 @@ int pg_db_getcopydata (SV * dbh, SV * dataline, int async) if (copystatus > 0) { sv_setpv(dataline, tempbuf); -#ifdef is_utf8_string - if (imp_dbh->pg_enable_utf8) + if (imp_dbh->pg_utf8_flag) SvUTF8_on(dataline); -#endif TRACE_PQFREEMEM; PQfreemem(tempbuf); } diff --git a/dbdimp.h b/dbdimp.h index 1310e16..6058ff1 100644 --- a/dbdimp.h +++ b/dbdimp.h @@ -30,13 +30,17 @@ struct imp_dbh_st { PGconn *conn; /* connection structure */ char *sqlstate; /* from the last result */ + bool pg_bool_tf; /* do bools return 't'/'f'? Set by user, default is 0 */ - bool pg_enable_utf8; /* should we attempt to make utf8 strings? Set by user, default is 0 */ bool prepare_now; /* force immediate prepares, even with placeholders. Set by user, default is 0 */ bool done_begin; /* have we done a begin? (e.g. are we in a transaction?) */ bool dollaronly; /* only consider $1, $2 ... as valid placeholders */ bool expand_array; /* transform arrays from the db into Perl arrays? Default is 1 */ bool txn_read_only; /* are we in read-only mode? Set with $dbh->{ReadOnly} */ + + int pg_enable_utf8; /* legacy utf8 flag: force utf8 flag on or off, regardless of client_encoding */ + bool pg_utf8_flag; /* are we currently flipping the utf8 flag on? */ + bool client_encoding_utf8; /* is the client_encoding utf8 last we checked? */ }; -- 1.7.1