1
0
mirror of https://github.com/php/php-src.git synced 2026-04-16 12:31:06 +02:00

Merge branch 'PHP-8.0'

* PHP-8.0:
  Fix bug #79375
This commit is contained in:
Nikita Popov
2020-10-28 11:04:41 +01:00
7 changed files with 305 additions and 7 deletions

View File

@@ -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);

View 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

View File

@@ -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 */

View File

@@ -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);
}
/* }}} */

View File

@@ -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);
}

View 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

View File

@@ -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