I am trying to perform a series of operations with both temporary tables and the non-temporary machines table.
I want them all to be executed in one transaction.
I am testing that the rollback works correctly by forcing it with throw new Exception(‘force rollback’);
However, the registration is always made in the machines table (it is not reversed as I expect). The result of mysqli_rollback is 1.
That could be happening?
Code:
<?php
session_start();
include_once("../utils/system_utils.php");
include_once("../models/Maquina.php");
include_once("../models/Temp.php");
set_time_limit(300);
$error = "";
$id = null;
// Parametros de respuesta
$success = 0;
$message="";
$data = [];
$id_maquina = post('id_maquina');
$nombre_anterior = post('nombre_anterior');
$nombre = post('duplicate_name');
$reuso = post('reuso');
registrarLog("[utils - duplicar_maquina] - POST: ".json_encode(filter_input_array(INPUT_POST)));
$conexion = db_connect();
mysqli_begin_transaction($conexion);
try {
$query = "CREATE TEMPORARY TABLE tmp_dup SELECT * from maquinas WHERE id = ?";
registrarLog("Params: $id_maquina - $query [Maquinas::createMaquinaTemporal]", DEBUG);
$stmt = mysqli_prepare($conexion, $query);
mysqli_stmt_bind_param($stmt, 'i', $id_maquina);
if (!mysqli_stmt_execute($stmt)){
throw new Exception(gettext('Los datos no han podido ser guardados'));
}
$query = "ALTER TABLE tmp_dup drop id";
registrarLog("$query [Temp::borrarCampoID]", DEBUG);
$stmt = mysqli_prepare($conexion, $query);
if(!mysqli_stmt_execute($stmt)) {
throw new Exception(gettext('Los datos no han podido ser guardados'));
}
$query = "UPDATE tmp_dup SET nombre = ?, "
."estado = 0, "
."accion = NULL, "
."mensaje = NULL, "
."fecha_estado = NULL, "
."fecha_alta = CURRENT_TIMESTAMP, "
."fecha_actualizacion = NULL, "
."fecha_lectura = NULL, "
."fecha_comm = NULL, ";
if (!$reuso){
$query .= "ip = NULL, "
."imei = CONCAT(imei, '2'), ";
}
$query .= "fecha_ultima_venta = NULL, "
."version = '0.0.0', "
."initialized = '0', "
."price_changed = '0', "
."unions_changed = '0', "
."tft_changed = '0', "
."lcd_changed = '0', "
."version_changed = '0', "
."coverage = NULL, "
."banda = NULL, "
."operador = NULL";
registrarLog("Params: $nombre, $query [Temp::borrarCampoID]", DEBUG);
$stmt = mysqli_prepare($conexion, $query);
mysqli_stmt_bind_param($stmt, 's', $nombre);
if(! mysqli_stmt_execute($stmt)){
throw new Exception(gettext('Los datos no han podido ser guardados'));
}
$id = false;
$query = 'INSERT INTO maquinas SELECT 0, tmp_dup.* FROM tmp_dup';
registrarLog("$query [Maquinas::createMaquinaDesdeTemp]", DEBUG);
$stmt = mysqli_stmt_init($conexion);
mysqli_stmt_prepare($stmt, $query);
if (mysqli_stmt_execute($stmt)){
$id = mysqli_insert_id($conexion);
}
if(!$id){
registrarLog('ERR - No se ha podido registrar la maquina temporal. Error: '.mysqli_stmt_error($stmt). '- [Maquina::createMaquinaDesdeTemp]');
}
else {
registrarLog('Se ha registrado la maquina temporal. ID = '.$id. '- [Maquina::createMaquinaDesdeTemp]');
}
if(!$id){
throw new Exception(gettext('Los datos no han podido ser guardados'));
}
throw new Exception('fuerzo el rollback');
mysqli_commit($conexion);
} catch (\Exception $e) {
registrarLogRedFlag("EXC - Se ha producido un error al duplicar la maquina (id = $id_maquina): Revisar posible inconsistencia en los datos.");
$message = $e->getMessage();
$rroll = mysqli_rollback($conexion);
registrarLogRedFlag("rroll: ".$rroll);
}
echo getResponseArray($success, $message, $data);
Add
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
before yourmysqli_connect()
(ornew mysqli()
) command, and this will ensure that errors with your SQL queries are reported correctly to PHP automatically (and in your case, will then be caught by your try/catch). That way you don’t need to clutter your script with repetitive code to keep checking errors after mysqli commands (and in any case, you were a long way from covering all of the commands where a failure could occur).@shingo It was active. When active, a set of sql commands cannot be guaranteed to all execute correctly. Not even running mysqli_begin_transaction at startup right?
Sure…but in development mode that doesn’t matter, you can show the errors. In a live environment you would redirect all PHP errors to a log file anyway, so the exact SQL error should never show up in that scenario either. And also, in this case at least, you’re catching the exceptions anyway.
I think your
ALTER TABLE
is causing an implicit commit, after which there is no active transaction to rollback. Statements That Cause an Implicit CommitIt does not matter what the autocommit status is, as it is effectively suspended when you explicitly start a transaction, until there is a commit or rollback (explicit or implicit).
Show 2 more comments