ID:               46144
 Updated by:       u...@php.net
 Reported By:      Progman2002 at gmx dot de
 Status:           Open
 Bug Type:         MySQLi related
 Operating System: Linux
 PHP Version:      5.2CVS-2009-01-25 (snap)
 New Comment:

After spending the evening on it, I am even more in the mood to change
status to "Won't fix".

Even if we copy error messages to a safe place we need to know when to
return the prepare error message. We should return the prepare error
message, if any mysql C API call made between the failed prepare and the
fetch of the error message by the user have succeeded. "Any" refers to
all calls made implicitly by the destructor of the overwritten statement
object before mysqli->prepare() returns. "Any" must not include calls
made by the user after prepare(). Something simple will not work:

mysql_error():

  if (prepare_error && no_regular_error)
    clean_and_return_pepare_error;

That simple approach would not ensure that "any" is as small as we want
it to be. We would need another flag that tells us if the user has made
any API calls after prepare() and before fetchting the error().

And that gets you into a real mess: intercept all PHP API calls? Maybe
abuse mysql->mysql->net.errno (a private member) again to check if it
gets reset - every C API call resets it. If our special
"prepare_error_fetch" flag is gone, the user must have made an API call
after prepare. Oh, problem, no mysql->mysql struct available in the
statement destructor that calls mysql_stmt_close()... what about
introducing a new global structure for tracking to be able to link
statements and connections, *ouch* -> "Won't fix"...


Previous Comments:
------------------------------------------------------------------------

[2009-09-17 17:41:48] u...@php.net

I don't know what to do with this report because in a way it is a user
error. The problem can easily be avoided by calling the destructor of
the mysql_statement class before assigning false to it. A simple
$stmt->close() before the second $stmt = $db->prepare() will fix it.


What happens is:

 $stmt = ... <valid_sql>
 $stmt = $mysqli->prepare('wrong_sql');

 <switch into php />
 <function_enter> 

    C API: mysql_stmt_init()
    C API: mysql_stmt_prepare() -> error
    copy error, because mysql_stmt_close() will clean it
    C API: mysql_stmt_close()

    RETURN false

 </function_enter>

 <evaluate assignment to $stmt>
    $stmt is a Prepared Statement for <valid_sql>
    destruct $stmt, because user did not clean it up!

    C API: mysql_stmt_close() 
    !!! previously saved error message gets cleaned !!!

 </evaluate assignment to $stmt>

 <assign return value to $stmt />
 <give control back to PHP script />

 var_dump($mysqli->error) -> no error message


One way to fix on the C level would be to blow up the MY_MYSQL struct
and copy the error message into some safe place. But I wonder how we
would know when to return the message copied into a safe place and when
not... currently I can't think of a way how we would know.

It may be possible to hack something with mysqlnd and make mysqlnd
behave different to libmysql (not clean/preserve error message in
mysqlnd' stmt dtor) but that would be just the wrong place and really
hackish....

Maybe we should set the status to "Won't fix".

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

[2009-05-18 14:43:14] Progman2002 at gmx dot de

Sure I can call close() by myself (which I normally do), but it doesn't
solve the bug itself. The point is you get an error situation and don't
know why.

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

[2009-05-18 02:17:56] felix9x at yahoo dot com

It's because the first $stmt object is destroyed by the second
assignment (which clears the last error message).

$sql = 'INSERT INTO
            SomeTest(UserID, RechtID)
        WHERE
            (?,?)';
if (!$stmt = $db->prepare($sql)) {
    die($db->error."-".$db->errno."-".$db->info);
}


Its equivalent to doing this: $stmt = false;
The destructor of the Mysqli_stmt class resets the Last error.

Its possible to call $stmt->close() explicitly. Probably best to use
this syntax:

$sql = 'INSERT INTO
            t(i)
        WHERE
            (?)';
$stmt = $db->stmt_init();
if(!$stmt->prepare($sql) ){
  die( $stmt->error );
}

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

[2009-01-25 10:50:41] Progman2002 at gmx dot de

The bug is still not fixed. Maybe it has something to do with an
uncalled destructor since I use the same variable $stmt.

Actual result:
--------------
PHP-Version: 5.2.9-dev
MySQL-Server-Version: 50060
MySQL-Protocol: 10
-0-

Expected result:
----------------
PHP-Version: 5.2.9-dev
MySQL-Server-Version: 50060
MySQL-Protocol: 10
(Showing a MySQL error which says "Syntax error near WHERE (?,?)" or
says sth. like "unfinished prepare statement before")

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

[2008-11-29 10:59:24] Progman2002 at gmx dot de

As the paste on the pasteboard is gone I'll add the code here.
-----------

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

$db = @new MySQLi('localhost', '', '', 'test');
if (mysqli_connect_errno()) {
    die('Konnte keine Verbindung zu Datenbank aufbauen, MySQL meldete:
'.mysqli_connect_error());
}
echo 'PHP-Version: '.PHP_VERSION."\n";
echo 'MySQL-Server-Version: '.$db->server_version."\n";
echo 'MySQL-Protocol: '.$db->protocol_version."\n";
$sql = 'CREATE TEMPORARY TABLE SomeTest(UserID INT NOT NULL, RechtID
INT NOT NULL)';
if (!$db->query($sql)) {
    die($db->error);
}
$sql = 'DELETE FROM
            SomeTest
        WHERE
            UserID = ?';
if (!$stmt = $db->prepare($sql)) {
    die($db->error);
}
// note the missing $stmt->close() here
$sql = 'INSERT INTO
            SomeTest(UserID, RechtID)
        WHERE
            (?,?)';
if (!$stmt = $db->prepare($sql)) {
    die($db->error."-".$db->errno."-".$db->info);
}
echo "done";
?>

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

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/46144

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

Reply via email to