Domanda

I have a PHP script that produces a MySQL query that would look something like this:

INSERT INTO `dailydb`.`probact_actionstaken`(`dispatcher`) VALUES ('kryan');

SET @actionTakenId = LAST_INSERT_ID();

INSERT INTO `dailydb`.`probact_actionstaken_types`(`actionTaken`,`type`)
VALUES
    (@actionTakenId, 2);

INSERT INTO `dailydb`.`probact_actionstaken_problems`(`actionTaken`,`problem`)
VALUES
    (@actionTakenId, 2);

INSERT INTO `dailydb`.`probact_actionstaken_actions`(`actionTaken`,`action`)
VALUES
    (@actionTakenId, 3);

INSERT INTO `dailydb`.`probact_actionstaken_text`(`actionTaken`,`input`,`value`)
VALUES
    (@actionTakenId, 3, '7576'),
    (@actionTakenId, 4, 'B61'),
    (@actionTakenId, 5, '4'),
    (@actionTakenId, 6, 'kryan'),
    (@actionTakenId, 7, 'test'),
    (@actionTakenId, 8, 'testing new debug');

SELECT `index`, `timestamp`
FROM `dailydb`.`probact_actionstaken`
WHERE `index`=@actionTakenId;

(note that for the purposes of displaying this query, all values are the result of a manual search-and-replace on :someId with the corresponding value, since I’m using prepared statements)

(also note that probact_actionstaken.index is an auto-incrementing primary key, and probact_actionstaken.timestamp defaults to CURRENT_TIMESTAMP)

The goal of the query is to insert a lot of related data (based on the automatic value of probact_actionstaken.index which is stored in @actionTakenId), and then return the automatically-generated index and timestamp for use (the script is called in an AJAX call that uses this output).

This query executes correctly for the most part: the five INSERT statements all insert the correct values in the database. My issue is that last SELECT statement. I try to go through the result of the query with the following PHP code:

$statement = $dbh->prepare($query);
$statement->execute($params);

$output['results'] = array();
do
{
    $output['results'][] = array();
    while ( $result = $statement->fetch(PDO::FETCH_ASSOC) )
    {
        $output['results'][count($output['results'])-1][] = $result;
        if ( isset($result['index']) )
        {
            $output['index'] = intval($result['index']);
        }
        if ( isset($result['timestamp']) )
        {
            $output['timestamp'] = $result['timestamp'];
        }
    }
} while ( $statement->nextRowset() );

echo json_encode($output);

The idea is to use the do...while block to get through the results from the INSERT statements, and then get the results of that final SELECT statement, which gets stored in my $output.

On my local machine (running PHP 5.4.4), this works perfectly. On the server, which has PHP 5.3.10, $statement->nextRowset() returns false every time, so I only get the first rowset (which is empty because it’s from the INSERT statement). Note that the actual insertions work correctly regardless, and when I run the above manually-filled-in query in phpMyAdmin’s SQL feature, it returns the correct result (a table consisting of the inserted index and timestamp).

Reading about these issues, it seems that multiple statements like this are a bad idea, and don’t actually work with true prepared statements, and I’m still using the default emulated prepared statements. So I could switch to separate calls of $dbh->prepare and $statement->execute, which would solve my problem, but I’m worried about race conditions. The server is using Nginx, and many users could be inputting entries at the same time. I don’t want LAST_INSERT_ID() to return some other user’s entry and associate these fields with the wrong action.

Is there some reason within MySQL or PHP that prevents race condition issues? Some step I could take? Or is there a safe way to use multiple prepared statements? And if the latter, does it work in PHP 3.5.10?

È stato utile?

Soluzione

I don’t want LAST_INSERT_ID() to return some other user’s entry and associate these fields with the wrong action.

The LAST_INSERT_ID() function returns the most recent ID generated in the current session only, not another MySQL session.

See http://dev.mysql.com/doc/refman/5.6/en/information-functions.html#function_last-insert-id

The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.

Because of the likelihood of race conditions, the function would be pretty useless if it weren't limited in scope in this way.

I don't know the answer about the difference you're seeing in processing multi-result queries between PHP 5.3 and PHP 5.4. It's considered best practice to avoid multi-queries.

It's also considered wise to use the same version of PHP (and all software) in your developer environment that you will deploy to in production.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top