Je bloque toujours sur l'utilisation abusive faite du varchar2 et de manière générale du stockage de données de type caractères. Tout est stocké n'importe comment et après le public (utilisateurs) se retrouve avec des données pas très jolies à regarder.
Ne blamons pas d'entrée le développeur, celui développe parfois des bons bugs dans les règles de l'art. L'administrateur de base de données est potentiellement un meilleur coupable.
L'utilisation illustrée ci-dessous a été réalisée sous une Oracle 10g R2. Oracle a assoupli depuis quelques temps, version 9, l'utilisation des types de données varchar2 (le type varchar devenant obsolète). il est possible de le déclarer non plus comme un tableau de n octets mais comme un tableau contenant n caractères.
Un exemple permet de dresser un portait du coupable :
Create table Toto (mytext Varchar2(200))
Tout va bien, vous venez de déclarer un attribut de la table comme varchar2 de 200.... octets ou char ? Vous allez dire peu importe : je vous invite à lire la suite et notamment le cas "tordu" ci-dessous. Pour le savoir, l'unité (char ou octet) employée par défaut va dépendre des paramètres NLS. Si vous avez un peu de chances, votre DBA a été bienveillant est à paramètrer le NLS_LENGTH_SEMANTICS en char et non en octets.
Le truc tordu arrive: imaginons que vous êtes amenés à stocker pour une très bonne raison des données caractères de langues exotico-européennes : bulgare, grecque, finnois. La liste est loin d'être exhaustive ! Vous vous retrouvez à gérer des données au format planétaire et universel : l'UTF8. J'entends déjà les experts critiqués le côté planétaire et universel de l'UTF8. Qu'il se rassure, il m'est arrivé de stocker du Klingon en UTF16.
L'UTF8 se comporte comme un jeux de caractères multi-octets sur les langues comme le grec, le bulgare etc. La grosse boulette vient du stockage en mode octet du varchar2. Vous vous attendez à stocker 200 caractères bulgares et vous vous retrouvez avec 200 octets en bulgare. Un caractère bulgare est représenté sur 2 octets... patatra, vous avez 100 caractères bulgares au lieu des 200 attendus.
Le cas peut encore être plus tordu si vous avez un attribut varchar2 taillé sur un nombre impair d'octets. Le dernier caractère est tronqué et par défaut remplacé par un caractère Blank d'un octet. Ca peut donner des situations folkloriques où le dernier mot spécifié sur le résumé d'un contrat est une insulte en bulgare.... Succès garanti à la clé en clientèle (situation purement hypothétique ne pouvant jamais arrivé ^^)
Pour parer à toute éventualité, quelques préconisations de bonne usage :
- Préférer le stockage en mode Char plutôt que l'octet, vous serez sur et certains du nombre de caractères que vous affichez (surtout en cas de mixe de jeux caractères anglais + bulgare)
- Oublier la mollesse : des déclarations fortes n'ont jamais tué personne : mytext Varchar2(200 CHAR)
Dans la suite du billet, je vais un peu blâmer le moteur de base Ora*** . Âme sensible s'abstenir.
La bonne blague arrive quand vous essayez de créer un attribut du style : {{ Create table Toto (mytext Varchar2(4000 CHAR))}}
Aucun problème, même avec une base en UTF8 !
La blague consiste à utiliser un outil d'alimentation UTF8-aware capable de balancer 4000 caractères bulgares dans cet attribut. Oracle explose avec une erreur grossière et abscon :
ORA-01461: cannot bind a LONG value for insert into a long column
Déjà d'entrée de jeu le message ne veut RIEN dire : qui parle de Long ? Pourquoi ne peut-on pas lier une valeur long avec un attribut long ? N'est-ce pas Oracle qui insiste pour que le monde arrête l'implémentation dans sa base de données du type long et le considère comme déprécié ? Les cores développeurs Oracle fument-ils du crack ou de l'herbe ?
Questions sans réponses pour le commun des mortels.
La faiblesse de l'engin, en l'occurence le moteur de base de données, vient d'une limite type "deus ex machina" ou "10 commandements" : le varchar2 ne peut faire plus de 4000 octets. Mais pourtant l'outil autorise la création d'un varchar2(4000 CHAR) et non 4000 BYTE sur une base avec jeu de caractères UTF8.
Incroyable mais vrai, plutôt que d'implémenter un principe du type qui peut le plus peut le moins, ils ont préféré choisir un principe du type le lièvre est aussi rapide que la tortue.
Grosso modo, ce qui prévaut c'est toujours la limité des 4000 octets. Point.
En bref, vous essayez de faire passer 4000 caractères bulgares : poids 8000 octets dans 4000. C'est le drame. D'autant plus avec le message déprimant parlant de Long.
A ce moment là, la ruse est de mise et il faut calculer à priori le poids de chaînes de caractères avant chargement (LengthB et consort). Un beau numéro de claquettes en perspective.
Le plus drôle c'est que la précédente "limite" (sur Oracle 8) était de 2000 octets (2 puissance 11) et maintenant 4000 (2 puissance 12). Ca m'impressionne vraiment la taille de leurs blocs au niveau de leur système interne de fichier. J'ai vaguement l'impression que des fois l'informatique progresse à pas de géant lilipucien.