-
Notifications
You must be signed in to change notification settings - Fork 7.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make sure deadlock errors are properly propagated and reports in a number of places in mysqli and PDO MySQL. This also fixes a memory and a segfault that can occur under these conditions.
- Loading branch information
1 parent
9353f11
commit b03776a
Showing
8 changed files
with
309 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
--TEST-- | ||
Bug #79375: mysqli_store_result does not report error from lock wait timeout | ||
--SKIPIF-- | ||
<?php | ||
require_once('skipif.inc'); | ||
require_once('skipifconnectfailure.inc'); | ||
if (!defined('MYSQLI_STORE_RESULT_COPY_DATA')) die('skip requires mysqlnd'); | ||
?> | ||
--FILE-- | ||
<?php | ||
|
||
require_once("connect.inc"); | ||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); | ||
$mysqli = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
$mysqli2 = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
|
||
$mysqli->query('DROP TABLE IF EXISTS test'); | ||
$mysqli->query('CREATE TABLE test (first int) ENGINE = InnoDB'); | ||
$mysqli->query('INSERT INTO test VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9)'); | ||
|
||
function testStmtStoreResult(mysqli $mysqli, string $name) { | ||
$mysqli->query("SET innodb_lock_wait_timeout = 1"); | ||
$mysqli->query("START TRANSACTION"); | ||
$query = "SELECT first FROM test WHERE first = 1 FOR UPDATE"; | ||
echo "Running query on $name\n"; | ||
$stmt = $mysqli->prepare($query); | ||
$stmt->execute(); | ||
try { | ||
$stmt->store_result(); | ||
echo "Got {$stmt->num_rows} for $name\n"; | ||
} catch(mysqli_sql_exception $e) { | ||
echo $e->getMessage()."\n"; | ||
} | ||
} | ||
function testStmtGetResult(mysqli $mysqli, string $name) { | ||
$mysqli->query("SET innodb_lock_wait_timeout = 1"); | ||
$mysqli->query("START TRANSACTION"); | ||
$query = "SELECT first FROM test WHERE first = 1 FOR UPDATE"; | ||
echo "Running query on $name\n"; | ||
$stmt = $mysqli->prepare($query); | ||
$stmt->execute(); | ||
try { | ||
$res = $stmt->get_result(); | ||
echo "Got {$res->num_rows} for $name\n"; | ||
} catch(mysqli_sql_exception $e) { | ||
echo $e->getMessage()."\n"; | ||
} | ||
} | ||
function testNormalQuery(mysqli $mysqli, string $name) { | ||
$mysqli->query("SET innodb_lock_wait_timeout = 1"); | ||
$mysqli->query("START TRANSACTION"); | ||
$query = "SELECT first FROM test WHERE first = 1 FOR UPDATE"; | ||
echo "Running query on $name\n"; | ||
try { | ||
$res = $mysqli->query($query); | ||
echo "Got {$res->num_rows} for $name\n"; | ||
} catch(mysqli_sql_exception $e) { | ||
echo $e->getMessage()."\n"; | ||
} | ||
} | ||
function testStmtUseResult(mysqli $mysqli, string $name) { | ||
$mysqli->query("SET innodb_lock_wait_timeout = 1"); | ||
$mysqli->query("START TRANSACTION"); | ||
$query = "SELECT first FROM test WHERE first = 1 FOR UPDATE"; | ||
echo "Running query on $name\n"; | ||
$stmt = $mysqli->prepare($query); | ||
$stmt->execute(); | ||
try { | ||
$stmt->fetch(); // should throw an error | ||
$stmt->fetch(); | ||
echo "Got {$stmt->num_rows} for $name\n"; | ||
} catch (mysqli_sql_exception $e) { | ||
echo $e->getMessage()."\n"; | ||
} | ||
} | ||
function testResultFetchRow(mysqli $mysqli, string $name) { | ||
$mysqli->query("SET innodb_lock_wait_timeout = 1"); | ||
$mysqli->query("START TRANSACTION"); | ||
$query = "SELECT first FROM test WHERE first = 1 FOR UPDATE"; | ||
echo "Running query on $name\n"; | ||
$res = $mysqli->query($query, MYSQLI_USE_RESULT); | ||
try { | ||
$res->fetch_row(); | ||
$res->fetch_row(); | ||
echo "Got {$res->num_rows} for $name\n"; | ||
} catch(mysqli_sql_exception $e) { | ||
echo $e->getMessage()."\n"; | ||
} | ||
} | ||
|
||
testStmtStoreResult($mysqli, 'first connection'); | ||
testStmtStoreResult($mysqli2, 'second connection'); | ||
|
||
$mysqli->close(); | ||
$mysqli2->close(); | ||
|
||
echo "\n"; | ||
// try it again for get_result | ||
$mysqli = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
$mysqli2 = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
|
||
testStmtGetResult($mysqli, 'first connection'); | ||
testStmtGetResult($mysqli2, 'second connection'); | ||
|
||
$mysqli->close(); | ||
$mysqli2->close(); | ||
|
||
echo "\n"; | ||
// try it again with unprepared query | ||
$mysqli = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
$mysqli2 = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
|
||
testNormalQuery($mysqli, 'first connection'); | ||
testNormalQuery($mysqli2, 'second connection'); | ||
|
||
$mysqli->close(); | ||
$mysqli2->close(); | ||
|
||
echo "\n"; | ||
// try it again with unprepared query | ||
$mysqli = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
$mysqli2 = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
|
||
testStmtUseResult($mysqli, 'first connection'); | ||
testStmtUseResult($mysqli2, 'second connection'); | ||
|
||
$mysqli->close(); | ||
$mysqli2->close(); | ||
|
||
echo "\n"; | ||
// try it again using fetch_row on a result object | ||
$mysqli = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
$mysqli2 = new my_mysqli($host, $user, $passwd, $db, $port, $socket); | ||
|
||
testResultFetchRow($mysqli, 'first connection'); | ||
testResultFetchRow($mysqli2, 'second connection'); | ||
|
||
$mysqli->close(); | ||
$mysqli2->close(); | ||
|
||
?> | ||
--CLEAN-- | ||
<?php | ||
require_once("clean_table.inc"); | ||
?> | ||
--EXPECTF-- | ||
Running query on first connection | ||
Got %d for first connection | ||
Running query on second connection | ||
Lock wait timeout exceeded; try restarting transaction | ||
|
||
Running query on first connection | ||
Got %d for first connection | ||
Running query on second connection | ||
Lock wait timeout exceeded; try restarting transaction | ||
|
||
Running query on first connection | ||
Got %d for first connection | ||
Running query on second connection | ||
Lock wait timeout exceeded; try restarting transaction | ||
|
||
Running query on first connection | ||
Got %d for first connection | ||
Running query on second connection | ||
Lock wait timeout exceeded; try restarting transaction | ||
|
||
Running query on first connection | ||
Got 1 for first connection | ||
Running query on second connection | ||
|
||
Warning: mysqli_result::fetch_row(): Error while reading a row in %s on line %d | ||
Got 0 for second connection |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
--TEST-- | ||
Bug #79375: mysqli_store_result does not report error from lock wait timeout | ||
--SKIPIF-- | ||
<?php | ||
if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded'); | ||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc'); | ||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc'); | ||
MySQLPDOTest::skip(); | ||
?> | ||
--FILE-- | ||
<?php | ||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc'); | ||
|
||
function createDB(): PDO { | ||
$db = MySQLPDOTest::factory(); | ||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | ||
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); | ||
return $db; | ||
} | ||
|
||
$db = createDB(); | ||
$db2 = createDB(); | ||
$db->query('DROP TABLE IF EXISTS test'); | ||
$db->query('CREATE TABLE test (first int) ENGINE = InnoDB'); | ||
$db->query('INSERT INTO test VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9)'); | ||
|
||
function testNormalQuery(PDO $db, string $name) { | ||
$db->exec("SET innodb_lock_wait_timeout = 1"); | ||
$db->exec("START TRANSACTION"); | ||
$query = "SELECT first FROM test WHERE first = 1 FOR UPDATE"; | ||
echo "Running query on $name\n"; | ||
try { | ||
$stmt = $db->query($query); | ||
echo "Got {$stmt->rowCount()} for $name\n"; | ||
} catch (PDOException $e) { | ||
echo $e->getMessage()."\n"; | ||
} | ||
} | ||
|
||
function testPrepareExecute(PDO $db, string $name) { | ||
$db->exec("SET innodb_lock_wait_timeout = 1"); | ||
$db->exec("START TRANSACTION"); | ||
$query = "SELECT first FROM test WHERE first = 1 FOR UPDATE"; | ||
echo "Running query on $name\n"; | ||
$stmt = $db->prepare($query); | ||
try { | ||
$stmt->execute(); | ||
echo "Got {$stmt->rowCount()} for $name\n"; | ||
} catch (PDOException $e) { | ||
echo $e->getMessage()."\n"; | ||
} | ||
} | ||
|
||
function testUnbuffered(PDO $db, string $name) { | ||
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); | ||
$db->exec("SET innodb_lock_wait_timeout = 1"); | ||
$db->exec("START TRANSACTION"); | ||
$query = "SELECT first FROM test WHERE first = 1 FOR UPDATE"; | ||
echo "Running query on $name\n"; | ||
$stmt = $db->prepare($query); | ||
$stmt->execute(); | ||
try { | ||
$rows = $stmt->fetchAll(); | ||
$count = count($rows); | ||
echo "Got $count for $name\n"; | ||
} catch (PDOException $e) { | ||
echo $e->getMessage()."\n"; | ||
} | ||
} | ||
|
||
testNormalQuery($db, 'first connection'); | ||
testNormalQuery($db2, 'second connection'); | ||
unset($db); | ||
unset($db2); | ||
echo "\n"; | ||
|
||
$db = createDB(); | ||
$db2 = createDB(); | ||
testPrepareExecute($db, 'first connection'); | ||
testPrepareExecute($db2, 'second connection'); | ||
unset($db); | ||
unset($db2); | ||
echo "\n"; | ||
|
||
$db = createDB(); | ||
$db2 = createDB(); | ||
testUnbuffered($db, 'first connection'); | ||
testUnbuffered($db2, 'second connection'); | ||
unset($db); | ||
unset($db2); | ||
echo "\n"; | ||
|
||
?> | ||
--CLEAN-- | ||
<?php | ||
require __DIR__ . '/mysql_pdo_test.inc'; | ||
MySQLPDOTest::dropTestTable(); | ||
?> | ||
--EXPECT-- | ||
Running query on first connection | ||
Got 1 for first connection | ||
Running query on second connection | ||
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction | ||
|
||
Running query on first connection | ||
Got 1 for first connection | ||
Running query on second connection | ||
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction | ||
|
||
Running query on first connection | ||
Got 1 for first connection | ||
Running query on second connection | ||
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters