Skip to content

Commit

Permalink
Fixed bug #66528
Browse files Browse the repository at this point in the history
Report errors in commit, rollback and autocommit handlers.
  • Loading branch information
nikic committed Oct 28, 2020
1 parent 68f80be commit 68dcaa2
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 7 deletions.
6 changes: 6 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ PHP NEWS
- MySQLi:
. Fixed bug #79375 (mysqli_store_result does not report error from lock wait
timeout). (Kamil Tekiela, Nikita)
. Fixed bug #76525 (mysqli::commit does not throw if MYSQLI_REPORT_ERROR
enabled and mysqlnd used). (Kamil Tekiela)

- PDO MySQL:
. Fixed bug #66528 (No PDOException or errorCode if database becomes
unavailable before PDO::commit). (Nikita)

29 Oct 2020, PHP 7.4.12

Expand Down
4 changes: 1 addition & 3 deletions ext/pdo/pdo_dbh.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,9 +829,7 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /*
}

fail:
if (attr == PDO_ATTR_AUTOCOMMIT) {
zend_throw_exception_ex(php_pdo_get_exception(), 0, "The auto-commit mode cannot be changed for this driver");
} else if (!dbh->methods->set_attribute) {
if (!dbh->methods->set_attribute) {
pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support setting attributes");
} else {
PDO_HANDLE_DBH_ERR();
Expand Down
22 changes: 18 additions & 4 deletions ext/pdo_mysql/mysql_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,11 @@ static int mysql_handle_commit(pdo_dbh_t *dbh)
{
PDO_DBG_ENTER("mysql_handle_commit");
PDO_DBG_INF_FMT("dbh=%p", dbh);
PDO_DBG_RETURN(0 == mysql_commit(((pdo_mysql_db_handle *)dbh->driver_data)->server));
if (mysql_commit(((pdo_mysql_db_handle *)dbh->driver_data)->server)) {
pdo_mysql_error(dbh);
PDO_DBG_RETURN(0);
}
PDO_DBG_RETURN(1);
}
/* }}} */

Expand All @@ -360,7 +364,11 @@ static int mysql_handle_rollback(pdo_dbh_t *dbh)
{
PDO_DBG_ENTER("mysql_handle_rollback");
PDO_DBG_INF_FMT("dbh=%p", dbh);
PDO_DBG_RETURN(0 <= mysql_rollback(((pdo_mysql_db_handle *)dbh->driver_data)->server));
if (mysql_rollback(((pdo_mysql_db_handle *)dbh->driver_data)->server)) {
pdo_mysql_error(dbh);
PDO_DBG_RETURN(0);
}
PDO_DBG_RETURN(1);
}
/* }}} */

Expand All @@ -370,7 +378,11 @@ static inline int mysql_handle_autocommit(pdo_dbh_t *dbh)
PDO_DBG_ENTER("mysql_handle_autocommit");
PDO_DBG_INF_FMT("dbh=%p", dbh);
PDO_DBG_INF_FMT("dbh->autocommit=%d", dbh->auto_commit);
PDO_DBG_RETURN(0 <= mysql_autocommit(((pdo_mysql_db_handle *)dbh->driver_data)->server, dbh->auto_commit));
if (mysql_autocommit(((pdo_mysql_db_handle *)dbh->driver_data)->server, dbh->auto_commit)) {
pdo_mysql_error(dbh);
PDO_DBG_RETURN(0);
}
PDO_DBG_RETURN(1);
}
/* }}} */

Expand All @@ -387,7 +399,9 @@ static int pdo_mysql_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
/* ignore if the new value equals the old one */
if (dbh->auto_commit ^ bval) {
dbh->auto_commit = bval;
mysql_handle_autocommit(dbh);
if (!mysql_handle_autocommit(dbh)) {
PDO_DBG_RETURN(0);
}
}
PDO_DBG_RETURN(1);

Expand Down
51 changes: 51 additions & 0 deletions ext/pdo_mysql/tests/bug66528.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
--TEST--
Bug #66528: No PDOException or errorCode if database becomes unavailable before PDO::commit
--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');

$dbh = MySQLPDOTest::factory();
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

$dbh->exec('DROP TABLE IF EXISTS test');
$dbh->exec('CREATE TABLE test (a int) engine=innodb');
$dbh->beginTransaction();
$dbh->exec('INSERT INTO test (a) VALUES (1), (2)');
$stmt = $dbh->query('SELECT * FROM test');

try {
$dbh->commit();
} catch (PDOException $e) {
echo $e->getMessage(), "\n";
}

try {
$dbh->rollBack();
} catch (PDOException $e) {
echo $e->getMessage(), "\n";
}

try {
$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false);
} catch (PDOException $e) {
echo $e->getMessage(), "\n";
}

?>
--CLEAN--
<?php
require __DIR__ . '/mysql_pdo_test.inc';
MySQLPDOTest::dropTestTable();
?>
--EXPECT--
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.

0 comments on commit 68dcaa2

Please sign in to comment.