Nesting LAST_INSERT_ID() in a PDO prepared statement? - php

Using PHP with MySQL and PDO prepared statements, I would like to mimic an opaque id in a simple, safe and efficient way. The idea is to add a random value to the current value of LAST_INSERT_ID.
INSERT INTO table
SET id = LAST_INSERT_ID( LAST_INSERT_ID() + FLOOR(1 + (RAND() * 99)) ),
text = ?,
...
The outer expression sets the id, the inner call gets the current value and modifies it. This works as expected when I test it with the Sequel Pro client. However, in the real script with a prepared statement the last insert id does not get updated/saved. So, I do get random ids, but they are not added up.
What am I missing?

This really does not work, and here's why:
LAST_INSERT_ID() returns 0 on the first call per connection because there is no last insert. Unfortunately, it does not just return the potential next AUTO_INCREMENT value.
If you would split it up into separate commands, first INSERT and then UPDATE within the same statement, LAST_INSERT_ID() would not get updated in between.
More over, "if you mix references to LAST_INSERT_ID() and LAST_INSERT_ID(expr), the effect is undefined" Manual vs. Manual
UPDATEd ids will be ignored by LAST_INSERT_ID()

Related

MySQLi Prepared Statement not executing

The following code runs without any errors but doesn't actually delete anything:
$update = $mysqli->prepare('DELETE FROM table WHERE RetailerID = ? AND Amount = ? AND FXRate = ?');
$update->bind_param('iii', $rID, $base_value, $fx_rate);
$update->execute();
$update->close();
I have numerous mysqli prepared statments in this same file that execute fine, but this one is the only one that doesn't modify the table. No errors or shown, but the row isn't deleted from the table either. I have verified that $rID, $base_value, and $fx_rate are the correct values, and a row is DEFINITELY present in table that matches those values.
The only difference between this statement and the others are the parameters and the fact that it's DELETE instead of SELECT or UPDATE. I also tried doing a SELECT or UPDATE instead of DELETE using the same WHERE parameters, but no luck. The issue seems to be that it's not finding a row that fits the WHERE parameters, but like I said, the row is definitely there.
Any ideas?
Is amount an integer or a double? You're converting to integer ('iii'), but I presume it'll be $0.34 or similar. Try 'idi' instead.
Edit: same applies for rate - is that an integer or double too?

PDO + PHP lastInsertId() issue

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.

Insert into Table 1, get incremented value, and insert into table 2

In php, I'm trying to insert a value into one table, return an auto-incremented value, and then insert that value along with other values into a second table.
I'm running into a few problems. First, while there's a lot of ways of doing this in SQL, I have to do this with php's mysql functions. I'm afraid of weird errors if I combine multiple statements together. Second, like I mentioned, I need this to be done in one query, as it'll be used for a web application.
My current query is like this
INSERT INTO TABLE1 VALUES(*);
INSERT INTO TABLE2
SELECT max(AutoIncrementedColumn)
FROM TABLE1;
The problem I'm having is that mysql_query() doesn't support multi queries. Also, I believe mysql_escape_string() removes anything it believes to be a multi query, so even if I could somehow get mysql_query to believe my query is not a multi query, I'm still out of luck unless I write my own escape method.
Does anyone have any ideas on how to deal with this problem?
EDIT: Forgot to mention that I can't use mysql_insert_id because the column that's autoincrementing is of type Bigint.

php function last_insert_id() is not working with REPLACE INTO query

I am using REPLACE INTO query to insert in to table but after executing query by using mysql_query() function and if I use last_insert_id() it is only giving me 0 value.
why this is happening so? and how can I overcome this behavior.
:Pankaj
You could try using INSERT INTO ... ON DUPLICATE KEY UPDATE instead. It accomplishes the same thing as REPLACE INTO, but in a single server-side operation. REPLACE INTO can end up causing two operations: delete the old one, insert the new one.
But regardless of the query type, you do have to ensure that the table's primary key is an auto_increment field. last_insert_id() does not work properly otherwise.
REPLACE INTO doesn't seem to affect the vaue that can be obtained via last_insert_id().
It seems to be the expected behavior, judging from this :
LAST_INSERT_ID() give wrong value after REPLACE INTO query
LAST_INSERT_ID()

Get autoincrement id after an insert query performed with a prepared statement

If I execute an insert query with a stored procedure with php and the mysqli_* functions, is there a way to retrieve the value of an autoincrement id field?
mysqli->insert_id does not seem to work.
Are you sure the last query you preformed was an INSERT?
mysqli->insert_id seems the proper answer:
Return Values
The value of the AUTO_INCREMENT field that was updated
by the previous query. Returns zero if
there was no previous query on the
connection or if the query did not
update an AUTO_INCREMENT value.
You could try to make a query to MySql like so:
SELECT LAST_INSERT_ID()
Not sure if it works with stored procedures though.
You could add this statement in your stored procedure after the insert:
SET #saved_id = LAST_INSERT_ID()
Then, execute this query after calling the procedure:
SELECT #saved_id
mysqli->insert_id (where mysqli represents your database connection)
must be used directly after the insert. If you run other queries on the same connection
before attempting to read insert_id you get 0 returned.

Categories