Last id with persistent connection - php

I'm currently using mysql_pconnect.
Is there a risk of erroneously retrieving the last id inserted?

Yes, there is a risk not only with last_insert_id but with transactions and other things.
mysql_pconnect isn't right thing for use it on production because many php instances will have access to a single connection

It seems, according to this note, that there might be a risk, when the insert query failed (quoting) :
be careful when using
last_insert_id() with persistent
connections - running
last_insert_id() after a failed
update/insert/etc will return the last
insert id of the last successful
update/insert made by that CONNECTION
rather than 0 for the number of rows
updated by the previous non-working
query

Related

When I execute a prepared statement to select rows in a db table, does pdo "fetch" the records and caches it?

When I execute a prepared statement to select rows in a db table, does pdo "fetch" the records and caches it?
i.e. If I perform a fetch after executing a "select" statement, does pdo perform multiple db calls for each record I want fetched or does it simply fetch each record from its cache? (assuming it has cache)
tnx.
When you ask does pdo perform multiple db calls for each record if you mean a database connection or query by saying calls, then no. the way it works pdo opens the connection, query once then if you use fetchAll() it gets all the values at once while fetch() will get one value at the the time.
The caching you are referring when using prepare() a and execute() for statements that will be issued multiple times with different parameter values optimizes the performance of your application by allowing the driver to negotiate client and/or server side caching of the query plan and meta information.
Unless you are fetching huge amount of records (into the memory) you should not be concern with the cost of using fetch.
Sooo.. to answer your question PDO will not cache fetch , it will be stored into memory.

Reliable or not PDO lastInsertId() when using transactions

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.

What happens if mysql_insert_id called form different places and different browsers at the same time?

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.

MySQL commit and transaction

I have a question regarding MySQL commits and transactions. I have a couple of PHP statements that execute MySQL queries. Do I just say the following?
mysql_query("START TRANSACTION");
//more queries here
mysql_query("COMMIT");
What exactly would this do? How does it help? For updates, deletes and insertions I also found this to block other queries from reading:
mysql_query("LOCK TABLES t1 WRITE, t2 WRITE");
//more queries here
mysql_query("UNLOCK TABLES t1, t2");
Would this block other queries whatever nature or only writes/selects?
Another question: Say one query is running and blocks other queries. Another query tries to access blocked data - and it sees that it is blocked. How does it proceed? Does it wait until the data is unblocked again and re-execute the query? Does it just fail and needs to be repeated? If so, how can I check?
Thanks a lot!
Dennis
In InnoDB, you do not need to explicitly start or end transactions for single queries if you have not changed the default setting of autocommit, which is "on". If autocommit is on, InnoDB automatically encloses every single SQL query in a transaction, which is the equivalent of START TRANSACTION; query; COMMIT;.
If you explicitly use START TRANSACTION in InnoDB with autocommit on, then any queries executed after a START TRANSACTION statement will either all be executed, or all of them will fail. This is useful in banking environments, for example: if I am transferring $500 to your bank account, that operation should only succeed if the sum has been subtracted from my bank balance and added to yours. So in this case, you'd run something like
START TRANSACTION;
UPDATE customers SET balance = balance - 500 WHERE customer = 'Daan';
UPDATE customers SET balance = balance + 500 WHERE customer = 'Dennis';
COMMIT;
This ensures that either both queries will run successfully, or none, but not just one.
This post has some more on when you should use transactions.
In InnoDB, you will very rarely have to lock entire tables; InnoDB, unlike MyISAM, supports row-level locking. This means clients do not have to lock the entire table, forcing other clients to wait. Clients should only lock the rows they actually need, allowing other clients to continue accessing the rows they need.
You can read more about InnoDB transactions here. Your questions about deadlocking are answered in sections 14.2.8.8 and 14.2.8.9 of the docs. If a query fails, your MySQL driver will return an error message indicating the reason; your app should then reissue the queries if required.
Finally, in your example code, you used mysql_query. If you are writing new code, please stop using the old, slow, and deprecated mysql_ library for PHP and use mysqli_ or PDO instead :)

SQL server transactions in PHP

I'm trying to grasp the idea of transactions fully. Therefore the following question... (ofcourse newbie, so don't laugh :D )
I have set up a (simplified) transaction in PHP (using the PHP SQL driver from microsoft). I want to get the rows I'm going to delete for some extra processing later:
sqlsrv_begin_transaction($conn);
$sql = "SELECT * FROM test WITH (XLOCK) WHERE a<10";
$statement = sqlsrv_query($conn,$sql);
$sql = "DELETE FROM test WHERE a<10";
sqlsrv_query($conn,$sql);
$result = get_result_array($statement);
sqlsrv_commit($conn);
$result2 = get_result_array($statement);
1) I do get the expected result in $result but an empty array in $result2. Why?
I would expect only a result in $result2 because then the transaction has actually been executed. I guess the result in $result is a sort of 'temporary' result in memory and not actually a result from the actual database.
2) It could be that between the moment the transaction was started and the actual commit, an other query from another connection has changed the rows which match (a<10)? That means that the results I'm expecting according to $result will be different from the actual changes in the database.
Or is it that (a) the transaction occurres with an in-memory copy of the database (not affected by in-between queries from other connections), or (b) the locks obtained since the beginning of the transaction are already in action for other queries from other connections?
After typing this I'm expecting answer b....?
I'm not familiar with the sqlsrv driver, but if it works anything like most other PHP DB drivers, the result of the sqlsrv_query call is not a result set in some form of array, but a PHP resource (see http://www.php.net/manual/en/language.types.resource.php). Calling get_result_array still retrieves data from that resource, in this case the database, and it does so immediately. The COMMIT only affects writes to the database, not reads, so you see your result immediately in result1. After you commit your transaction (i.e, the DELETE), the next call correctly returns an empty result set.
I tested it out with some mysql tools (which i'm more familiar with):
1. When I start a transaction and do a 'select' of one particular record I directly get the result. Then from an other connection I delete the same record (with autocommit) it is gone for that connection but for the first connection the record is still there (I did the 'select' again without committing the transaction). Only after committing the transaction of the first connection and doing the 'select' again the record is gone.
2. When I do the same but acquire an exclusive lock for the first 'select' query then the delete query of the second connection waits until the transaction of the first connection has been committed.
Conclusion: In situation (1) for the second select query of the first connection, the database IS returning a result as it was at the moment of the start of the transaction... thus WITHOUT taking into account other (write) queries running AFTER the start of the transaction. Situation (2) is exactly the answer 2b from my original question. :)

Categories