Introducción
merql es una biblioteca pura de PHP que realiza fusiones de tres vías del estado de bases de datos. Crea instantáneas de tablas, calcula conjuntos de cambios entre ellas, fusiona dos conjuntos de cambios a partir de una base común y detecta conflictos a nivel de columna. Aplica a filas y columnas el mismo algoritmo que git usa con archivos de código fuente.
Sin herramientas externas. Admite MySQL y SQLite mediante adaptadores de conexión y controladores de dialecto conectables.
Ejemplo rápido
Una fusión requiere tres estados de los mismos datos: la base de la que partieron ambas copias y las dos copias después de modificarse de forma independiente. En este caso, se creó un entorno de pruebas a partir de la base de datos en vivo y ambos se han editado desde entonces.
use Merql\Connection;
use Merql\Merql;
$live = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');
// Base: live at the moment the sandbox was copied from it.
Merql::init($live);
Merql::snapshot('base');
// ... copy live.sqlite to sandbox.sqlite, then both databases change ...
// Ours: the sandbox with our edits. Theirs: live with its own changes.
Merql::init($sandbox);
Merql::snapshot('ours');
Merql::init($live);
Merql::snapshot('theirs');
$result = Merql::merge('base', 'ours', 'theirs');
if ($result->isClean()) {
foreach ($result->operations() as $operation) {
echo "{$operation->type} {$operation->table} #{$operation->rowKey} ({$operation->source})\n";
}
} else {
echo "Conflicts: {$result->conflictCount()}\n";
}
Cada instantánea se captura de la base de datos que contiene ese estado. Capturar las tres de una sola base de datos, una tras otra, no representa dos copias divergentes: theirs ya contendría los cambios de ours, y cada fila insertada por ours se marcaría como un conflicto insert_insert.
El resultado enumera las operaciones que transforman la base en el estado fusionado, con los cambios de ambos lados y una marca que indica el origen de cada una (ours, theirs o merged). Para trasladar los cambios del entorno de pruebas a la base de datos en vivo, aplique las operaciones que aún le falten mediante Aplicación con protección.
O desde CLI, que se conecta a la base de datos indicada en MERQL_DB_DSN para cada snapshot:
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot base
# ... copy live.sqlite to sandbox.sqlite, then both databases change ...
MERQL_DB_DSN="sqlite:/data/sandbox.sqlite" vendor/bin/merql snapshot ours
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot theirs
vendor/bin/merql merge base ours theirs --dry-run
Por qué merql
La mayoría de las herramientas de bases de datos que comparan estados trabajan a nivel de fila: si alguna columna de una fila difiere entre dos versiones, toda la fila se marca como conflicto. Esto genera conflictos falsos cuando dos personas editan columnas distintas de la misma fila.
merql combina los cambios a nivel de columna. Si una parte modificó title y la otra status, esos cambios se combinan sin conflictos. Solo se produce un conflicto cuando ambas partes modifican la misma columna con valores distintos.
En columnas TEXT y JSON, merql puede profundizar un nivel más con la combinación a nivel de celda. Dos ediciones en distintas líneas de una columna de texto, o en distintas claves de una columna JSON, pueden combinarse sin conflictos mediante el mismo algoritmo de tres vías. La combinación a nivel de celda es opcional: pase CellMergeConfig a ThreeWayMerge. La fachada Merql y CLI solo combinan a nivel de columna. Consulte Combinación a nivel de celda.
El modelo de combinación
Base (snapshot at fork time)
/ \
Ours (one copy's changes) Theirs (the other copy's changes)
\ /
--------- MERGE ----------
|
Merged result
+ conflicts (if any)
Tres estados, igual que en git. Base es el ancestro común. Nuestro y suyo son las dos copias después de divergir, cada una capturada desde su propia base de datos. Las reglas de combinación tratan ambas partes por igual. Los nombres aparecen en el source de cada operación, en los tipos de conflicto (update_delete significa que nuestro lado actualizó y el suyo eliminó) y en las políticas de resolución OursWins y TheirsWins. Sin una base común, no se puede distinguir entre «añadido» y «sin cambios».
Una combinación no escribe nada por sí sola. Sus operaciones transforman la base en el estado combinado, por lo que Merql::apply() las ejecuta todas sin problemas solo en una base de datos que aún contiene la base. Para incorporar los cambios de una parte a una base de datos que ya contiene los de la otra, como al promover ediciones de sandbox a live, aplique solo las operaciones que le faltan; consulte Aplicación protegida.
Requisitos
- PHP 8.2 o posterior.
ext-mbstringyext-zlib, requeridos por la dependenciapitmaster/pitmasterque proporciona la diferencia de texto.- Una extensión de base de datos para el adaptador que use:
ext-pdoconext-pdo_sqliteoext-pdo_mysql, oext-mysqli. Lacomposer.jsonde merql solo sugiereext-pdoyext-mysqli, así que Composer no las comprueba. - Una base de datos MySQL o SQLite. Otros motores requieren un controlador personalizado.
Qué no es
merql no es una herramienta de migración de esquemas. No replica bases de datos ni las sincroniza en tiempo real. Resuelve un problema específico: dadas tres versiones de una base de datos, produce una cuarta versión combinada e identifica los conflictos.
Documentación
- Primeros pasos -- instalación, primera instantánea y primera combinación.
- Referencia de CLI -- referencia completa del comando
vendor/bin/merql. - Referencia de PHP API -- documentación completa de API para todas las clases.
- Planes de combinación -- identificadores de operación estables, grupos de cambios y selecciones.
- Aplicación protegida -- escrituras seleccionadas con condiciones previas para las filas activas.
- Reversión -- artefactos de reversión y comprobaciones de desviaciones.