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. :)
Related
how can i start a transaction in mysqli and PDO? and how can i commit it? i have seen 2 ways : using autocommit(false) and begin_transaction methods , but which one is the best way? i got headache! autocommit(false) or begin_transaction?
Your question isn't clear still I'm answering it. It is straightforward to begin and commit a transaction. In PDO, do something like this:
<?php
$pdo = // connect to database with PDO
$pdo->beginTransaction();
// query database here
$result = // query result
if($result) {
// if result is okay
$pdo->commit();
} else {
$pdo->rollBack();
}
?>
**Explanation: **
First of all, you start a transaction
Next, you query database
If result is okay, commit the transaction
If result is not okay, rollBack and the query will not execute
Reference: http://php.net/manual/en/pdo.transactions.php
About Auto-commit: In auto-commit mode, each query is a complete transaction and it is executed instantly. By default, PDO auto-commits each query. Turning off the auto-commit will need you to commit the query manually. In general scenario, you should not turn it off.
When you need to execute an important query, i.e multi-part queries that depend on each other for their final result like an amount transfer between two accounts (where you need to deduct amount from one table and to add it to another table), simply begin a transaction. PDO will not execute any query until you commit the transaction. If something goes wrong, everything will be rolled back to its previous state.
Lastly, there isn't any big difference between turning off auto-commit and beginning a transaction. By beginning transactions, you can simplify the tasks otherwise you will need to manually commit each query regardless of its nature.
I hope it answers your question.
I have a system that handles many queries per second. I code my system with mysql and PHP.
My problem is mysqli transaction still commit the transaction even the record is deleted by other user at the same time , all my table are using InnoDB.
This is how I code my transaction with mysqli:
mysqli_autocommit($dbc,FALSE);
$all_query_ok=true;
$q="INSERT INTO Transaction() VALUES()";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
$q="INSERT INTO Statement() VALUES()";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
if($all_query_ok==true){
//all success
mysqli_commit($dbc);
}else{
//one of it failed , rollback everything.
mysqli_rollback($dbc);
}
Below are the query performed at the same time in other script by another user and then end up messing the expected system behaviour,
$q="DELETE FROM Transaction...";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
Please advice , did I implement the transaction wrongly? I have read about row-level locking and believe that innoDB does lock the record during a transaction
I don't know which kind of transactions you're talking about but with the mysqli extension I use the following methods to work with transactions:
mysqli::begin_transaction
mysqli::commit
mysqli::rollback
Then the process is like:
Starting a new transaction with mysqli::begin_transaction
Execute your SQL queries
On success use mysqli::commit to confirm changes done by your queries in step 2 OR on error during execution of your queries in step 2 use mysqli::rollback to revert changes done by them.
You can think of transactions like a temporary cache for your queries. It's someway similar to output caching in PHP with ob_* functions. As long as you didn't have flushed the cached data, nothing happens on screen. Same with transactions: as long as you didn't have commited anything (and autocommit is turned off) nothing happens in the database.
I did some research on row level locking which can lock record from delete or update
FOR UPDATE
Official Documentation
Right after the begin transaction I have to select those record I wanted to lock like below
SELECT * FROM Transaction WHERE id=1 FOR UPDATE
So that the record will be lock until transaction end.
This method doesn't work on MyISAM type table
Looks like a typical example of race condition. You execute two concurrent scripts modifying data in parallel. Probably your first script successfully inserts records and commits the transaction, and the second script successfully deletes records afterwards. I'm not sure what you mean by "the query performed at the same time in other script by other user" though.
You will have to do this this way:
mysqli_autocommit($dbc,FALSE);
$dbc->begin_transaction();
$all_query_ok=true;
$q="INSERT INTO Transaction() VALUES()";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
$q="INSERT INTO Statement() VALUES()";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
if($all_query_ok==true){
//all success
mysqli_commit($dbc);
}else{
//one of it failed , rollback everything.
mysqli_rollback($dbc);
}
you can use the object oriented or the procedural style when calling begin_transaction (I prefer the object oriented).
I'm having a weird issue with connecting to the master database twice.
$devdb01 = $this->load->database('master',true);
$dbstate = $this->load->database('mastercheck',true);
I have 2 different connection id's but it seems if i'm running a query on the master database it won't execute another query even though its a separate database connection. Basically i'm trying to use a SP that runs a restore database and then run another query checking if the database is still in a restoring state.
I run the queries like the following:
$dbresult=$devdb01->query($query);
$dbcheck=$dbstate->query($checkquery);
echo $devdb01->conn_id;
echo $dbstate->conn_id;
$query = "EXECUTE master..sqlbackup '-SQL \"RESTORE DATABASE $dbname FROM DISK = [$restoredir$backupselected] WITH REPLACE, MOVE [$dataname] TO [$datadir$dbname$dataprepend] , MOVE [$logname] TO [$logdir$dbname$logprepend]\"'";
$dbresult=$devdb01->query($query);
$checkquery="SELECT state_desc from sys.databases where [name]='$dbname'";
$dbcheck=$dbstate->query($checkquery);
var_dump($dbcheck);
I just get an output of bool(false) for that var_dump. I verified that the connection id's are different after connecting to the database.
When I try to check the result of the 2nd query I get an error saying i'm calling a member function on a non-object so it must not be executing the query. If I reverse the order it will execute the other query like expected.
I can't tell if this is a PHP limitation? I'm using code igniter so it could be something related to that as well. I tested with SQL Management Studio and it works fine in separate query windows connecting multiple times to the master database. What i'm trying to accomplish is to come up with a way to run this first query then not do anything until I know that query is completed first.
Update
It appears if I turn off Persistent Database connections by disabling pconnect it will use the same connection twice.
This is the output from the connection id with pconnect set to false:
Resource id #52Resource id #52
I got a very weird problem with oracle today.
I setup a new server with xampp for developing, i activated mssql and oracle and everything was just fine until i tried to execute an update statement.
Every select, insert, etc is working fine with PHP 5.3.
I also can parse the statement and get a ressource id back, but when i try to execute the statement my whole site is not responding.
no error, nothing. just timeout until i restart the apache.
here the code... it's the test code, so there should be no problem at all.
$conn = oci_connect('***', '***', '***');
$query ="UPDATE CHAR*** SET TPOS = 14, ID = 5, DIFF = 'J' WHERE ***NR = '3092308' AND LA*** = '5'";
echo $query;
echo '<br>';
echo $stid = oci_parse($conn, $query);
oci_execute($stid, OCI_DEFAULT);
oci_free_statement($stid2);
Any hints or ideas? :-(
I already tried to reinstall the oracle instant client and another version. I am using 10g like our db at the moment.
best regards
pad
The row may be locked by another session. If this is the case, your session will hang until the other transaction ends (commit/rollback).
You should do a SELECT FOR UPDATE NOWAIT before attempting to update a row (pessimistic locking):
If the row is locked, you will get an error and can return a message to the user that this record is currently being updated by another session. In most cases an explicit message is preferable to indefinite waiting.
If the row is available, you will make sure no session modifies its content until you commit (and thus you will prevent any form of lost update).
There are other reasons why a simple update may take a long time but they are less likely, for instance:
When you update an unindexed foreign key, Oracle needs to acquire a lock on the whole parent table for a short time. This may take a long time on a busy and/or large table.
There could be triggers on the table that perform additional work.
For further reading: pessimistic vs optimistic locking.
Can the php function mysql_insert_id() return no result after processing the INSERT query in mysql db?
Just to clarify. There was a script performing by cron on the production site. It contained a cycle for generating invoices for users. Each iteration consists of a INSERT db query and the mysql_insert_id() operation going right after the query - to fetch the generated invoice number. A set of iterations were performed without fetching the last inserted number.
Can it be caused by high db server load or by some other reasons that are not linked to the problem at the php code site?
Any help would be appreciated!
Offhand, I can think of a few cases where MySQL wouldn't return the ID:
The table you're inserting into doesn't have an AUTO_INCREMENTed primary key.
You're inserting multiple rows at once.
You're calling mysql_insert_id() from a different connection than the INSERT query was executed.
The INSERT query didn't succeed (for instance, it encountered a deadlock). Make sure you are checking the return value from mysql_query(), then use mysql_errno() and mysql_error().
MySQL docs have a full list of conditions and details on how this function works.
Of course, it's also possible there is a bug in MySQL, which would depend on which version of MySQL you are using.
If you're running the commands through a shell script, and run them both separately as in;
mysql -e "insert into table ( field1 ) values ( 'val1' );" "database"
lastId=`mysql -e "select last_insert_id();" "database"`
Then that won't work as the second call makes a new connection to the server. You need to do something like the following, as it is all done within a single database call / connection;
lastId=`mysql -e "
insert into table ( field1 ) values ( 'val1' );
select last_insert_id();
" "database"`
You'll need to look up the extra parameters required for the MySQL command to remove formatting and header row - I'm afraid I can't remember them off the top of my head!