UTF8, PostgreSQL a Perl DBI

Z PostgreSQL
Skočit na navigaci Skočit na vyhledávání

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