Hi,

I'm new to the list, so I apologize if this is the wrong place to post!

Anyway, several years now, I've been struggling with errors such as:

PHP Warning: <function>: <num> is not a valid MySQL result resource in <file>

at various places on the sites I work on. But the warnings only happens a few times a day, despite the thousands of visitors we have daily. Besides, I've carefully verified that the PHP code is in fact not wrong (like duplicate mysql_free_result and the likes). Eventually we ignored the problem due to its rarity.

Lately however, the likelihood of errors at certain places in our system increased, which made my boss grant me indefinitely time to solve the problem.

After some three to four days of analysis of live memory dumps, I traced the problem to be the active_result_id field within the php_mysql_conn structure. Apparently this field is not cleared when a persistent MySQL connection is pulled from the list, so should you accidentally hit that same resource id and then perform another query, your previous query was freed.

An example:

bug.php:

<?php

header("Content-Type: text/plain");

mysql_pconnect("hostname", "username", "password");

if ($_GET["step"] == 1)
{
  $res = mysql_unbuffered_query("SELECT * FROM db.table");
  echo("Created resource ".(int)$res."\n");
  mysql_free_result($res);
}
else if ($_GET["step"] == 2)
{
  $res1 = mysql_query("SELECT * FROM db.table");
  echo("Created resource ".(int)$res1."\n");
  $res2 = mysql_query("SELECT * FROM db.table");
  echo("Created resource ".(int)$res2."\n");
  mysql_free_result($res2);
  if (!is_resource($res1))
    echo("Resource ".(int)$res1." was destroyed\n");
  else
    mysql_free_result($res1);
}
?>


Now, if I open bug.php?step=1 and then bug.php?step=2 within the same HTTP connection, I get:


bug.php?step=1:
Created resource 4


bug.php?step=2:
Created resource 4
Created resource 5
Resource 4 was destroyed


The thing is, when bug.php?step=1 is called, a MySQL connection is established and resource 3 contains a MySQL structure and active_result_id is set to 0. Then we perform an unbuffered query which get resource id 4. This id is stored in active_result_id.

When we call bug.php?step=2 immediately after, we get the very same MySQL connection as well as the active_result_id set to 4. Then we perform the first query, and while mysql_query actually looks for resource #4, it doesn't find it and so does nothing. But then it performs the query and creates resource #4. Finally we perform the 2nd query, but only this time resource #4 actually exist, so the resource is freed before the query is performed, thus resulting in subsequent errors.


So, to summarise the problem is really quite simple. active_result_id is only cleared within php_mysql_do_connect if the connection is not persistent, or if a persistent connection was not found. So the natural solution is simply to clear it as it should (patch attached)


I'm sorry if my mail is too large, but my experience with other open source projects, is that a full description of the cause and reason of a patch increase the chance of the patch getting accepted (and understood).


For the record, the problem apparently affects all versions of PHP. (tested on various home-built versions up to and including 4.4.7, as well as Debian built PHP 5.2.0-8). I might also contact the Debian PHP maintainers regarding the bug, so that I won't have to use home-built packages on all our servers (or turn off persistent connections).


Best regards,
 Peter Christensen
diff -Nru php-src.old/ext/mysql/php_mysql.c php-src.new/ext/mysql/php_mysql.c
--- php-src.old/ext/mysql/php_mysql.c	2007-06-07 13:20:09.000000000 +0200
+++ php-src.new/ext/mysql/php_mysql.c	2007-06-07 13:20:19.000000000 +0200
@@ -732,6 +732,7 @@
 #endif
 
 			mysql = (php_mysql_conn *) le->ptr;
+			mysql->active_result_id = 0;
 		}
 		ZEND_REGISTER_RESOURCE(return_value, mysql, le_plink);
 	} else { /* non persistent */

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to