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?