UTF8, PostgreSQL a Perl DBI
Implementace UTF8 je v Perlu docela komplikovaná a neočekávaně komplikovaný byl i přechod na UTF8. Problém na který jsem narazil, a kterému jsem věnoval poměrně hodně času, bylo náhodné chybné dekódování diakritiky a to i v situaci, kdy aplikace i databáze jely v UTF-8 a já jsem navíc potřeboval zpracovávat načtené řetězce v knihovnách, které braly v potaz UTF8 flag řetězců. Před třemi roky, kdy jsem psal aplikaci pro přenos čísel, tak jsem se trefil do přechodného období, kdy se redesignoval DBI ovladač Postgresu a dekódování fungovalo divně. A jak sleduji vývoj (říjen 2013), tak by se situace měla zlepšovat, nicméně v tuto chvíli jsou ještě s kódováním určité problémy (na Fedoře 20 se DBI chová úplně stejně).
Níže popsaný postup mi funguje a nepoškozuje diakritiku:
#!/usr/bin/env perl # # ručně vytvořte tabulku CREATE TABLE IF NOT EXISTS test_czech_tab(content varchar); TRUNCATE test_czech_tab; # z konzole spusťte příkaz INSERT INTO test_czech_tab VALUES('>JIHOMORAVSKÝ<'),('>Špindlerův Mlýn<'); # # use strict; use warnings; use DBI; use Encode; use Data::Dumper; use utf8; $dbh1 = DBI->connect("dbi:Pg:dbname=postgres","pavel","", {AutoCommit=>1,RaiseError=>1,PrintError=>1,pg_enable_utf8 => 0}); $dbh1->do("SET application_name TO 'czech check'"); #$dbh1->do("set client_encoding to 'utf8'"); $dbh1->do("INSERT INTO test_czech_tab VALUES('>TestL<')"); $dbh1->do("INSERT INTO test_czech_tab VALUES('>JIHOMORAVSKÝ<'),('>Špindlerův Mlýn<')"); my $stm1 = $dbh1->prepare( <<END ); SELECT count(*) FROM test_czech_tab WHERE content IN ('>JIHOMORAVSKÝ<','>Špindlerův Mlýn<') END my $stm2 = $dbh1->prepare( <<END ); SELECT content FROM test_czech_tab; END $stm1->execute(); my ($content); $stm1->bind_columns(\$content); die "cannot to execute query" if !$stm1->fetch(); die "unexpected result" if $content != 4; $stm2->execute(); my ($cnt); $stm2->bind_columns(\$cnt); while ($stm2->fetch()) { utf8::upgrade($cnt); print "$cnt\n"; print Encode::is_utf8($cnt) ? "je utf\n" : "neni utf\n"; print Dumper($cnt); } printf "tests are ok\n";
Problém je v polofunkčním pg_enable_utf8 => 1. Pokud se vyhodí, tak výše uvedený test zafunguje, ale interně nejsou řetězce načtené z databáze označené jako UTF a v některé funkce je mohou opět poškodit.
Očekávaný výstup je:
bash-4.1$ perl test.pl >JIHOMORAVSKÝ< je utf $VAR1 = ">JIHOMORAVSK\x{c3}\x{9d}<"; >Špindlerův Mlýn< je utf $VAR1 = ">\x{c5}\x{a0}pindler\x{c5}\x{af}v Ml\x{c3}\x{bd}n<"; >TestL< je utf $VAR1 = '>TestL<'; >JIHOMORAVSKÝ< je utf $VAR1 = ">JIHOMORAVSK\x{c3}\x{9d}<"; >Špindlerův Mlýn< je utf $VAR1 = ">\x{c5}\x{a0}pindler\x{c5}\x{af}v Ml\x{c3}\x{bd}n<"; tests are ok
Popisovaný problém by měl být odstraněn v nové (třetí) generaci DBI driveru http://blog.endpoint.com/2014/02/dbdpg-utf-8-perl-postgresql.html