Edit report at http://bugs.php.net/bug.php?id=44454&edit=1

 ID:               44454
 Updated by:       fel...@php.net
 Reported by:      mfisc...@php.net
 Summary:          Unexpected exception thrown in foreach() statement
-Status:           Verified
+Status:           Assigned
 Type:             Bug
 Package:          PDO related
 Operating System: *
 PHP Version:      5.*, 6CVS (2009-04-25)
-Assigned To:      
+Assigned To:      mysql



Previous Comments:
------------------------------------------------------------------------
[2010-06-03 20:58:10] rgagnon24 at gmail dot com

I've attached a patch "fix-mysql_statement.c-5.2.13.patch" to resolve
this problem.



From: http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html



   "When used after mysql_store_result(), mysql_fetch_row() returns NULL
when there are no more rows to retrieve. When used after
mysql_use_result(), mysql_fetch_row() returns NULL when there are no
more rows to retrieve or if an error occurred"



The patch simply does not raise an exception during a NULL result from
mysql_fetch_row() since it only indicates the exhaustion of data.



The condition added simply matches the use of either mysql_use_result()
or mysql_store_result() called in pdo_mysql_stmt_execute().  When not
buffered, mysql_use_result() is called.  Therefore, the same check is
performed after the fetch before deciding to raise an exception.



Also, when un-buffered queries are used, the test script above would be
invalid as you cannot perform another operation on a result-set when not
all of the results have been retrieved.



The patch was created against the released PHP 5.2.3 source code
tarball.  It's so small, you should be able to modify it easily for any
version of the mysql_statement.c file.

------------------------------------------------------------------------
[2010-06-03 20:36:48] rgagnon24 at gmail dot com

From: http://dev.mysql.com/doc/refman/5.0/en/mysql-errno.html



   "Note that some functions like mysql_fetch_row() don't set
mysql_errno() if they succeed."



And: http://dev.mysql.com/doc/refman/5.0/en/mysql-fetch-row.html



   "Note that error is not reset between calls to mysql_fetch_row()"

-----------------------------------------------------------------------------

Since all the SELECT'd rows are fetched ok, the error from the botched
insert is still hanging around for mysql_errno() to find, and raise the
exact same exception after the data is finished being iterated.

------------------------------------------------------------------------
[2010-06-03 20:03:37] rgagnon24 at gmail dot com

Looking at pdo_mysql extension source code, I see the exception is
actually being raised twice, not being buffered.



from php5.2.13 source, file ext/pdo_mysql/mysql_statement.c is calling
_pdo_mysql_error() in two places.  Once from line 218 in
pdo_mysql_stmt_execute() after mysql_real_query() fails, and then again
at line 425 in pdo_mysql_stmt_fetch() because mysql_errno() indicates
there is an error.

------------------------------------------------------------------------
[2010-05-08 06:08:33] gregory at tiv dot net

Correction:

-----------

if ( $conn->errorCode() ) {



should be



if ( $conn->errorCode() !== '00000' ) {

------------------------------------------------------------------------
[2010-05-07 02:13:48] gregory at tiv dot net

I have a simpler test case, one solution/explanation and one
workaround.

Tested under:

Windows - PHP 5.2.13 (cli) (built: Feb 24 2010 14:32:32)

FreeBSD - PHP 5.2.12 with Suhosin-Patch 0.9.7 (cli) (built: Feb 24 2010
23:12:45)



Demonstration code:

-------------------



<?php

#

# PDO foreach exception bug

# Demonstration code

# Author: Gregory Karpinsky, http://www.tiv.net

# 2010-05-06



print '<p>This code works OK (Exception is cleaned artificially)</p>';

$conn = new PDO( 'mysql:host=localhost', 'test', 'test' );

$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$oRS = $conn->query( "select 'OK'" );

foreach ( $oRS as $row ) {

        try {

                $conn->query( 'Bogus SQL' );

        } catch (PDOException $e) {}

        if ( $conn->errorCode() ) {

                $conn->query( "select 'CLEAN_PDO_ERROR'" );

        }

print '<p>NO exception will be thrown.</p>';

}



print '<p>This code works OK (two separate connections)</p>';

$conn = new PDO( 'mysql:host=localhost', 'test', 'test' );

$conn2 = new PDO( 'mysql:host=localhost', 'test', 'test' );

$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$conn2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$oRS = $conn->query( "select 'OK'" );

foreach ( $oRS as $row ) {

        try {

                $conn2->query( 'Bogus SQL' );

        } catch (PDOException $e) {}

print '<p>NO exception will be thrown.</p>';

}





print '<p>This code throws unexpected exception in foreach</p>';

$conn = new PDO( 'mysql:host=localhost', 'test', 'test' );

$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$oRS = $conn->query( "select 'OK'" );

foreach ( $oRS as $row ) {

        try {

                $conn->query( 'Bogus SQL' );

        } catch (PDOException $e) {}

print '<p>Exception will be thrown after this...</p>';

}

?>

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    http://bugs.php.net/bug.php?id=44454


-- 
Edit this bug report at http://bugs.php.net/bug.php?id=44454&edit=1

Reply via email to