The manual for mysql_insert_id() says
Because mysql_insert_id() acts on the last performed query, be sure to
call mysql_insert_id() immediately after the query that generates the
value.
This statement does not indicate any scope. Is it a serverwide value? Could a server running many scripts or many instances of one script return a value for mysql_insert_id() which was not generated by the last insert performed by the script calling it?
The scope of mysql_insert_id() is the MySQL connexion. Not the user + password, but the actual connection for the current script. (Note that the MySQL connection can also be a parameter of mysql_insert_id)
If you close and re-open the MySQL connexion, mysql_insert_id() will not return the id of the id inserted in the previous one.
If an id has been inserted just after your adding, but by another script execution (I mean with another connexion) the mysql_insert_id() will return your ID, not the ID actually created after your but in another connection.
Example :
$c1 = mysql_connect($srv, $usr, $pwd);
$c2 = mysql_connect($srv, $usr, $pwd);
$sql = "INSERT INTO table1 (col1, col2) VALUES('x', 'y') "
mysql_query($sql, $c1); // first insert
mysql_query($sql, $c2); // second insert
$id1 = mysql_insert_id($c1);
$id2 = mysql_insert_id($c2);
$id1 will be the id inserted first, $id2 the id inserted after.
Yes, please see the following article: How to Get the Unique ID for the Last Inserted Row
For LAST_INSERT_ID(), the most recently generated ID is maintained in
the server on a per-connection basis. It is not changed by another
client. It is not even changed if you update another AUTO_INCREMENT
column with a nonmagic value (that is, a value that is not NULL and
not 0). Using LAST_INSERT_ID() and AUTO_INCREMENT columns
simultaneously from multiple clients is perfectly valid. Each client
will receive the last inserted ID for the last statement that client
executed.
The scope is per-connection. If each run of the script opens a separate connection (which is what usually happens), then they will have separate scopes.
Related
If I have this code:
<?php
$q = $sql->prepare("INSERT INTO `table` (row) VALUES ('1')");
$q->execute();
$lastid = $sql->lastInsertId(); // is this a 2nd query?
?>
Would it run as two separate SQL queries?
If so, is there a way to do it in one?
$lastid = $sql->lastInsertId();
it works like a query because it will select from database the last inserted id.
as the documentation said
Returns the ID of the last inserted row, or the last value from a sequence object, depending on the underlying driver.
No, in fact it doesn't run an SQL query.
Here's the line from ext/pdo_msyql/mysql_driver.c:
char *id = php_pdo_int64_to_str(mysql_insert_id(H->server) TSRMLS_CC);
This is a call to the MySQL API, mysql_insert_id(). This internally accesses the last insert id as a property, not by running SQL.
I use PDO transaction
try {
DB::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
DB::$db->beginTransaction();
$db->prepare( insert query );
$db->execute();
$last_insert_id = $db->lastInsertId();
...
...
Multiple concurrent requests are expected on this script.
Question: is it possible that lastInsertId() return incorrect value for the user, who actually inserted the row?
(by "incorrect value" i mean: id that is inserted by some other user).
You're safe. The ID you get will be the correct one.
PDO's lastInsertId (and mysql's last_insert_id to which your PDO delegates the call in this case) gives the last autogenerated ID on a per-connection basis.
From mysql's documentation:
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.
Concurrent connections will not compromise the integrity of the returned id. And as you mention in your comment, transactions have no bearing on this. Just don't forget to commit!
I'll also mention there is the unlikely possibility, if you run multiple statements on the same connection, and if your execute method throws an exception which isnt handled correctly, that lastInsertId could return the id of the last successful insert on that connection. But it can never return an ID from another user's query.
Maybe i have some stupid questions about mysql_insert_id...
I need to get the last inserted Id of a query using mysql_insert_id after mysql_query("INSERT INTO").
mysql_insert_id retrieves the generated Id by the previous query, but my question is...
One php file called test.php with the following code
mysql_query("INSERT INTO table (FirstName, LastName) VALUES ('" . $_POST['FirstName'] . "', '" . $_POST['LastName'] . "'); ")
$lastId = mysql_insert_id();
...continues some code using $lastId
What happens if the test.php file called from different places and different browsers at the SAME TIME ?
or what happens if other php file that containts INSERT INTO query called at the same time ?
What actually Id gets back the mysql_insert_id() ?
We have to do with a rare probability ?
I do not know if I become understandable...
The PHP manual states that the value of mysql_insert_id() returns only the most recently generated AUTO_INCREMENT value:
The value of the MySQL SQL function LAST_INSERT_ID() always contains the most recently generated AUTO_INCREMENT value, and is not reset between queries.
The MySQL manual states the it will return the last AUTO_INCREMENT based on a per-connection basis:
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.
Additionally, you should stop using mysql_ functions as they are being deprecated.
Each client will be sitting on a separate connection. mysql_insert_id() will get the id of the last insert query, based on the passed/current connection. So you don't have to worry about multiple scripts causing problems.
Also, from the code you provided above, your script is vulnerable to a SQL injection attack. Escape your user-input or even better, use PDO/MySQLi.
You get back the autoincrement ID from the last query on your connection. It will not cross connections, so there is no harm in two scripts running simultaneously.
Side note: if you have the option, use the mysqli or PDO libraries instead of the deprecated mysql libarary.
Wouldn't there be a problem with it if for example when a user clicks on a link, a new row is automatically inserted and then the php code requests the last inserted id, and at the same time another row is inserted by another user, so the returned id is actually not the one I'm expecting..?
Am I wrong? Is there a way to do the same without that 'security' hole?
(like maybe from within the prepared statement or something...)
P.S the id is automatically generated.
Thank you.
As mentioned in the manual:
LAST_INSERT_ID() (with no argument) returns a BIGINT (64-bit) value representing the first automatically generated value that was set for an AUTO_INCREMENT column by the most recently executed INSERT statement to affect such a column. For example, after inserting a row that generates an AUTO_INCREMENT value, you can get the value like this:
mysql>SELECT LAST_INSERT_ID();
->195
The currently executing statement does not affect the value of
LAST_INSERT_ID(). Suppose that you generate an AUTO_INCREMENT value
with one statement, and then refer to LAST_INSERT_ID() in a
multiple-row INSERT statement that inserts rows into a table with its
own AUTO_INCREMENT column. The value of LAST_INSERT_ID() will remain
stable in the second statement; its value for the second and later
rows is not affected by the earlier row insertions. (However, if you
mix references to LAST_INSERT_ID() and LAST_INSERT_ID(expr), the
effect is undefined.)
If the previous statement returned an error, the value of
LAST_INSERT_ID() is undefined. For transactional tables, if the
statement is rolled back due to an error, the value of
LAST_INSERT_ID() is left undefined. For manual ROLLBACK, the value of
LAST_INSERT_ID() is not restored to that before the transaction; it
remains as it was at the point of the ROLLBACK.
So, LAST_INSERT_ID() is always transaction-safe (even though you don't use transaction).
The MySQL Server transfers the insert ID as part of the OK message after a successful INSERT. This ID is stored in PDO, therefore without a round-trip to the server PDO can return you the correct ID for your connection in a safe way.
Reference: http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#OK_Packet
To counteract this you would use a transaction.
This would essentially isolate your insert from others, so as long as your Insert/lastInsertId() call is within the same transaction, it will work just fine.
Maybe my question is silly but there's one thing I wish to know about mysql_insert_id(). On php.net is written following:
Retrieves the ID generated for an AUTO_INCREMENT column by the previous query (usually INSERT).
What will be output of this this function if before script reaches line where mysql_insert_id() is used another script inserts something in database?
As far as it's known to me it will return id of the last inserted item (does not matter from where). If I am right, I wish to know how to avoid this problem but using mysql_insert_id() immediately when insert query is executed (because there still is a little chance to get in "trouble").
It will return the id of the last row that was inserted in the current session, so you have nothing to fear. In fact you pass the link identifyer as argument or otherwise it defaults to the last link opened. From the docs
The MySQL connection. If the link identifier is not specified, the
last link opened by mysql_connect() is assumed. If no such link is
found, it will try to create one as if mysql_connect() was called with
no arguments. If no connection is found or established, an E_WARNING
level error is generated.
So if you have
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
mysql_select_db('mydb');
mysql_query("INSERT INTO mytable (product) values ('kossu')");
printf("Last inserted record has id %d\n", mysql_insert_id());
This will always be correct (if mytable has an AUTO_INCREMENT column, of course) because it retrieves the last generated id in the session. If at the same time another user runs exactly the same query, he would have a different session and he would get his id correctly