Godt nyt på udvikler-fronten: Tricky UTF-8-faldgrube bliver fikset i MySQL

19. juli 2017 kl. 06:235
Godt nyt på udvikler-fronten: Tricky UTF-8-faldgrube bliver fikset i MySQL
Illustration: Privatfoto.
MySQL er en populær database, men håndteringen af tegnsæt kan være tricky.
Artiklen er ældre end 30 dage
Manglende links i teksten kan sandsynligvis findes i bunden af artiklen.

Tegnsæt er en evig kilde til for tidlige grå hår hos udviklere, men med Unicode og UTF-8 burde det da efterhånden være nogenlunde til at gennemskue? Ikke helt, men vi nærmer os.

En udvikler hos konsulentfirmaet EverSQL måtte for nylig sande, at UTF-8 ikke altid var UTF-8 - i hvert fald når det gælder MySQL, skriver EverSQL i et blogindlæg.

For de uindviede er UTF-8 det tegnsæt, som bruges til at dække over tegn fra stort set alle alfabeter og understøtter også en lang række symboler og emojis. Men det optager plads. Helt bestemt fire bytes for hvert tegn, hvis man skal have det hele med.

Det har måske været årsagen til, at MySQL i sin tid valgte at kun at understøtte en delmængde af UTF-8 ved blot at bruge tre bytes i stedet for fire.

Artiklen fortsætter efter annoncen

Problemet for konsulenten fra EverSQL var, at en kunde havde problemer med, at data slet ikke blev skrevet i databasen, selvom SQL-forespørgslen så rigtig ud. Både inputtet og tabellen i databasen var UTF-8, så det burde ikke have været tegnsættet, der var problemet, men fejlmeddelelsen antydede noget andet.

Nanveforvirring

MySQL havde som sagt implementeret UTF-8 med blot tre bytes, men når man konfigurerede tabellen i databasen, valgte man det stadig under navnet 'UTF-8'.

Senere har MySQL tilføjet et tegnsæt med fire bytes som blev kaldt utf8mb4, og det gamle tegnsæt skiftede sådan set også navn til utf8mb3. Men af hensyn til bagudkompatibiliteten var navnet 'UTF-8' i konfigurationen stadig det samme som utf8mb3.

Så når man som i dette tilfælde stod med en applikation, som sendte fire byte tegn til en tabel, der forventede op til tre bytes pr. tegn, så gik det galt.

Artiklen fortsætter efter annoncen

Forvirringen hos EverSQL er ikke unik, hvis man ser på forskellige vejledninger til UTF-8 i MySQL på nettet. Den gode nyhed er, at med MySQL 8, som er på vej, bliver UTF-8 med fire bytes, utf8mb4, standardformatet i MySQL-tabeller, skriver MySQL's Morgan Tucker i et blogindlæg og et svar til EverSQL.

Samtidig vil utf8mb3 også blive sat som 'forældet', men vil stadig være understøttet af hensyn til bagudkompatibiliteten. Problemet er, at man skal genopbygge en tabel, hvis man skifter tegnsæt, både hvis man skifter fra Latin1, UTF-8 eller et andet tegnsæt til utf8mb4. Derfor vil mange foretrække at holde fast i tidligere tegnsæt.

Ifølge MySQL skulle der ikke være nogen problemer med ydelsen ved at skifte til det fire bytes-baserede utf8mb4.

5 kommentarer.  Hop til debatten
Denne artikel er gratis...

...men det er dyrt at lave god journalistik. Derfor beder vi dig overveje at tegne abonnement på Version2.

Digitaliseringen buldrer derudaf, og it-folkene tegner fremtidens Danmark. Derfor er det vigtigere end nogensinde med et kvalificeret bud på, hvordan it bedst kan være med til at udvikle det danske samfund og erhvervsliv.

Og der har aldrig været mere akut brug for en kritisk vagthund, der råber op, når der tages forkerte it-beslutninger.

Den rolle har Version2 indtaget siden 2006 - og det bliver vi ved med.

Debatten
Log ind eller opret en bruger for at deltage i debatten.
settingsDebatindstillinger
5
21. juli 2017 kl. 08:51

Windows er jeg usikker på, men det er vist noget med UTF-16, eller UCS-2?

Ja det er UCS-2, dvs. UTF-16 inden de udvidede Unicode til mere end 16 bit. Det har den ret uheldige effekt at du kan have Windows filnavne som ikke er lovlig Unicode (fx fordi de indeholder halve surrogate pairs) og som dermed ikke kan repræsenteres i fx standard UTF-8. Desuden er det, som Linux, normalization-følsomt så du kan have to filer med samme visuelle navn i et directory.

Mht. macOS så bliver det sådan med APFS, Apple's nye filsystem som allerede er rullet ud på iOS og som kommer med næste version af macOS, at det bliver normalformbevarende, dvs. det gemmer det filnavn du kommer med, men normalization-insensitive dvs. to filnavne der normaliserer til det samme betragtes som ens.

Mht. Linux er det bare en samling bytes uden anden semantik.

3
20. juli 2017 kl. 14:02

MacOS normaliserer filnavne med NFD (eller tæt på), så vidt jeg husker, efter jeg (som Joe, formoder jeg) var blevet ramt af problemet, da jeg en dag stod med en Linux webserver med to fuldstændigt ens filnavne i et directory: $ ls Det var Sørens.html Det var Sørens.html

Linux er i praksis vist ret ligeglad, men den gængse konvention er tilsyneladende at bruge NFC. MacOS er muligvis mere stringent med NFD. Så flytter man en tar-fil fra det ene sted til det andet (og/eller mounter et Mac-directory i en virtuel Linux måske), kan man altså uforvarende få nogle vældigt sjove resultater. Så jo, MacOS er "anderledes". (Man kan selvfølgelig også sige at det er Linux, der er anderledes.)

Windows er jeg usikker på, men det er vist noget med UTF-16, eller UCS-2?

2
19. juli 2017 kl. 11:04

Unicode normalization forms (http://unicode.org/reports/tr15/) er nu ikke implementeret anderledes i macOS end i andre systemer. Der er mulighed for at kode mange tegn med diakritika i fire forskellige normaliseringsformer, men macOS hælder ikke til nogen af dem - og det gør andre systemer nu heller ikke. Det er død-irriterende med forskellige realiseringer af det samme tegn, men her er macOS programmeringssproget Swift nu lidt smartere end andre sprog (i hvert fald dem jeg kender,) for alle fire former bliver her tolket ens. Man skal altid normalisere data man modtager, som du rigtigtnok skriver, og man gør nok bedst i at vælge NFC, selvom NFD giver nogle ekstra muligheder for at bruge regex.

1
19. juli 2017 kl. 10:10

Jeg begundte også at fælle grå hård, da jeg fandt ud af at der er noget der hedder UTF-8 formal form. Naturligvis har alle implementering er UTF-8 valgt den samme formal form, bortset fra Mac OS. Dette betyder fx at bogstavet å har forskellig binær værdi på Mac OS sammenlignet med alle andre.

I MySQL vil å være lig med å uanset platform, hvis du genner med UTF8_general_ci. Men å sendt fra mac og andre å'er er forskellige, hvis du gemmer som UTF8_binary_ci. Og filnavne er mac OS å'er altid forskellige fra andre å'er.

Derfor skal man huske ikke at bruge UTF8-binary_ci i MySQL og man skal også huske at normaliserer filnavne, hvis du lader en klient vælge navnet.