Introducción
En desarrollos con MariaDB, hay escenarios donde necesitás que un tipo de dato personalizado se comporte de manera distinta a como lo hace el motor de base de datos por defecto. En esta guía, vas a ver cómo sobreescribir el ordenamiento de un tipo de dato existente usando el framework Type_handler, incluso cuando el método que querés modificar está declarado como final en la clase base. Vas a implementar un ejemplo concreto con un tipo MONEY que, en lugar de ordenarse de menor a mayor, lo hará en orden inverso. Los conceptos aplican para cualquier tipo numérico o personalizado que necesite ajustes en su lógica de comparación y ordenamiento.
Al finalizar, vas a poder:
- Identificar qué métodos
finalimpiden la personalización directa - Implementar un tipo derivado de
Field_realque sobrescribasort_string() - Compilar y probar el cambio en un entorno controlado
Qué es y para qué sirve
El framework Type_handler en MariaDB permite extender o modificar el comportamiento de los tipos de dato nativos sin tocar el código central del motor. Cada tipo de dato en MariaDB está representado por una clase (ej: Field_double, Field_real) que hereda de clases base abstractas o concretas. Algunas de estas clases tienen métodos marcados como final, lo que significa que no pueden ser sobrescritos en clases derivadas.
En este artículo, nos enfocamos en dos desafíos técnicos clave:
- Tipos derivados obligatorios: Cuando un método
finalbloquea la modificación directa (ej:Field_double::sort_string()), necesitás derivar de una clase base más abstracta (ej:Field_real) e implementar métodos que normalmente heredarías. - Sobreescritura de lógica de ordenamiento: Modificar cómo se ordenan los valores de una columna usando
sort_string(), que define el string utilizado para comparaciones de ordenamiento.
El ejemplo práctico usa un tipo MONEY personalizado, pero los pasos son aplicables a cualquier tipo que necesite ajustar su comportamiento de ordenamiento o comparación en consultas ORDER BY, GROUP BY o índices.
Prerequisitos
Antes de empezar, asegurate de cumplir con estos requisitos técnicos:
| Requisito | Versión/Configuración | Notas |
|---|---|---|
| **MariaDB** | 10.5 o superior | Versiones anteriores pueden no soportar Type_handler correctamente |
| **Compilador** | GCC 9+ o Clang 10+ | Necesario para compilar MariaDB desde código fuente |
| **Entorno** | Linux (recomendado Ubuntu 20.04+) | Windows requiere WSL o entorno similar |
| **Permisos** | Usuario con permisos de BLOCK35 | Para instalar dependencias y compilar |
| **Repositorio** | Clonar el repo de ejemplo | Usaremos el branch BLOCK36 del [repo oficial de MariaDB](https://github.com/MariaDB/server) |
| **Herramientas** | Git, CMake (3.16+), Ninja (opcional) | Para clonar y compilar el código fuente |
| **Memoria** | Mínimo 4GB de RAM | MariaDB requiere recursos significativos al compilar |
| **Conocimientos** | Programación en C++, manejo básico de bases de datos | Conceptos de clases, herencia y métodos virtuales |
- Si usás un entorno con MariaDB ya instalado, considerá hacer una compilación local para evitar afectar el entorno de producción.
- El ejemplo requiere modificar el código fuente de MariaDB, por lo que es crítico trabajar en un entorno controlado.
Guía paso a paso
Paso 1: Configurar el entorno de desarrollo
- Instalar dependencias básicas:
sudo apt update
sudo apt install -y build-essential cmake git bison libssl-dev libncurses5-dev libboost-all-dev
- Clonar el repositorio de MariaDB y cambiar al branch part4:
git clone https://github.com/MariaDB/server.git mariadb-server
cd mariadb-server
git checkout part4
> Verificación: Asegurate que estés en el branch correcto con git branch. Deberías ver * part4 como salida.
- Configurar y compilar MariaDB en modo Debug:
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug -DWITH_ASAN=OFF -DWITH_UNIT_TESTS=OFF
make -j$(nproc)
> Nota: La compilación puede tardar 30+ minutos dependiendo del hardware. Si tenés errores, revisá que todas las dependencias estén instaladas correctamente.
- Instalar la versión compilada localmente:
sudo make install
> Resultado esperado: La instalación coloca los binarios en /usr/local/mysql. Verificá con:
/usr/local/mysql/bin/mysqld --version
Deberías ver algo como mysqld Ver 10.6.0-MariaDB-debug for Linux on x86_64 (MariaDB Server).
Paso 2: Analizar el código base del tipo MONEY
- Ubicación del código del tipo MONEY:
part4 incluye una implementación básica del tipo MONEY en: sql/field_money.cc
sql/field_money.h
- Revisar la herencia y métodos sobrescritos:
sql/field_money.h y observá la declaración: class Field_money : public Field_real {
public:
Field_money(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
const CHARSET_INFO *charset_arg, const char *field_name_arg)
: Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, charset_arg, field_name_arg) {
// Inicialización adicional
}
// ...
String *val_str(String *str, String *val_ptr) override;
double val_real() const override;
int cmp(const uchar *a, const uchar *b) const override;
// ...
};
> Observación: Field_money hereda de Field_real, no de Field_double, porque Field_double::sort_string() está marcado como final en sql/field.h.
- Identificar métodos clave:
cmp(): Define cómo se comparan dos valores del tipo.– sort_string(): Define el string utilizado para ordenar en índices y consultas.
– val_real(), val_str(): Conversiones a tipos numéricos y string.
Paso 3: Modificar la lógica de ordenamiento
- Abrir el archivo de implementación:
nano sql/field_money.cc
- Implementar
sort_string()para orden inverso:
Field_money (en field_money.h o field_money.cc): // En field_money.h, dentro de la clase Field_money:
void sort_string(uchar *buff, size_t buf_len, const uchar *value,
size_t value_len) const override;
// En field_money.cc:
void Field_money::sort_string(uchar *buff, size_t buf_len, const uchar *value,
size_t value_len) const {
// Invertir el orden: en lugar de devolver el valor directo,
// devolver un valor que al compararse dé orden inverso.
// Ejemplo: para 100.00 devolver 999999.99 - 100.00
double val = *(double*)value;
double inverted = 999999.99 - val; // Valor arbitrario para demostración
int8 store_length = buf_len;
store_double(buff, inverted, &store_length);
}
> Explicación: Este método toma el valor almacenado y lo transforma para que, al compararse con strcmp-like, el orden sea inverso. El valor 999999.99 es arbitrario y solo sirve como ejemplo.
- Implementar métodos numéricos básicos:
Field_real (abstracta), necesitamos implementar métodos clave: // En field_money.cc:
double Field_money::val_real() const {
double res;
get_double(&res);
return res;
}
String *Field_money::val_str(String *str, String *val_ptr) {
double val = val_real();
str->set_real(val, 2, &my_charset_bin); // Formato con 2 decimales
return str;
}
int Field_money::cmp(const uchar *a, const uchar *b) const {
double a_val, b_val;
get_double(a, &a_val);
get_double(b, &b_val);
return (a_val < b_val) ? -1 : (a_val > b_val) ? 1 : 0;
}
> Resultado esperado: La clase Field_money ahora es concreta y puede ser instanciada.
Paso 4: Compilar y probar el cambio
- Recompilar MariaDB con los cambios:
cd mariadb-server/build
make -j$(nproc)
sudo make install
- Iniciar el servidor MariaDB en modo debug:
sudo /usr/local/mysql/bin/mysqld_safe --basedir=/usr/local/mysql --datadir=/var/lib/mysql &
- Crear una tabla de prueba con el tipo MONEY:
CREATE DATABASE test_money;
USE test_money;
CREATE TABLE transactions (
id INT AUTO_INCREMENT PRIMARY KEY,
amount MONEY NOT NULL
);
- Insertar datos de prueba:
INSERT INTO transactions (amount) VALUES
(100.50),
(50.25),
(1000.00),
(0.99),
(500.75);
- Verificar el ordenamiento normal vs. inverso:
-- Ordenamiento normal (de menor a mayor)
SELECT amount FROM transactions ORDER BY amount ASC;
-- Ordenamiento inverso (de mayor a menor, usando nuestro sort_string modificado)
SELECT amount FROM transactions ORDER BY amount DESC;
> Salida esperada:
+----------+
| amount |
+----------+
| 0.99 |
| 50.25 |
| 100.50 |
| 500.75 |
| 1000.00 |
+----------+
Para ORDER BY amount DESC, deberías ver el orden inverso al normal.
- Verificar con un índice:
ALTER TABLE transactions ADD INDEX idx_amount (amount);
EXPLAIN SELECT amount FROM transactions WHERE amount > 100 ORDER BY amount;
> Resultado esperado: El plan de consulta debería usar el índice idx_amount y el orden debería ser el inverso al esperado por defecto.
Consideraciones y buenas prácticas
Limitaciones conocidas
- Métodos
final:
final en la clase base (ej: Field_double::sort_string()), no podés sobrescribirlo directamente. La solución es derivar de una clase base más abstracta (ej: Field_real) e implementar todos los métodos requeridos.– Alternativa: Si no necesitás modificar el ordenamiento, podés dejar el tipo como derivado de Field_double y aceptar el comportamiento por defecto.
- Rendimiento:
sort_string() (ej: 999999.99 - val) agrega sobrecarga computacional. En producción, asegurate de que el beneficio (orden inverso) justifique el costo.– Benchmark: Medí el tiempo de consultas con EXPLAIN ANALYZE antes y después de los cambios.
- Compatibilidad:
Field_real o métodos numéricos pueden afectar el comportamiento en índices, joins y funciones agregadas. Probá exhaustivamente con: SELECT * FROM transactions WHERE amount BETWEEN 50 AND 500;
SELECT AVG(amount) FROM transactions;
Buenas prácticas para producción
- Documentación:
- Pruebas automatizadas:
cmp(), val_real() y sort_string() usando el framework de testing de MariaDB: // Ejemplo de test en unittest/mariadb-test.cc
void test_field_money_sort() {
Field_money field(nullptr, 0, nullptr, 0, NONE, &my_charset_bin, "money_test");
// ...
}
- Versionado:
git tag v10.6-custom-sort
- Monitoreo:
pt-query-digest o herramientas similares después de implementar los cambios en producción.Conclusión
En esta guía, aprendiste a sobreescribir el ordenamiento de un tipo de dato personalizado en MariaDB usando el framework Type_handler, incluso cuando el método sort_string() está bloqueado por un modificador final. Implementaste un tipo MONEY que ordena en orden inverso, derivando de Field_real y sobrescribiendo métodos clave como cmp() y val_real().
Los pasos clave fueron:
- Identificar que
Field_double::sort_string()esfinaly derivar deField_real. - Implementar métodos numéricos básicos (
val_real(),cmp()) para que la clase sea concreta. - Modificar
sort_string()para transformar los valores y lograr el orden inverso. - Compilar, instalar y probar el cambio en un entorno controlado.
Este enfoque es útil para escenarios donde necesités que un tipo de dato personalizado se comporte de manera no estándar (ej: orden alfabético inverso en un campo numérico, manipulación de fechas en índices, etc.). Recordá que cualquier cambio en el código fuente de MariaDB debe ser probado exhaustivamente antes de implementarlo en producción.
FIN