Quel est l'impact de LC_CTYPE sur une base de données PostgreSQL?


25

Donc, j'ai quelques serveurs Debian avec PostgreSQL dessus. Historiquement, ces serveurs et PostgreSQL sont localisés avec le jeu de caractères Latin 9 et à l'époque c'était bien. Maintenant, nous devons gérer des choses comme le polonais, le grec ou le chinois, donc le changer devient un problème croissant.

Lorsque j'ai essayé de créer une base de données UTF8, j'ai reçu le message:

ERREUR: l'encodage UTF8 ne correspond pas aux paramètres régionaux fr_FR Détail: le paramètre LC_CTYPE choisi nécessite l'encodage LATIN9.

À quelques reprises, j'ai fait des recherches sur le sujet avec mon ancien copain Google, et tout ce que j'ai pu trouver était des procédures trop compliquées comme la mise à jour de Debian LANG, la recompilation de PostgreSQL avec le jeu de caractères correct, l'édition de toutes les LC_variables système et d'autres solutions obscures. Donc pour l'instant, nous laissons cette question de côté.

Récemment, il est revenu, les Grecs veulent le truc et le Latin 9 ne veut pas. Et pendant que j'examinais à nouveau cette question, un collègue est venu me voir et m'a dit: «Non, c'est facile, regardez.»

Il n'a rien édité, n'a pas fait de tours de magie, il a juste fait cette requête 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;

Et cela a bien fonctionné.

En fait, je ne savais pas LC_CTYPE='C'et j'étais surpris que l'utilisation de ce n'était pas sur les premières solutions sur Google et même sur Stack Overflow. J'ai regardé autour de moi et je n'ai trouvé qu'une mention sur la documentation PostgreSQL.

Lorsque LC_CTYPE est C ou POSIX, tout jeu de caractères est autorisé, mais pour les autres paramètres de LC_CTYPE, il n'y a qu'un seul jeu de caractères qui fonctionnera correctement. Étant donné que le paramètre LC_CTYPE est figé par initdb, la flexibilité apparente d'utiliser différents codages dans différentes bases de données d'un cluster est plus théorique que réelle, sauf lorsque vous sélectionnez les paramètres régionaux C ou POSIX (désactivant ainsi toute prise de conscience des paramètres régionaux réels).

Alors ça m'a fait me demander, c'est trop facile, trop parfait, quels sont les inconvénients? Et j'ai encore du mal à trouver une réponse. Alors là je viens poster ici:

tl; dr: Quels sont les inconvénients de l'utilisation LC_CTYPE='C'sur une localisation spécifique? Est-ce mauvais de le faire? À quoi dois-je m'attendre pour casser?

Réponses:


26

Quels sont les inconvénients de l'utilisation de LC_CTYPE = 'C' sur une localisation spécifique

La documentation mentionne la relation entre les paramètres régionaux et les fonctionnalités SQL dans la prise en charge des paramètres régionaux :

Les paramètres régionaux influencent les fonctionnalités SQL suivantes:

  • Ordre de tri dans les requêtes à l'aide de ORDER BY ou des opérateurs de comparaison standard sur les données textuelles

  • Les fonctions supérieure, inférieure et initcap

  • Opérateurs d'appariement de modèles (LIKE, SIMILAR TO et expressions régulières de style POSIX); les paramètres régionaux affectent à la fois la correspondance insensible à la casse et la classification des caractères par expressions régulières de classe de caractères

  • La famille de fonctions to_char

  • La possibilité d'utiliser des index avec des clauses LIKE

Le premier élément (ordre de tri) concerne LC_COLLATEet les autres semblent tous concerner LC_CTYPE.

LC_COLLATE

LC_COLLATEaffecte les comparaisons entre les chaînes. En pratique, l'effet le plus visible est l'ordre de tri. LC_COLLATE='C'(ou POSIXqui est un synonyme) signifie que c'est l'ordre des octets qui conduit les comparaisons, tandis qu'une locale dans le language_REGIONformulaire signifie que les règles culturelles conduiront les comparaisons.

Un exemple avec des noms français, exécuté depuis l'intérieur d'une base de données UTF-8:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
 AS l(firstname)
order by firstname collate "fr_FR";

Résultat:

 Prénom 
-----------
 béatrice
 bérénice
 bernard
 boris

béatricevient avant boris, parce que le E accentué se compare à O comme s'il n'était pas accentué. C'est une règle culturelle.

Cela diffère de ce qui se passe avec un Cenvironnement local:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris')) 
 AS l(firstname)
order by firstname collate "C";

Résultat:

 Prénom 
-----------
 bernard
 boris
 béatrice
 bérénice

Maintenant, les noms avec E accentué sont poussés à la fin de la liste. La représentation en octets de éUTF-8 est l'hexadécimal C3 A9et pour oça 6f. c3est supérieure 6fen vertu de la Clocale, 'béatrice' > 'boris'.

Ce ne sont pas que des accents. Il y a des règles plus complexes avec césure, ponctuation et caractères étranges comme œ. Des règles culturelles étranges sont à prévoir dans chaque région.

Maintenant, si les chaînes à comparer se produisent pour mélanger différentes langues, comme lorsque vous avez une firstnamecolonne pour des personnes de tous les autres pays, il se peut que n'importe quel lieu particulier ne domine pas, de toute façon, car différents alphabets pour différentes langues n'ont pas été conçus pour être triés les uns contre les autres.

Dans ce cas, Cc'est un choix rationnel, et il a l'avantage d'être plus rapide, car rien ne peut battre les comparaisons d'octets purs.

LC_CTYPE

La LC_CTYPEdéfinition de «C» implique que les fonctions C aiment isupper(c)ou ne tolower(c)donnent les résultats attendus que pour les caractères dans la plage US-ASCII (c'est-à-dire jusqu'au point de code 0x7F en Unicode).

Parce que les fonctions SQL LIKE upper(), lower()ou initcap sont mises en œuvre Postgres au - dessus de ces fonctions libc, ils sont touchés par ce que dès qu'il ya des caractères non US-ASCII dans des chaînes.

Exemple:

test=> show lc_ctype;
  lc_ctype   
-------------
 fr_FR.UTF-8
(1 row)

-- Good result
test=> select initcap('élysée');
 initcap 
---------
 Élysée
(1 row)

-- Wrong result
-- collate "C" is the same as if the db has been created with lc_ctype='C'
test=> select initcap('élysée' collate "C");
 initcap 
---------
 éLyséE
(1 row)

Pour les Cparamètres régionaux, éest traité comme un caractère non catégorisable.

De même, des résultats erronés sont également obtenus avec des expressions régulières:

test=> select 'élysée' ~ '^\w+$';
 ?column? 
----------
 t
(1 row)

test=> select 'élysée' COLLATE "C" ~ '^\w+$';
 ?column? 
----------
 f
(1 row)

Donc, si je comprends bien, nous aurions le problème de commande même si vous avez créé un serveur UTF-8? Je suppose qu'avoir le système LC_CTYPE réglé sur UTF-8, ou compiler PostgreSQL en UTF-8 entraînera le même problème de comparaison que vous le signalez.
Gregoire D.

Pour aller plus loin, serait-il possible de forcer l'assemblage des requêtes afin que la comparaison soit localement correcte?
Gregoire D.

Oui, les comparaisons de chaînes individuelles peuvent intégrer leurs propres règles de classement, comme je le fais dans cette réponse avec l' collate "C"after the order by. C'est à vous de déterminer si et où votre application en a besoin. La plupart des applications ne se soucient pas vraiment.
Daniel Vérité

1
Notez également que les colonnes individuelles peuvent avoir un COLLATEspécificateur différent de celui de la base de données.
Daniel Vérité

2
Cette réponse est vraiment pour LC_COLLATE, pas LC_CTYPE. LC_CTYPE est utilisé pour décider si un caractère est un chiffre, une lettre, un espace, une ponctuation, etc.
jjanes

10

En référence à la réponse acceptée par Daniel à propos du tri à l'aide de classements, veuillez noter que si vous exécutez PostgreSQL sur un Mac, votre classement préféré peut ne pas fonctionner comme prévu en raison de paramètres inadéquats pour certains classements au niveau du système d'exploitation. Vous pouvez en savoir plus sur le problème ici:

http://www.postgresql.org/message-id/4B4E845F.80906@postnewspapers.com.au

Ce n'est pas un problème spécifique à PostgreSQL, en particulier, mais plutôt un problème avec la configuration par défaut de Mac pour les paramètres de classement. Mon système actuel exécute PostgreSQL 9.3 sur OS X El Capitan version 10.11 et souffre de ce problème. Mon système renvoie les mêmes résultats de requête, que j'utilise le classement «fr_FR» ou «en_US». Par exemple:

Utilisation du classement «fr_FR»:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "fr_FR";

results:
==============
bernard
boris
béatrice
bérénice

Utilisation du classement «en_US»:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "en_US";

results:
==============
bernard
boris
béatrice
bérénice

Sur mon système, les paramètres de classement (au niveau du système d'exploitation) sont les mêmes pour «fr_FR» et «en_US» comme démontré dans le shell en exécutant diff:

cd /usr/share/locale
diff fr_FR.UTF-8/LC_COLLATE en_US.UTF-8/LC_COLLATE

J'espère que ces informations supplémentaires seront utiles à toute personne lisant ceci qui utilise PostgreSQL sur un Mac qui souffre de ce problème.


Comment puis-je le faire fonctionner sur les Mac modernes. Avez-vous fait quelque chose pour le faire fonctionner sur votre Mac?
Dinesh Kumar du
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.