diff --git a/NEWS b/NEWS index eecfe0f7c1e..31e8843282c 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,10 @@ PHP NEWS . Fixed bug GH-14100 (Corrected spelling mistake in php.ini files). (Marcus Xavier) +- MySQLnd: + . Fix bug GH-14255 (mysqli_fetch_assoc reports error from + nested query). (Kamil Tekiela) + - Opcache: . Fixed bug GH-14109 (Fix accidental persisting of internal class constant in shm). (ilutov) diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 9fa46acbb7d..14c2af6a1a9 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -731,7 +731,7 @@ void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, zend /* TODO: We don't have access to the connection object at this point, so we use low-level * mysqlnd APIs to access the error information. We should try to pass through the connection * object instead. */ - if (MyG(report_mode) & MYSQLI_REPORT_ERROR) { + if (MyG(report_mode) & MYSQLI_REPORT_ERROR && result->conn) { MYSQLND_CONN_DATA *conn = result->conn; unsigned error_no = conn->m->get_error_no(conn); if (error_no) { diff --git a/ext/mysqli/tests/gh14255.phpt b/ext/mysqli/tests/gh14255.phpt new file mode 100644 index 00000000000..375eda0c5b5 --- /dev/null +++ b/ext/mysqli/tests/gh14255.phpt @@ -0,0 +1,31 @@ +--TEST-- +Bug GH-14255 (mysqli_fetch_assoc reports error from nested query) +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- +query('SELECT 1 '); +$c = $ca->fetch_assoc(); +try { + $mysqli->query('SELECT non_existent_column'); +} catch (Exception $e) { + echo "Caught exception"."\n"; +} +$c = $ca->fetch_assoc(); + +print "done!"; +?> +--EXPECTF-- +Caught exception +done! diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 0331518d7d3..cf091a802bb 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -972,6 +972,13 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int bool fetched_anything; zval *row_data; + // We clean the error here because in unbuffered mode we could receive a new error + // and therefore consumers of this method are checking for errors + MYSQLND_CONN_DATA *conn = result->conn; + if (conn) { + SET_EMPTY_ERROR(conn->error_info); + } + DBG_ENTER("mysqlnd_res::fetch_into"); if (FAIL == result->m.fetch_row(result, &row_data, flags, &fetched_anything)) { RETVAL_FALSE;