In questo articolo, voglio introdurre il supporto ICU in PostgreSQL, su cui ho lavorato per PostgreSQL versione 10, che apparirà entro la fine dell'anno.
Ordinamento
L'ordinamento è una funzionalità importante di un sistema di database. In primo luogo, gli utenti generalmente vogliono vedere i dati ordinati. Qualsiasi risultato di query che contiene più di una riga ed è destinato all'utilizzo da parte dell'utente finale probabilmente vorrà essere ordinato, solo per una migliore esperienza utente. In secondo luogo, gran parte della funzionalità interna di un sistema di database dipende dall'ordinamento dei dati o dalla disponibilità di dati ordinati. Gli indici B-tree sono un esempio ovvio. Gli indici BRIN hanno conoscenza dell'ordine. Il partizionamento dell'intervallo deve confrontare i valori. Unisci join dipende dall'input ordinato. L'idea comune a queste diverse tecniche è che, in parole povere, se hai ordinato i dati e sai cosa stai cercando, è molto più veloce individuare il luogo in cui dovrebbero essere trovati.
Ci sono due aspetti importanti per l'ordinamento. Uno è l'algoritmo di ordinamento. Questo è un argomento standard nell'informatica e molto lavoro è stato dedicato a PostgreSQL nel corso degli anni per perfezionare i vari algoritmi e metodi di ordinamento, ma non è quello di cui scriverò. L'altro è decidere in quale ordine dovrebbero essere le cose, che è ciò che chiamiamo confronto. In molti casi, questa scelta è ovvia. 1 viene prima di 2. FALSE viene prima di VERO … beh, qualcuno lo ha appena deciso arbitrariamente. A di solito viene prima di B. Ma quando si tratta di testi in linguaggio naturale, le cose si fanno interessanti. Esistono molti modi diversi per ordinare il testo e i metodi effettivi per fascicolare le stringhe di testo sono più complicati di quanto possa sembrare. Lingue diverse preferiscono ordinamenti diversi, ma anche all'interno di una lingua possono esserci variazioni per applicazioni diverse. E ci sono dettagli di cui preoccuparsi, come cosa fare per spazi bianchi, punteggiatura, differenze tra maiuscole e minuscole, segni diacritici e così via. Consulta l'algoritmo di confronto Unicode per ulteriori informazioni su questo.
Prima che la funzionalità ICU fosse impegnata, tutta questa funzionalità era facilitata dalla libreria C nel sistema operativo. PostgreSQL fondamentalmente passa semplicemente le stringhe a strcmp()
, strcoll()
, e simili e funziona con il risultato. Le librerie C nei vari sistemi operativi implementano le varie varianti e sfumature di confronto sopra menzionate a diversi livelli di funzionalità e qualità, in modo che PostgreSQL possa fare ciò che può fare il tuo sistema operativo.
Modifica delle regole di confronto
I problemi iniziano se il sistema operativo ha bisogno di modificare le regole di confronto che fornisce. Perché dovrebbero volerlo fare? Potrebbe essere che la precedente confronto fosse sbagliata e doveva essere aggiustata. Forse è stato pubblicato un nuovo standard per una lingua e la raccolta deve essere aggiornata per questo. Forse la rappresentazione interna delle regole di confronto e dei dati delle stringhe è stata modificata per motivi di prestazioni o perché era necessario implementare funzionalità aggiuntive. Per molti programmi, questo non è un problema. Potresti semplicemente vedere un output ordinato in modo leggermente diverso, se noti una differenza. Per un sistema di database, tuttavia, questo è un grosso problema. Come descritto sopra, PostgreSQL memorizza i dati ordinati negli indici e in altri luoghi e si basa sull'ordinamento per essere corretto. Se l'ordinamento non è corretto, una ricerca nell'indice potrebbe non trovare i dati effettivamente presenti. Oppure una scrittura su un indice scriverà in una posizione diversa. Oppure i dati vengono scritti o letti dalla partizione sbagliata. Ciò può portare a dati duplicati erroneamente o alla comparsa di una perdita di dati perché i dati non sono dove vengono cercati. In altre parole, può portare al danneggiamento dei dati e alla (apparente) perdita di dati.
Sfortunatamente, non c'era molto che potevamo fare finora. I sistemi operativi aggiornano le loro regole di confronto ogni volta che ne hanno voglia, forse come parte di un aggiornamento al pacchetto della libreria C. Non c'è modo di scoprirlo in modo ragionevole, o forse esaminando in dettaglio i pacchetti di aggiornamento. E anche allora, rifiuterai un aggiornamento importante della tua libreria C perché hai notato che le regole di confronto in alcune impostazioni locali che non stai utilizzando sono state modificate? Era una situazione molto scomoda.
Entra in terapia intensiva
Allora, da dove arriva la terapia intensiva? ICU, International Components for Unicode, è una libreria che fornisce servizi di internazionalizzazione e localizzazione, comprese le regole di confronto. Quindi, a questo proposito, è un'alternativa all'utilizzo delle funzionalità nella libreria C standard. La cosa bella è che l'ICU fornisce esplicitamente alcune garanzie sulla stabilità delle regole di confronto:
- Le regole di confronto non verranno modificate in modo incompatibile come parte di un aggiornamento di una versione minore.
- Un confronto ha una versione che può essere ispezionata e quando un confronto cambia in modo incompatibile, la versione cambia.
Per gli utenti di PostgreSQL, questo significherà in pratica:
- Gli aggiornamenti di routine dei pacchetti del sistema operativo non interferiranno con la validità dei dati ordinati. Dato che un
postgres
binary è collegato a una particolare versione principale dilibicu
, gli aggiornamenti di routine dei pacchetti del sistema operativo non finiranno conpostgres
essere collegato a una nuova versione principale dilibicu
, purché a) non aggiorni i pacchetti PostgreSQL, oppure b) i pacchetti PostgreSQL siano ancora collegati alla stessa versione principale di ICU di prima. I confezionatori dovranno fare attenzione a mantenerlo correttamente, ma in pratica non dovrebbe essere troppo problematico. - Quando gli aggiornamenti principali del pacchetto e del sistema operativo cambiano la versione di un confronto, abbiamo un modo per rilevarlo e avvisare l'utente. In questo momento ci limitiamo a mettere in guardia e offrire alcune linee guida e strumenti per sistemare le cose, ma in futuro potremmo perfezionarlo e automatizzarlo ulteriormente.
(Per renderlo più esplicito per i packager:in un ramo stabile del tuo sistema operativo, non dovresti cambiare la versione ICU principale a cui è collegato un determinato set di pacchetti PostgreSQL.)
Utilizzo di terapia intensiva
Per poterlo utilizzare, PostgreSQL deve essere compilato in modo esplicito con il supporto dell'ICU. Quando costruisci dal sorgente, usa ./configure --with-icu
insieme ad altre opzioni desiderate. Ci aspettiamo che anche la maggior parte dei principali pacchetti binari offra questo per impostazione predefinita. Al termine, le regole di confronto basate su ICU vengono offerte insieme alle regole di confronto basate su libc offerte dalle versioni precedenti. (Quindi la creazione con il supporto per ICU non rimuove il supporto per le regole di confronto libc; i due esistono insieme.) Controllare la documentazione per i dettagli su come selezionare un confronto basato su ICU rispetto a uno basato su libc. Ad esempio, se avevi specificato in precedenza
CREATE TABLE ... (... x text COLLATE "en_US" ...)
ora potresti farlo
CREATE TABLE ... (... x text COLLATE "en-x-icu" ...)
Questo dovrebbe darti più o meno lo stesso comportamento visibile all'utente di prima, tranne per il fatto che il tuo database sarà più a prova di futuro quando si tratta di aggiornamento. (Su Linux/glibc, l'ordinamento dovrebbe essere per lo più lo stesso, ma potrebbero esserci piccole differenze in alcuni dettagli. Se, tuttavia, stai utilizzando un sistema operativo la cui libreria C non supporta affatto le regole di confronto Unicode, come macOS o versioni precedenti di FreeBSD, allora questo sarà un grande cambiamento — in meglio.)
Attualmente, il supporto ICU è disponibile solo per le regole di confronto specificate in modo esplicito. Le regole di confronto predefinite in un database sono sempre fornite dalla libreria C. Affrontare questo è un progetto futuro.
Se aggiorni un tale database con pg_upgrade
ad esempio a una nuova installazione di PostgreSQL che è collegata a una versione principale più recente di ICU che ha cambiato la versione di confronto di quella confronto che stai utilizzando, riceverai un avviso e dovrai correggere ad esempio tutti gli indici che dipendono dal collazione. Le istruzioni per questo sono anche nella documentazione.
Chiavi abbreviate
Quindi questa modifica fornirà alcuni miglioramenti molto importanti per la robustezza a lungo termine di un sistema di database. Ma ICU è anche un miglioramento rispetto alla libreria C di sistema in altre aree.
Ad esempio, i B-tree di PostgreSQL possono memorizzare quelle che vengono chiamate chiavi abbreviate per migliorare le prestazioni e l'archiviazione. Per i tipi di dati di stringhe di testo, con la libreria C standard, calcoliamo queste chiavi abbreviate usando strxfrm()
funzione. Tuttavia, abbiamo appreso che molte librerie C hanno una varietà di bug e comportamenti scorretti che rendono questo approccio non affidabile. Quindi l'ottimizzazione delle chiavi abbreviate è attualmente disabilitata per i tipi di dati stringa. Con ICU, possiamo utilizzare le chiamate API equivalenti e calcolare le chiavi abbreviate in quello che riteniamo sia un modo affidabile e stabile. Quindi ci sono possibili miglioramenti delle prestazioni anche da questa mossa.
Altre regole di confronto
Oltre a questi miglioramenti interni di robustezza e prestazioni, ci sono anche alcune nuove funzionalità rivolte agli utenti.
Per alcune lingue, nella pratica potrebbe essere rilevante più di un ordinamento. (Questo potrebbe iniziare.) Un esempio è che per il tedesco esiste un ordinamento standard utilizzato per la maggior parte degli scopi e un ordinamento "rubrica" utilizzato per elenchi di nomi. La libreria C standard fornisce solo una di queste varianti (probabilmente la prima). Ma se vuoi scrivere un'applicazione che ordini correttamente, ad esempio, sia i nomi dei prodotti che i nomi dei clienti, devi essere in grado di utilizzarli entrambi.
Ad esempio, l'esempio di Wikipedia in tedesco può ora essere riprodotto con PostgreSQL:
CREATE TABLE names (name text); INSERT INTO names VALUES ('Göbel'), ('Goethe'), ('Goldmann'), ('Göthe'), ('Götz'); => SELECT name FROM names ORDER BY name COLLATE "de-u-co-standard-x-icu"; name ---------- Göbel Goethe Goldmann Göthe Götz => SELECT name FROM names ORDER BY name COLLATE "de-u-co-phonebk-x-icu"; name ---------- Göbel Goethe Göthe Götz Goldmann => SELECT name FROM names ORDER BY name COLLATE "de-AT-u-co-phonebk-x-icu"; name ---------- Goethe Goldmann Göbel Göthe Götz
(Con glibc, COLLATE "de_DE"
e COLLATE "de_AT"
restituisci infatti il primo ordine.)
Un modo interessante per combinare diverse funzionalità potrebbe essere quello di utilizzare i domini per modellare la differenza sopra menzionata tra nomi di prodotti e nomi di clienti:
CREATE DOMAIN product_name AS text COLLATE "de-u-co-standard-x-icu"; CREATE DOMAIN person_name AS text COLLATE "de-u-co-phonebk-x-icu";
(Questo è solo un esempio. Ovviamente puoi anche allegare quei COLLATE
clausole alle definizioni di colonna direttamente o utilizzarle nelle query.)
Ancora più collazioni
Infine, e questo è chiaramente ciò che il mondo stava aspettando, ora c'è un modo per ordinare correttamente gli emoji. Questo è essenziale per garantire che tutte le facce del tuo gatto siano nell'ordine corretto. Confronta
=# SELECT chr(x) FROM generate_series(x'1F634'::int, x'1F644'::int) AS _(x) ORDER BY chr(x) COLLATE "und-x-icu"; chr ----- 😴 😵 😶 😷 😸 😹 😺 😻 😼 😽 😾 😿 🙀 🙁 🙂 🙃 🙄
con
=# CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = 'und-u-co-emoji'); =# SELECT chr(x) FROM generate_series(x'1F634'::int, x'1F644'::int) AS _(x) ORDER BY chr(x) COLLATE "und-u-co-emoji-x-icu"; chr ----- 🙂 🙃 😶 🙄 😴 😷 😵 🙁 😺 😸 😹 😻 😼 😽 🙀 😿 😾
Sì, in realtà esiste uno standard al riguardo.
Altro in arrivo
Questo è solo l'inizio. ICU offre molte funzionalità in quest'area che non stiamo ancora esponendo tramite PostgreSQL. Sono disponibili opzioni per l'ordinamento senza distinzione tra maiuscole e minuscole, l'ordinamento senza distinzione tra accenti e la personalizzazione totale delle regole di confronto. Cerca quelli nelle future versioni di PostgreSQL.