Então, eu tenho poucos servidores Debian com PostgreSQL nele. Historicamente, esses servidores e o PostgreSQL são localizados com o conjunto de caracteres Latin 9 e naquela época estava tudo bem. Agora temos que lidar com coisas como polonês, grego ou chinês, então mudá-lo se tornou um problema crescente.
Quando tentei criar um banco de dados UTF8, recebi a mensagem:
ERRO: a codificação UTF8 não corresponde à localidade fr_FR Detalhe: A configuração LC_CTYPE escolhida requer a codificação LATIN9.
Poucas vezes fiz uma pesquisa sobre o assunto com meu velho amigo Google, e tudo que encontrei foram alguns procedimentos supercomplicados como atualizar o Debian LANG
, recompilar o PostgreSQL com o charset correto, editar todas as LC_
variáveis do sistema e outras soluções obscuras. Então, por enquanto, deixamos essa questão de lado.
Recentemente, voltou novamente, os gregos querem as coisas e o latim 9 não quer. E enquanto eu estava analisando essa questão novamente, um colega veio até mim e disse: “Não, é fácil, olhe”.
Ele não editou nada, não fez truques de mágica, ele apenas fez esta consulta SQL:
CREATE DATABASE my_utf8_db
WITH ENCODING='UTF8'
OWNER=admin
TEMPLATE=template0
LC_COLLATE='C'
LC_CTYPE='C'
CONNECTION LIMIT=-1
TABLESPACE=pg_default;
E funcionou bem.
Na verdade, eu não sabia LC_CTYPE='C'
e fiquei surpreso que usar isso não estivesse nas primeiras soluções do Google e até mesmo no Stack Overflow. Olhei em volta e só encontrei uma menção na documentação do PostgreSQL.
Quando LC_CTYPE é C ou POSIX, qualquer conjunto de caracteres é permitido, mas para outras configurações de LC_CTYPE existe apenas um conjunto de caracteres que funcionará corretamente. Como a configuração LC_CTYPE é congelada pelo initdb, a aparente flexibilidade para usar diferentes codificações em diferentes bancos de dados de um cluster é mais teórica do que real, exceto quando você seleciona a localidade C ou POSIX (desativando assim qualquer reconhecimento de localidade real).
Então isso me fez pensar, isso é muito fácil, muito perfeito, quais são as desvantagens? E eu tenho dificuldade em encontrar uma resposta ainda. Então vou postar aqui:
tl; dr: Quais são as desvantagens de usar LC_CTYPE='C'
em uma localização específica? É ruim fazer isso? O que devo esperar para quebrar?
A documentação menciona a relação entre localidades e recursos SQL no Locale Support :
O primeiro item (ordem de classificação) é sobre
LC_COLLATE
e os outros parecem ser sobreLC_CTYPE
.LC_COLLATE
LC_COLLATE
afeta comparações entre strings. Na prática, o efeito mais visível é a ordem de classificação.LC_COLLATE='C'
(ouPOSIX
que é um sinônimo) significa que é a ordem dos bytes que conduz as comparações, enquanto uma localidade nolanguage_REGION
formulário significa que as regras culturais irão conduzir as comparações.Um exemplo com nomes em francês, executados de dentro de um banco de dados UTF-8:
Resultado:
béatrice
vem antesboris
de , porque o E acentuado se compara com O como se não fosse acentuado. É uma regra cultural.Isso difere do que acontece com uma
C
localidade:Resultado:
Agora os nomes com E acentuado são colocados no final da lista. A representação de byte
é
em UTF-8 é o hexadecimalC3 A9
e parao
ele é6f
.c3
é maior do que6f
isso naC
localidade,'béatrice' > 'boris'
.Não são apenas acentos. Existem regras mais complexas com hifenização, pontuação e caracteres estranhos como
œ
. Regras culturais estranhas são esperadas em cada localidade.Agora, se as strings a serem comparadas misturam idiomas diferentes, como quando se tem uma
firstname
coluna para pessoas de todo o mundo, pode ser que qualquer localidade em particular não deva dominar, de qualquer maneira, porque alfabetos diferentes para idiomas diferentes não foram projetados para serem ordenados uns contra os outros.Neste caso
C
é uma escolha racional, e tem a vantagem de ser mais rápida, pois nada supera comparações de bytes puros.LC_CTYPE
Definir
LC_CTYPE
como 'C' implica que C funciona comoisupper(c)
outolower(c)
fornece resultados esperados apenas para caracteres no intervalo US-ASCII (ou seja, até o ponto de código 0x7F em Unicode).Como as funções SQL como
upper()
,lower()
ouinitcap
são implementadas no Postgres em cima dessas funções libc, elas são afetadas por isso assim que houver caracteres não US-ASCII nas strings.Exemplo:
Para a
C
localidade,é
é tratado como um caractere não categorizável.Da mesma forma, resultados errados também são obtidos com expressões regulares:
Em referência à resposta aceita de Daniel sobre classificação usando agrupamentos, esteja ciente de que, se você estiver executando o PostgreSQL em um Mac, seu agrupamento preferido pode não funcionar como esperado devido a configurações inadequadas para alguns agrupamentos no nível do sistema operacional. Você pode ler mais sobre o assunto aqui:
http://www.postgresql.org/message-id/[email protected]
Este não é um problema específico do PostgreSQL, especificamente, mas sim um problema com a configuração padrão do Mac para configurações de agrupamento. Meu sistema atual está executando o PostgreSQL 9.3 no OS X El Capitan versão 10.11 e sofre com esse problema. Meu sistema retorna os mesmos resultados de consulta, independentemente de eu usar o agrupamento “fr_FR” ou “en_US”. Por exemplo:
Usando o agrupamento “fr_FR”:
Usando o agrupamento “en_US”:
No meu sistema, as configurações de agrupamento (no nível do sistema operacional) são as mesmas para “fr_FR” e “en_US”, conforme demonstrado no shell executando diff:
Espero que esta informação adicional seja útil para quem está lendo isso e está usando o PostgreSQL em um Mac que sofre desse problema.