ugrás a tartalomhoz

A MySQL nem ért magyarul, de még lehet rajta segíteni

Hojtsy Gábor · 2007. Jan. 4. (Cs), 01.16
A Weblabor adatbázisa vidáman él (korábbi verziókról való frissítés okán) latin-1-esnek beállított, de UTF-8-as adatokat tároló tábla beállításokkal, hiszen az adatbázis kiszolgálónak mindegy, hogy milyen kódolású táblában milyen adatokat tárolunk, egészen addig, amíg sorrendezni nem kell az adatokat, vagy (ezesetben sajnos) indexet nem kell készíteni hozzájuk. Itt jönnek ugyanis a galibák. Ha felismertetjük a MySQL szerverrel a valós magyar karaktereket, rögtön kiderül, hogy a program azt állítja, hogy tud magyarul, de mégsem.

A probléma egyrészt ott jelentkezik, hogy ha olyan adatokat szeretnénk sorrendezni, amit a MySQL valamiért azonosnak tekint. Ilyenkor nem fogunk helyes sorrendet kapni a magyar nyelv szabályai szerint. Ez azonban még a kisebb probléma. Nagyobb gond, hogy ha indexünk is van egy adott mezőre, akkor nem fogunk tudni beilleszteni olyan mezőket, amikkel "egyenértékű" már van az adatbázisban. Az egyenértékűséget itt a használt egybevetés alapján dönti el a MySQL, lásd Bártházi András cikkét a témában.

Sajnos mi is szembesültünk ezzel a problémával, ugyanis a Weblabor Drupal 4.6-ról 5.0-ra történő frissítése során az egyik kellemesnek szánt mellékhatás, hogy a kellően magas MySQL verziószámot látva a Drupal valós utf8 kezelésre téríti át az adatbázist. Nos, eközben meg is kaptuk az adatbázistól, hogy nem lehet "Nagy Peter" és "Nagy Péter" nevű felhasználónk, hiszen ezek az egybevetés szerint azonosak. Itt jelentkezett számunkra a hiba, bár nyilván nem csak a Nagy Péterek érintettek a problémában, a hibakereséshez ez is kiváló információt adott. Nem is kellett messzire menni, hiszen épp a napokban volt nálunk szó erről a problémáról.

A szálakat felgöngyölítve úgy tűnik, hogy még a 3.23-as vagy még korábbi MySQL verzióban egy magyar adta meg a jelenleg is használt magyar nyelvi egybevetése szabályokat a MySQL fejlesztői számára. Ugyanazokat az egybevetése szabályokat használja a MySQL függetlenül attól, hogy latin2 vagy utf8 alapú kódolást használunk. Mivel esetünkben az utf8 alapú kódolásnál jelentkezett a hiba, megnéztem, hogy mégis mi a konkrét probléma az egybevetéssel. Készítettem egy kis programot erre a célra:

<?php

// Az egymással ellenőrizni kívánt karakter csoportok
$chars = array(
  array('a', 'á', 'A', 'Á'),
  array('e', 'é', 'E', 'É'),
  array('i', 'í', 'I', 'Í'),
  array('o', 'ó', 'ö', 'ő', 'O', 'Ó', 'Ö', 'Ő'),
  array('u', 'ú', 'ü', 'ű', 'U', 'Ú', 'Ü', 'Ű'),
);

// Valamilyen felhasználóval csatlakozunk
mysql_connect('localhost', 'testcharset', 'testcharset');

// Minden karaktercsoportra CSV-t állítunk elő
foreach ($chars as $cf) {
  
  // A tömb struktúra alap feltöltése, fejléccel
  $results = array_fill(0, count($cf)+1, array_fill(0, count($cf)+1, ' '));
  $results[0] = array_merge(array(' '), $cf);
  
  // Karakterenként megyünk végig a csoport elemein
  $y = 0;
  while ($basechar = array_shift($cf)) {
    // Bal oldali fejléc
    $results[$y+1][0] = $basechar;
    // Minden karakter párra ellenőrzünk
    foreach ($cf as $x => $char) {
      $result = mysql_fetch_row(
        mysql_query(
          "SELECT _utf8 '$basechar' = _utf8 '$char' COLLATE utf8_hungarian_ci"
        )
      );
      $results[$y+1][$x+$y+2] = $row[0];
    }
    $y++;
  }
  // Kiírjuk az aktuális CSV szegmenst
  foreach ($results as $row) {
    print join(', ', $row) . "\n";
  }
}
Az egyszerű ellenőrző szkript célja, hogy kiderüljön: a gyakorlatban milyen problémákat okoz a hibás magyar összevetés. Ennek érdekében utf8 kódolással mentettem el a teszt fájlt, és mindkét karaktersorozatnál megadtam, hogy utf8 kódolással kezelje a MySQL, valamint kényszerítettem ezen belül a magyar összevetést (ez még András cikkében nem szerepelt, azóta jelent meg). Lássuk egy OpenOffice.org-ban színezett eredménytáblán, hogy mit kapunk:

A MySQL véleménye a magyar karakterek egyenértékűségéről

Az ábrán a zöld színűek a helyes sikeres egybevetések (kis és nagybetűkre nem érzékeny összehasonlítás lévén a kis és nagybetűk azonosnak tekintendők). A kék színek a helyes sikertelen egybevetéseket jelzik, ahol tehát nem szabad azonosnak tekinteni két karaktert, és a MySQL jól is viselkedik. A piros (sajnos többségben lévő) szín pedig a hibás egybevetéseket jelöli, tehát ahol nem szabadna azonosnak tekinteni a két karaktert a magyar helyesírás szabályai szerint, a MySQL mégis így tesz. Ez azt jelenti, hogy nem lehet például szurok és szúrok az értéke két sorban egy egyedinek definiált mezőnek, pedig ezek teljesen mást jelentenek a magyar nyelvben.

Mit lehet mégis tenni a probléma ellen? Gondolhatnánk, hogy ha hibás a magyar egybevetés, akkor próbálkozzunk az utf8_general_ci-vel. Átírva a fenti szkriptben az egybevetést legalább egységes képet kapunk: minden tesztelt esetben egyes értéket kapunk, azaz a MySQL ilyenkor minden magyar betűt minden ékezetes formájával és minden nagybetűs ékezetes formájával is azonosnak tekint. Így például nem lehet szúr és szűr egy egyedinek definiált mezőben, hiszen egybevetés szerint ezek azonosak (ugyanez a kettős még az utf8_hungarian_ci szerint lehetséges). A helyzet tehát legfeljebb annyiban jobb, hogy most már nincsenek kivételek, egységesen minden ékezetes karakter hibás.

Törekedhetünk viszont arra, hogy javuljon a helyzet! A MySQL álláspontját Peter Gulutzan foglalta össze a 12519-es MySQL hibajelentésben. Egyrészt linkeket ad mások hibajelentéseire, elismerve, hogy nem jó a MySQL működése ebben az esetben. Másrészt viszont lehetőleg hivatalos magyar álláspontra vár, azaz egy angol nyelvű hivatalos dokumentumra, ami a magyar helyesírás vonatkozó szabályait részletezi. Elvégre is a több kapcsolódó hibajelentést is beküldő magyar fejlesztők véleménye ugyanúgy csak vélemény, mint az eredeti magyar egybevetést beküldő álláspontja, akinek a fenti ábrából is láthatóan érdekes elképzelései voltak a magyar karakterek egyenértékűségéről. Ha már kijavítják a hibát, szeretnék a hivatalos szabályokat figyelembe venni. Ha minden jól megy, a MySQL valamikori későbbi verziójába kerül egy javított, más nevű egybevetés, és a jelenlegi helytelen magyar egybevetést idővel kiveszik a rendszerből. Ehhez elsősorban egy hivatalos dokumentumra lenne szükség, úgyhogy aki segíteni tud ebben, az ne habozzon!

Hogy a Weblabor számára mi lesz a rövid távú megoldás, az egyelőre nyitott kérdés. Lehetséges, hogy megpróbálunk együtt élni a MySQL adott hibájával, és néhány felhasználónkat név módosításra kell kérnünk az ütközések elkerülése érdekében. Lehetséges, hogy bináris egybevetést kell alkalmaznunk, aminél értelemszerűen ilyen problémák nem merülnek fel, hiszen ott csak a binárisan pontosan megegyező karaktersorozatok azonosak. Sajnos egyik sem túl csábító út.
 
1

Egybevetés

sajt · 2007. Jan. 4. (Cs), 20.06
Nem az lenne a legegyszerűbb, ha mindenki létrehozhatna ilyen egybevetéseket? PHP-ban pld. van ilyen.
A másik dolog pedig, hogy nem érdemes ékezetes karaktereket használni felhasználónevekben, még ha lehet is.
8

Nem érdemes

Joó Ádám · 2008. Május. 17. (Szo), 12.04
Nem érdemes magyarnak lenni.
2

További adalék az elinduláshoz

tms · 2007. Jan. 4. (Cs), 22.09
Felvettem a kapcsolatot Tímár Andrással az OpenOffice.org és a Firefox (és még sok más) honosítási projekt kulcsemberével. Az idézetek tőle származnak, engedéllyel. Időhiány miatt küldöm be én amiket mondott.

"Másrészt viszont lehetőleg hivatalos magyar álláspontra vár, azaz egy
angol nyelvű hivatalos dokumentumra, ami a magyar helyesírás vonatkozó
szabályait részletezi." - ez annyira tipikus!!!! Hivatalos legyen, meg
angolul is. De az első jöttment [...] gyerek kódját bevették szó nélkül,
csak a javításhoz kell hivataloskodni. Egyáltalán mi az, hogy
hivatalos?! Na mindegy."

"A magyar helyesírás szabályai" c. könyv megemlékezik a sorbarendezésról
a 14., 15. és 16. pontokban. Ez fenn van a neten is több helyen, meg
lehet nézni. Persze magyarul, de kb. 1 óra alatt le is lehet fordítani
angolra. Ennél hivatalosabb szabály e kérdésben nem nagyon van, bár
megemlékeznek bizonyos könyvtári katalogizáló szabványokról, amit nem
specifikálnak pontosabban."

Az érintett néhány fejezet (is) elérhető innen:
http://mek.niif.hu/01500/01547/index.phtml
Volna erre nyelvi szövegekhez értő vállalkozó?
Sajna, nyelvi-nyelvtani szövegek fordításában nem vagyok jó.

"De nem is ez a lényeg. A szabály a
sorbarendezésre vonatkozik, és vagy jó a mysql felhasználók céljaira,
vagy nem. Ha nem, mert pl. nem tetszik az, hogy aá eé ií oó öő uú és üű
párok egyenértékűek, akkor új szabályt kell létrehozni, ami a
felhasználóknak megfelel. El kell dönteni fejben, hogy mire kell ez a
szabály. Szavak abécébe rendezéséhez? Vagy máshoz? Attól függ, hogy jó
lesz-e a hivatalos szabály."

"A szabály szerint is egyforma az e és az é, de ilyen esetben egyértelmű,
hogy az e után jön az é. Az ICU-ban különböző szintű precedenciákat
lehet megadni, pl. e <<< é azt jelenti, hogy e ugyanaz, mint az é, de ha
csak az a különbség, akkor e után jön az é - ez a három kacsacsőr. Az
egy kacsacsőr meg azt jelenti, hogy valami mindig előrébb van, mint a másik."

"Meg lehet nézni, hogy más programok hogy oldották ezt meg. A glibc pl.
nem jól, azt ne nézzétek. Az IBM ICU-ban (icu.sf.net) viszont úgy van,
ahogy én jónak látom, már ami a szavak betűrendbe rendezését illeti.
Szerintem egy IBM ICU-féle megoldás elég meggyőző lehet a MySQL-esek
számára is."

http://www-306.ibm.com/software/globalization/icu/index.jsp
Szóval azt lehetne mondani a mysqles arcnak, h csinálja úgy ahogy az ICU az jó nekünk.
3

bináris mező

Hojtsy Gábor · 2007. Jan. 6. (Szo), 21.13
A problémát meg lehet kerülni például úgy, ha binárisnak vesszük a mezőt, de akkor a Geza és geza is két különböző felhasználói név lesz. Így az egyébként használt karakterkódolás lesz érvényben, viszont az egybevetést a rendszer nem használja fel ezen a mezőn, hanem bináris egybevetést alkalmaz. Például a probléma szemléltetése és megkerülése bináris mezővel:
mysql> set names utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> create table pelda (nev varchar(25), unique(nev));
Query OK, 0 rows affected (0.01 sec)

mysql> show create table pelda;
+-------+--------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                             |
+-------+--------------------------------------------------------------------------------------------------------------------------+
| pelda | CREATE TABLE `pelda` (
  `nev` varchar(25) default NULL,
  UNIQUE KEY `nev` (`nev`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 | 
+-------+--------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into pelda values ('almos');
Query OK, 1 row affected (0.00 sec)

mysql> insert into pelda values ('álmos');
ERROR 1062 (23000): Duplicate entry 'álmos' for key 1
mysql> insert into pelda values ('Almos');
ERROR 1062 (23000): Duplicate entry 'Almos' for key 1

mysql> create table pelda2 (nev varchar(25) binary, unique(nev));
Query OK, 0 rows affected (0.00 sec)

mysql> show create table pelda2;
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                                                  |
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| pelda2 | CREATE TABLE `pelda2` (
  `nev` varchar(25) character set utf8 collate utf8_bin default NULL,
  UNIQUE KEY `nev` (`nev`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 | 
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

mysql> insert into pelda2 values ('almos');
Query OK, 1 row affected (0.00 sec)

mysql> insert into pelda2 values ('Almos');
Query OK, 1 row affected (0.00 sec)

mysql> insert into pelda2 values ('álmos');
Query OK, 1 row affected (0.00 sec)

mysql> select * from pelda2;
+--------+
| nev    |
+--------+
| Almos  | 
| almos  | 
| álmos | 
+--------+
3 rows in set (0.00 sec)
4

Ez ennyire nem lenne probléma?

randomly · 2007. Jan. 17. (Sze), 22.22
Sziasztok!

Ez a magyar ékezetes dolog ennyire nem lenne gond?
Nagyon csend van a témában, vagy háttérben zajlanak a dolgok? :-)

rand
5

gordiusz

gitamp · 2007. Jan. 21. (V), 18.22
Én a magam részéről egyedi kulcsot csak olyan karakteres mezőre használok amelyek valóban kódszerűek (A-Z, 0-9, esetleg @,_,$,#). Még a kisbetű - nagybetű keveredést sem engedem meg egyedi kulcs esetén. A felhasználó által szabadon beírt szöveg elképesztő hibákra ad lehetőséget - nem csak az ékezeteknél. Találkoztam már nulla helyett o-betűvel, egy helyett két szóközzel, stb.

Így viszont az ékezet már csak esztétikai hibává válik.
6

fejlemenyek?

nevergone · 2007. Feb. 21. (Sze), 14.00
Nem igazan tudtam nyomon kovetni a fejlemenyeket, de erdekelne, hogy mi lett ennek az ugynek valami vegkifejlete? Kapott a MySQL olyan dokumentumot, vagy segitseget, ami alapjan varhato egy kesobbi valtozatban a magyar nyelvi sajatossagoknak megfelelo helyes egybevetesi szabaly megjelenese?
7

eredmény?

gex · 2007. Május. 29. (K), 16.52
mi lett - ha lett egyáltalán - ennek az eredménye? tud valaki ilyenről? ránézésre január 31. óta nem történt semmi.
9

Nem tűnik nem jónak

Balogh Tibor · 2008. Okt. 12. (V), 08.53