Files
cacti/tests/Unit/Database/DatabaseTransactionPortabilityTest.php
Thomas Vincent cb5ef7ba4a fix: commit transactions on MySQL as well as MariaDB (#7928)
* fix: commit transactions on MySQL as well as MariaDB

db_commit_transaction() gated the commit on SELECT @@in_transaction. That
variable is MariaDB only; MySQL answers 'Unknown system variable
in_transaction', so the guard was false on every MySQL install and the commit
never ran. The transaction was left open and the function reported failure.

Verified against both engines: MariaDB returns 0, MySQL 8 raises ERROR 1193.

Every caller was affected, including device removal, which reports 'Unable to
remove Device N because its database transaction could not be committed' and
leaves the device in place.

Use PDO::inTransaction(), which tracks the state itself and answers the same
question on both engines.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* Update transaction regression assertion

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
2026-09-02 16:54:17 -07:00

41 lines
1.7 KiB
PHP

<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
*/
$databaseSource = file_get_contents(dirname(__DIR__, 3) . '/lib/database.php');
test('the commit path does not depend on the MariaDB in_transaction variable', function () use ($databaseSource) {
$start = strpos($databaseSource, 'function db_commit_transaction(');
$end = strpos($databaseSource, 'function ', $start + 1);
$body = substr($databaseSource, $start, $end - $start);
// MySQL answers "Unknown system variable 'in_transaction'", so the guard
// was always false there and the commit never ran.
expect($body)->not->toContain("SELECT @@in_transaction")
->and($body)->toContain('inTransaction()');
});
test('PDO reports transaction state identically regardless of engine', function () {
$pdo = new PDO('sqlite::memory:', null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
expect($pdo->inTransaction())->toBeFalse();
$pdo->beginTransaction();
expect($pdo->inTransaction())->toBeTrue();
expect($pdo->commit())->toBeTrue()
->and($pdo->inTransaction())->toBeFalse();
});
test('committing without an open transaction is refused rather than fatal', function () {
$pdo = new PDO('sqlite::memory:', null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
// The guard exists because PDO::commit() throws when nothing is open.
expect($pdo->inTransaction())->toBeFalse();
});