En este artículo, quiero presentar la compatibilidad con ICU en PostgreSQL, en la que he trabajado para la versión 10 de PostgreSQL, que aparecerá más adelante este año.
Clasificación
La clasificación es una funcionalidad importante de un sistema de base de datos. Primero, los usuarios generalmente quieren ver los datos ordenados. Cualquier resultado de consulta que contenga más de una fila y esté destinado al consumo del usuario final probablemente querrá ordenarse, solo para una mejor experiencia del usuario. En segundo lugar, gran parte de la funcionalidad interna de un sistema de base de datos depende de la clasificación de datos o de la disponibilidad de datos clasificados. Los índices de árbol B son un ejemplo obvio. Los índices BRIN tienen conocimiento de orden. La partición de rango tiene que comparar valores. Las combinaciones de combinación dependen de la entrada ordenada. La idea que es común a estas diferentes técnicas es que, en términos generales, si ha ordenado los datos y sabe lo que está buscando, hace que sea mucho más rápido ubicar el lugar donde se debe encontrar.
Hay dos aspectos importantes en la clasificación. Uno es el algoritmo de clasificación. Este es un tema estándar en informática, y se ha invertido mucho trabajo en PostgreSQL a lo largo de los años para refinar los diversos algoritmos y métodos de clasificación, pero eso no es sobre lo que escribiré. La otra es decidir en qué orden deben estar las cosas, que es lo que llamamos colación. En muchos casos, esa elección es obvia. 1 viene antes que 2. FALSO viene antes que VERDADERO... bueno, alguien decidió arbitrariamente eso. A generalmente viene antes de B. Pero cuando se trata de texto en lenguaje natural, las cosas se ponen interesantes. Hay muchas formas diferentes de ordenar el texto, y los métodos reales para recopilar cadenas de texto son más complicados de lo que parece. Diferentes idiomas prefieren diferentes órdenes de clasificación, pero incluso dentro de un mismo idioma, puede haber variaciones para diferentes aplicaciones. Y hay detalles de los que preocuparse, como qué hacer con los espacios en blanco, la puntuación, las diferencias entre mayúsculas y minúsculas, los signos diacríticos, etc. Busque el Algoritmo de intercalación Unicode para obtener más información sobre esto.
Antes de que se comprometiera la función ICU, toda esta funcionalidad la facilitó la biblioteca C en el sistema operativo. PostgreSQL básicamente solo pasa cadenas a strcmp()
, strcoll()
, y similares y trabaja con el resultado. Las bibliotecas de C en los diversos sistemas operativos implementan las diversas variantes de intercalación y los matices mencionados anteriormente en diferentes niveles de funcionalidad y calidad, por lo que PostgreSQL puede hacer lo que su sistema operativo puede hacer.
Cambio de colaciones
Los problemas comienzan si el sistema operativo alguna vez necesita cambiar una intercalación que proporciona. ¿Por qué querrían hacer eso? Puede ser que la intercalación anterior haya sido incorrecta y haya que corregirla. Tal vez se publicó un nuevo estándar para un idioma y la intercalación debe actualizarse para eso. Tal vez la representación interna de la intercalación y los datos de cadena se modificó por motivos de rendimiento o porque era necesario implementar una funcionalidad adicional. Para muchos programas, esto no es un problema. Es posible que solo vea una salida ordenada ligeramente diferente, si nota alguna diferencia. Sin embargo, para un sistema de base de datos, este es un problema importante. Como se describió anteriormente, PostgreSQL almacena datos ordenados en índices y otros lugares y se basa en el orden de clasificación para que sea correcto. Si el orden de clasificación no es correcto, es posible que una búsqueda en el índice no encuentre los datos que realmente están allí. O escribir en un índice escribirá en un lugar diferente. O los datos se escriben o se leen en la partición incorrecta. Esto puede conducir a datos duplicados erróneamente o la apariencia de pérdida de datos porque los datos no están donde se buscan. En otras palabras, puede conducir a la corrupción de datos y la pérdida (aparente) de datos.
Desafortunadamente, no había mucho que pudiéramos hacer al respecto hasta ahora. Los sistemas operativos actualizan sus intercalaciones cuando lo desean, quizás como parte de una actualización de su paquete de biblioteca C. No hay forma de averiguar esto de una manera razonable, o tal vez inspeccionando los paquetes de actualización en detalle. E incluso entonces, ¿rechazará una actualización importante de su biblioteca C porque notó que se cambió la intercalación en algún lugar que no está usando? Fue una situación muy incómoda.
Ingresar a la UCI
Entonces, ¿dónde entra la UCI? ICU, International Components for Unicode, es una biblioteca que proporciona funciones de internacionalización y localización, incluida la intercalación. Entonces, en ese sentido, es una alternativa al uso de las instalaciones en la biblioteca C estándar. Lo bueno es que ICU proporciona explícitamente algunas garantías sobre la estabilidad de las colaciones:
- Una intercalación no se cambiará de manera incompatible como parte de una actualización de versión menor.
- Una intercalación tiene una versión, que se puede inspeccionar, y cuando una intercalación cambia de manera incompatible, la versión cambia.
Para los usuarios de PostgreSQL, esto significará en la práctica:
- Las actualizaciones rutinarias del paquete del sistema operativo no interferirán con la validez de los datos ordenados. Desde un
postgres
binario está vinculado a una versión principal particular delibicu
, las actualizaciones rutinarias del paquete del sistema operativo no terminarán conpostgres
estar vinculado a una nueva versión principal delibicu
, siempre que a) no actualice los paquetes de PostgreSQL, o b) los paquetes de PostgreSQL sigan vinculados a la misma versión principal de ICU que antes. Los empaquetadores deberán tener cuidado para mantener esto correctamente, pero eso no debería ser demasiado problemático en la práctica. - Cuando las actualizaciones importantes del sistema operativo y del paquete cambian la versión de una recopilación, tenemos una manera de detectarlo y advertir al usuario. En este momento, solo advertimos y ofrecemos algunas pautas y herramientas para solucionar problemas, pero en el futuro podríamos perfeccionar y automatizar esto aún más.
(Para hacer esto más explícito para los empaquetadores:en una rama estable de su sistema operativo, no debe cambiar la versión principal de ICU con la que está vinculado un conjunto de paquetes de PostgreSQL dado).
Uso de la UCI
Para poder usar esto, PostgreSQL debe construirse explícitamente con soporte ICU. Al construir desde la fuente, use ./configure --with-icu
junto con otras opciones deseadas. Esperamos que la mayoría de los principales paquetes binarios también ofrezcan esto de forma predeterminada. Cuando se hace esto, las intercalaciones basadas en ICU se ofrecen junto con las intercalaciones basadas en libc que ofrecían versiones anteriores. (Por lo tanto, compilar con compatibilidad con ICU no elimina la compatibilidad con la intercalación de libc; las dos existen juntas). Consulte la documentación para obtener detalles sobre cómo seleccionar una intercalación basada en ICU frente a una basada en libc. Por ejemplo, si previamente había especificado
CREATE TABLE ... (... x text COLLATE "en_US" ...)
ahora podrías hacerlo
CREATE TABLE ... (... x text COLLATE "en-x-icu" ...)
Esto debería brindarle aproximadamente el mismo comportamiento visible para el usuario que antes, excepto que su base de datos estará más preparada para el futuro cuando se trata de actualizar. (En Linux/glibc, el orden de clasificación debería ser casi el mismo, pero podría haber pequeñas diferencias en algunos detalles. Sin embargo, si está utilizando un sistema operativo cuya biblioteca C no admite la intercalación Unicode en absoluto, como macOS o versiones anteriores de FreeBSD, entonces este será un cambio importante, para mejor).
Actualmente, la compatibilidad con ICU solo está disponible para intercalaciones especificadas explícitamente. La biblioteca C siempre proporciona la intercalación predeterminada en una base de datos. Abordar esto es un proyecto de futuro.
Si actualiza dicha base de datos mediante pg_upgrade
por ejemplo, a una nueva instalación de PostgreSQL que está vinculada con una versión principal más nueva de ICU que ha cambiado la versión de intercalación de esa intercalación que está utilizando, recibirá una advertencia y tendrá que corregir, por ejemplo, cualquier índice que dependa de la colación. Las instrucciones para esto también se encuentran en la documentación.
Teclas abreviadas
Por lo tanto, este cambio proporcionará algunas mejoras muy importantes para la solidez a largo plazo de un sistema de base de datos. Pero ICU también es una mejora con respecto a la biblioteca del sistema C en otras áreas.
Por ejemplo, los árboles B de PostgreSQL pueden almacenar lo que se denominan claves abreviadas para mejorar el rendimiento y el almacenamiento. Para los tipos de datos de cadenas de texto, con la biblioteca C estándar, calcularíamos estas claves abreviadas usando strxfrm()
función. Sin embargo, hemos aprendido que muchas bibliotecas de C tienen una variedad de errores y malos comportamientos que hacen que este enfoque no sea confiable. Por lo tanto, la optimización de claves abreviadas está actualmente deshabilitada para los tipos de datos de cadena. Con ICU, podemos usar las llamadas API equivalentes y calcular claves abreviadas en lo que creemos que es una forma confiable y estable. Por lo tanto, también hay posibles mejoras de rendimiento a partir de este movimiento.
Más colaciones
Además de estas mejoras internas de robustez y rendimiento, también hay algunas funciones nuevas para el usuario.
Para algunos idiomas, más de un orden de clasificación puede ser relevante en la práctica. (Esto podría ayudarlo a comenzar). Un ejemplo es que para el alemán, hay un orden de clasificación estándar que se usa para la mayoría de los propósitos y un orden de clasificación de "guía telefónica" que se usa para listas de nombres. La biblioteca C estándar solo proporciona una de esas variantes (probablemente la primera). Pero si desea escribir una aplicación que clasifique correctamente, digamos, tanto los nombres de los productos como los nombres de los clientes, debe poder usar ambos.
Por ejemplo, el ejemplo de la Wikipedia en alemán ahora se puede reproducir 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"
y COLLATE "de_AT"
de hecho devolver el primer pedido.)
Una forma interesante de combinar varias funciones podría ser usar dominios para modelar la diferencia mencionada anteriormente entre los nombres de productos y los nombres de clientes:
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";
(Esto es solo un ejemplo. Por supuesto, también puede adjuntar esos COLLATE
cláusulas a definiciones de columna directamente o utilícelas en consultas).
Aún más intercalaciones
Finalmente, y esto es claramente lo que el mundo había estado esperando, ahora hay una forma de clasificar correctamente los emojis. Esto es esencial para garantizar que todas las caras de tu gato estén en el orden correcto. Comparar
=# 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í, en realidad hay un estándar sobre esto.
Más por venir
Este es solo el comienzo. ICU ofrece mucha funcionalidad en esta área que aún no estamos exponiendo a través de PostgreSQL. Hay opciones para la clasificación que no distingue entre mayúsculas y minúsculas, la clasificación que no distingue los acentos y la personalización total de una colación. Búsquelos en futuras versiones de PostgreSQL.