mysqli_affected_rows gotcha

Recently I programmed the following screen in PHP:

Not rocket science eh? Well, unbelievably my implementation of the above had a bug. How on earth was that possible?

The bug was the following: If you changed the state, everything worked fine. But if you chose the same state as is already selected, an Exception gets thrown.

Initially I suspected a simple coding mistake. When I looked at the code, everything looked right. I had used the following "algorithm":

I used the PHP function mysqli_affected_rows for that and unbelievably that has the following functionality: it only returns the number of _changed_ rows i.e. the number of rows:

I can't imagine a case where one would want to know that. I couldn't find any function to return the number of rows matching, independent of if the values were changed or not. (The older version mysql_affected_rows exhibits the identical functionality.)

So I had to write the following function:

 * Returns the number of rows which matched the WHERE
 * clause on the last UPDATE statement. This is not the
 * same as mysqli_affected_rows, which only returns the
 * number of changed rows.
public static function DbUpdatedRows() {
    $link = self::DbGetLink();  // mysqli object
    $info = mysqli_info($link);
    if (preg_match('/Rows matched: (d+) +Changed/',
            $info, $matches))
        return $matches[1];
    throw new Exception("DbUpdatedRows called although ".
        "it doesn't look like an UPDATE was the ".
        "last statement: mysqli_info returned '$info'");

I've just checked, and in InnoDB inside a transaction, it's good to see that (as with Oracle) write-locks are indeed placed on all matched rows not just updated rows.

And don't get me started on using DB-specific function calls (i.e. functions named mysql_x) as opposed to using a DB-abstraction layer like DBI in Perl, JDBC in Java, etc. Nor why I'm using PHP or MySQL in the first place.

This article is © Adrian Smith.
It was originally published on 8 Oct 2008
More on: FAIL | PHP | MySQL