mirror of
https://github.com/php/php-src.git
synced 2026-04-16 12:31:06 +02:00
@@ -1070,7 +1070,8 @@ void mysqli_stmt_fetch_mysqlnd(INTERNAL_FUNCTION_PARAMETERS)
|
||||
}
|
||||
MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_VALID);
|
||||
|
||||
if (FAIL == mysqlnd_stmt_fetch(stmt->stmt, &fetched_anything)) {
|
||||
if (FAIL == mysqlnd_stmt_fetch(stmt->stmt, &fetched_anything)) {
|
||||
MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
|
||||
RETURN_BOOL(FALSE);
|
||||
} else if (fetched_anything == TRUE) {
|
||||
RETURN_BOOL(TRUE);
|
||||
|
||||
172
ext/mysqli/tests/bug79375.phpt
Normal file
172
ext/mysqli/tests/bug79375.phpt
Normal file
@@ -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
|
||||
@@ -119,9 +119,11 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
|
||||
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
|
||||
} else {
|
||||
COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
|
||||
COPY_CLIENT_ERROR(stmt->error_info, result->stored_data->error_info);
|
||||
stmt->result->m.free_result_contents(stmt->result);
|
||||
stmt->result = NULL;
|
||||
stmt->state = MYSQLND_STMT_PREPARED;
|
||||
DBG_RETURN(NULL);
|
||||
}
|
||||
|
||||
DBG_RETURN(result);
|
||||
@@ -176,7 +178,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((result = result->m.store_result(result, conn, MYSQLND_STORE_PS | MYSQLND_STORE_NO_COPY))) {
|
||||
if (result->m.store_result(result, conn, MYSQLND_STORE_PS | MYSQLND_STORE_NO_COPY)) {
|
||||
UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, result->stored_data->row_count);
|
||||
stmt->state = MYSQLND_STMT_PREPARED;
|
||||
result->type = MYSQLND_RES_PS_BUF;
|
||||
@@ -879,7 +881,9 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, const unsi
|
||||
} else if (ret == FAIL) {
|
||||
if (row_packet->error_info.error_no) {
|
||||
COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
|
||||
COPY_CLIENT_ERROR(stmt->error_info, row_packet->error_info);
|
||||
if (stmt) {
|
||||
COPY_CLIENT_ERROR(stmt->error_info, row_packet->error_info);
|
||||
}
|
||||
}
|
||||
SET_CONNECTION_STATE(&conn->state, CONN_READY);
|
||||
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
|
||||
|
||||
@@ -905,7 +905,7 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
|
||||
result->memory_pool->checkpoint = checkpoint;
|
||||
|
||||
DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
|
||||
DBG_RETURN(PASS);
|
||||
DBG_RETURN(ret);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
@@ -255,7 +255,10 @@ static int pdo_mysql_stmt_execute_prepared_libmysql(pdo_stmt_t *stmt) /* {{{ */
|
||||
|
||||
/* if buffered, pre-fetch all the data */
|
||||
if (H->buffered) {
|
||||
mysql_stmt_store_result(S->stmt);
|
||||
if (mysql_stmt_store_result(S->stmt)) {
|
||||
pdo_mysql_error_stmt(stmt);
|
||||
PDO_DBG_RETURN(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,6 +301,7 @@ static int pdo_mysql_stmt_execute_prepared_mysqlnd(pdo_stmt_t *stmt) /* {{{ */
|
||||
/* if buffered, pre-fetch all the data */
|
||||
if (H->buffered) {
|
||||
if (mysql_stmt_store_result(S->stmt)) {
|
||||
pdo_mysql_error_stmt(stmt);
|
||||
PDO_DBG_RETURN(0);
|
||||
}
|
||||
}
|
||||
@@ -386,7 +390,8 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt) /* {{{ */
|
||||
/* if buffered, pre-fetch all the data */
|
||||
if (H->buffered) {
|
||||
if (mysql_stmt_store_result(S->stmt)) {
|
||||
PDO_DBG_RETURN(1);
|
||||
pdo_mysql_error_stmt(stmt);
|
||||
PDO_DBG_RETURN(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -621,6 +626,7 @@ static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori
|
||||
PDO_DBG_INF_FMT("stmt=%p", S->stmt);
|
||||
if (S->stmt) {
|
||||
if (FAIL == mysqlnd_stmt_fetch(S->stmt, &fetched_anything) || fetched_anything == FALSE) {
|
||||
pdo_mysql_error_stmt(stmt);
|
||||
PDO_DBG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
113
ext/pdo_mysql/tests/bug79375.phpt
Normal file
113
ext/pdo_mysql/tests/bug79375.phpt
Normal file
@@ -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
|
||||
@@ -23,5 +23,7 @@ $stmt = $db->query("select (select 1 union select 2)");
|
||||
|
||||
print "ok";
|
||||
?>
|
||||
--EXPECT--
|
||||
--EXPECTF--
|
||||
|
||||
Warning: PDO::query(): SQLSTATE[21000]: Cardinality violation: 1242 Subquery returns more than 1 row in %s on line %d
|
||||
ok
|
||||
|
||||
Reference in New Issue
Block a user