Merge branch 'PHP-7.4' into PHP-8.0

* PHP-7.4:
  Fixed bug #80458
This commit is contained in:
Nikita Popov 2020-12-04 17:00:20 +01:00
commit 9dc42b4114
3 changed files with 199 additions and 10 deletions

4
NEWS
View file

@ -34,6 +34,10 @@ PHP NEWS
. Fixed bug #80368 (OpenSSL extension fails to build against LibreSSL due to
lack of OCB support). (Nikita)
- PDO MySQL:
. Fixed bug #80458 (PDOStatement::fetchAll() throws for upsert queries).
(Kamil Tekiela)
- Phar:
. Fixed bug #73809 (Phar Zip parse crash - mmap fail). (cmb)
. Fixed #75102 (`PharData` says invalid checksum for valid tar). (cmb)

View file

@ -619,6 +619,11 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da
static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */
{
pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data;
if (!S->result) {
PDO_DBG_RETURN(0);
}
#ifdef PDO_USE_MYSQLND
zend_bool fetched_anything;
@ -632,6 +637,10 @@ static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori
PDO_DBG_RETURN(1);
}
if (!S->stmt && S->current_data) {
mnd_free(S->current_data);
}
#else
int ret;
@ -655,16 +664,6 @@ static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori
}
#endif /* PDO_USE_MYSQLND */
if (!S->result) {
strcpy(stmt->error_code, "HY000");
PDO_DBG_RETURN(0);
}
#ifdef PDO_USE_MYSQLND
if (!S->stmt && S->current_data) {
mnd_free(S->current_data);
}
#endif /* PDO_USE_MYSQLND */
if ((S->current_data = mysql_fetch_row(S->result)) == NULL) {
if (!S->H->buffered && mysql_errno(S->H->server)) {
pdo_mysql_error_stmt(stmt);

View file

@ -0,0 +1,186 @@
--TEST--
Bug #80458 PDOStatement::fetchAll() throws for upsert queries
--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');
$db = MySQLPDOTest::factory();
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$db->query('DROP TABLE IF EXISTS test');
$db->query('CREATE TABLE test (first int) ENGINE = InnoDB');
$res = $db->query('INSERT INTO test(first) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16)');
var_dump($res->fetchAll());
$stmt = $db->prepare('DELETE FROM test WHERE first=1');
$stmt->execute();
var_dump($stmt->fetchAll());
$res = $db->query('DELETE FROM test WHERE first=2');
var_dump($res->fetchAll());
$stmt2 = $db->prepare('DELETE FROM test WHERE first=3');
$stmt2->execute();
foreach($stmt2 as $row){
// expect nothing
}
$stmt3 = $db->prepare('DELETE FROM test WHERE first=4');
$stmt3->execute();
var_dump($stmt3->fetch(PDO::FETCH_ASSOC));
$stmt = $db->prepare('SELECT first FROM test WHERE first=5');
$stmt->execute();
var_dump($stmt->fetchAll());
$db->exec('DROP PROCEDURE IF EXISTS nores');
$db->exec('CREATE PROCEDURE nores() BEGIN DELETE FROM test WHERE first=6; END;');
$stmt4 = $db->prepare('CALL nores()');
$stmt4->execute();
var_dump($stmt4->fetchAll());
$db->exec('DROP PROCEDURE IF EXISTS nores');
$db->exec('DROP PROCEDURE IF EXISTS ret');
$db->exec('CREATE PROCEDURE ret() BEGIN SELECT first FROM test WHERE first=7; END;');
$stmt5 = $db->prepare('CALL ret()');
$stmt5->execute();
var_dump($stmt5->fetchAll());
$stmt5->nextRowset(); // needed to fetch the empty result set of CALL
var_dump($stmt5->fetchAll());
$db->exec('DROP PROCEDURE IF EXISTS ret');
/* With emulated prepares */
print("Emulated prepares\n");
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$stmt = $db->prepare('DELETE FROM test WHERE first=8');
$stmt->execute();
var_dump($stmt->fetchAll());
$res = $db->query('DELETE FROM test WHERE first=9');
var_dump($res->fetchAll());
$stmt2 = $db->prepare('DELETE FROM test WHERE first=10');
$stmt2->execute();
foreach($stmt2 as $row){
// expect nothing
}
$stmt3 = $db->prepare('DELETE FROM test WHERE first=11');
$stmt3->execute();
var_dump($stmt3->fetch(PDO::FETCH_ASSOC));
$stmt = $db->prepare('SELECT first FROM test WHERE first=12');
$stmt->execute();
var_dump($stmt->fetchAll());
$db->exec('DROP PROCEDURE IF EXISTS nores');
$db->exec('CREATE PROCEDURE nores() BEGIN DELETE FROM test WHERE first=13; END;');
$stmt4 = $db->prepare('CALL nores()');
$stmt4->execute();
var_dump($stmt4->fetchAll());
$db->exec('DROP PROCEDURE IF EXISTS nores');
$db->exec('DROP PROCEDURE IF EXISTS ret');
$db->exec('CREATE PROCEDURE ret() BEGIN SELECT first FROM test WHERE first=14; END;');
$stmt5 = $db->prepare('CALL ret()');
$stmt5->execute();
var_dump($stmt5->fetchAll());
$stmt5->nextRowset(); // needed to fetch the empty result set of CALL
var_dump($stmt5->fetchAll());
$db->exec('DROP PROCEDURE IF EXISTS ret');
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$stmt = $db->prepare('DELETE FROM test WHERE first=15');
$stmt->execute();
var_dump($stmt->fetchAll());
$stmt = $db->prepare('SELECT first FROM test WHERE first=16');
$stmt->execute();
var_dump($stmt->fetchAll());
?>
--CLEAN--
<?php
require __DIR__ . '/mysql_pdo_test.inc';
MySQLPDOTest::dropTestTable();
?>
--EXPECT--
array(0) {
}
array(0) {
}
array(0) {
}
bool(false)
array(1) {
[0]=>
array(2) {
["first"]=>
int(5)
[0]=>
int(5)
}
}
array(0) {
}
array(1) {
[0]=>
array(2) {
["first"]=>
int(7)
[0]=>
int(7)
}
}
array(0) {
}
Emulated prepares
array(0) {
}
array(0) {
}
bool(false)
array(1) {
[0]=>
array(2) {
["first"]=>
string(2) "12"
[0]=>
string(2) "12"
}
}
array(0) {
}
array(1) {
[0]=>
array(2) {
["first"]=>
string(2) "14"
[0]=>
string(2) "14"
}
}
array(0) {
}
array(0) {
}
array(1) {
[0]=>
array(2) {
["first"]=>
int(16)
[0]=>
int(16)
}
}