MySQL soporta nativamente las transacciones en las tablas InnoDB (y, por lo tanto, MariaDB y Percona).
Pero, ¿qué es eso de las transacciones?, vamos a verlo con un ejemplo muy simplificado.
En un sistema bancario, yo realizo una transferencia de 100 € a mi amigo Luis. Internamente, se hace un update de mi usuario restándome 100 € y se hace un nuevo update del usuario Luis añadiéndole 100 €. Hasta aquí todo correcto (y desde luego, muy simplificado).
Pero ¿qué pasa si después de quitarme a mí los 100 € el sistema falla y, por lo tanto, no le añade los 100 € a Luis?, pues que quedan 100 € perdidos en tierra de nadie.
Para evitar esto, están las transacciones. Tenemos una serie de operaciones, que se deben ejecutar todas, si todo va bien confirmamos y se ejecutan, si algo falla por el camino volvemos atrás y queda todo estaba.
Eso son las transacciones, que iniciamos con START TRANSACTION;
o con BEGIN;
y si todo es como esperábamos, la confirmamos (y se ejecuta) con COMMIT;
o si detectamos algún fallo, volvemos atrás con ROLLBACK;
WordPress no está construido con transacciones, aunque nosotros podemos utilizarlas en nuestras consultas (en el 99 % de los casos no serían necesarias), pero nada nos impide utilizarlas en nuestras pruebas.
Escenario de uso real
En este caso, además de descargar la base de datos y realizarlo en local, era un poco complicado realizar el update y si iba mal, volver a restaurar la Base de Datos y volver a probar, así que usé las transacciones directamente en MySQL.
Se trataba de una web que llevaba online muchos años y había pasado por diversos hostings y de los miles de fotos que tenía, había unas 100 que no podía regenerar desde WP Cli por tener mal la ruta en los metadatos (wp media regenerate --only-missing
).
Al ver uno de los errores Warning: Can't find "xxxyyyzzz" (ID 1881).
y buscar el ID en la tabla wp_postmeta
se veía que el valor de _wp_attached_file
era incorrecto, del tipo /home/.xxxx/yyyy/www.zzzz.com/wp-content/uploads/2008/09/xxxyyyzzz.jpg
en lugar de 2008/09/xxxyyyzzz.jpg
La solución era efectuar el reemplazo en la base de datos, pero para evitar errores y nuevas pruebas restaurando la base de datos, la solución fue comenzar una transacción, buscar, realizar update, buscar de nuevo, mirar el valor de ese campo ID en concreto y al comprobar que todo está correcto ejecutar el update.
START TRANSACTION;
SELECT * FROM wp_postmeta WHERE meta_value like '/home/.xxxx/yyyy/www.zzzz.com/wp-content/uploads/%';
UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, '/home/.xxxx/yyyy/www.zzzz.com/wp-content/uploads/', '') WHERE meta_value like '/home/.xxxx/yyyy/www.zzzz.com/wp-content/uploads/%';
SELECT * FROM wp_postmeta WHERE meta_value like '/home/.xxxx/yyyy/www.zzzz.com/wp-content/uploads/%';
SELECT * FROM wp_postmeta WHERE post_id=1881;
ROLLBACK;
Iniciamos una transacción.
En la primera consulta buscamos todas las entradas que contengan dicha cadena y nos devuelve unos 100 registros.
En la segunda consulta ejecutamos un UPDATE
eliminando esa cadena y manteniendo el resto de la ruta.
En la tercera consulta, volvemos a ejecutar la primera, pero ahora nos da cero resultados.
En la cuarta consulta miramos el postmeta
del ID
original para comprobar que la ruta está correcta.
Y a continuación volvemos todo al estado anterior.
Y todo ha quedado como estaba, sin ningún cambio, pero verificando que nuestra consulta era correcta. En realidad la manera correcta de utilizarlo es con comprobaciones y realizar ROLLBACK
o COMMIT
según el resultado.
Pero en este caso era algo manual, en el que reviso resultados y al ver que todo está correcto, simplemente ejecuto la consulta de UPDATE
ya sin transacciones al comprobar que los resultados son los deseados.
Y como veis, esto mismo lo podemos aplicar a multitud de situaciones dónde no estamos seguros al 100 % de los resultados sobre la BD. Recordad siempre crear backups antes de hacer nada, por muy seguro que sea lo que vamos a hacer y si es posible, trabajad siempre sobre una copia (desarrollo, staging, local, etc.).