It's my first time working with Firebird database and I need some help in the transactions department. I need to run multiple functions inside a ''main function'', those functions have queries, but if one fails, I need them all to rollback. Can I open the transaction in the top of the main function and close it at the bottom or do I have to open and close for each query? I'll post an example.
public function Main_function()
{
$id = $this->create_user_id();
$connection = ibase_connect($this->_db, $this->_username, $this->_password, '100');
$trans = ibase_trans($connection, IBASE_READ+IBASE_COMMITTED+IBASE_REC_NO_VERSION);
$query = "INSERT INTO USERS (user_id, name) VALUES ('john', '$id')";
$newuser = ibase_query($trans, $query);
$return = $this->insert_new_job($id);
ibase_commit($trans);
ibase_close($connection);
}
public function create_user_id()
{
$id = '2';
return $id;
}
public function insert_new_job($id)
{
///DO I NEED TO OPEN A NEW TRANSACTION OR THE OTHER IS STILL ACTIVE?
$query = "INSER INTO jobs (name, id, job) VALUES ('john',$id,'developer') ";
$result = ibase_query($trans, $query);
return $result;
}
If you want all statements executed in a single transaction, then you need to start the transaction once, use it for all statements you want in that transaction, and then commit at the end of the unit work. In other words: you should not start a transaction for each individual statement.
Starting a transaction for each statement would not have your desired result, as each would be executed in its own transaction, which is - by your own words - not what you want. In addition, as Firebird supports multiple active transactions on a single connection, you would have the added problem that you need to ensure that you keep track of all transactions handles to properly commit or rollback, as starting a new transaction will not automatically end a previous transaction.
As an aside, I don't program in PHP, but it looks like $connection and $trans (and other variables) are global variables, and it would be better to declare them as local variables and pass them to your query methods instead of relying on access to global variables.
Related
Below is the syntax from a Model using Codeigniter Framework. So i want to do this kind of query, i grouped many query inside another class like this, but i used this transaction outside the class like this.
$this->db->trans_begin();
$insert1 = $this->Actmain->tempPerwakilanToTransaksi($t_nib_kppa_temp['id_permohonan']);
$insert2 = $this->Actmain->transaksiPerwakilanToHistory($t_nib_kppa_temp['id_permohonan'],$arrData['id_history']);
if(!$insert1 || !$insert2) {
$this->db->trans_rollback();
return FALSE;
} else {
$this->db->trans_commit();
echo "Yatta desune"
}
I want to check if at least one query that didn't work successfully, it will rollback all the query. The problem is, it's always return true which came from query in variable $insert1 or $insert2. It's always echo Yatta Desune but the insert and update query didn't executed.
What is the proper way to used db transaction in Codeigniter 3 but bunch of query executed in different Model ?
How could I make a transaction using multiple php controllers to make the queries?
Currently, my code is like this (A bit more complex, but I am not allowed to show anymore)
$operation = new Operations();
$bill = new Bills();
$employee = new Employee();
$operation->setName("Name");
$operationCreated = $operation->create();
$result = "";
if($operationCreated){
$bill->setAmount(1000);
$billCreated = $bill->create();
if($billCreated){
$employee->setName("Name");
$result= $employee->create();
}
}
return $result;
The way it is right now leads to a problem where I might have an operation created well, but no billing and no employee and my database ends with a row I don't want because I don't have all the information I need in the database.
I need a way to revert the changes if any one of them fails, and I think a transaction would do the trick, but I don't know how can be done. All three classes extend the Database class that has the create() method.
I have a PHP function written that takes a series of parameters and generates a string. That string is then used in a Magento $readConnection->fetchOne($thestring); request and fails to actually read from the database, indicating via error message that the call to fetchOne is on a non-object.
However, the Varien_DB method takes the string query as a parameter, and should return an array as the result.
function updateDB($tablename,$entity,$selector,$id){
$query = 'SELECT entity_id, `value` FROM '.$tablename.' WHERE entity_id = '.$entity.' AND '.$selector.' = '.$id;
$result = $readConnection->fetchOne($query);
}
The reason behind writing this as a function is that I have a large selection of products that get updated pricing on a daily basis, but not all receive the same updates. In addition, the prices are located on different tables in the database, so by passing the relevant values to the function I can run the appropriate query.
The $query generated by passing the values to the function is a valid string, and can be used by itself as a query against the database (verified through Navicat against the db). When the $query is used by $readConnection->fetchOne, it returns an error of "Call to a member function fetchOne() on a non-object", so I am not sure what is going on. The $query does not need to be an object, nor does it fail when run by itself against the db.
Per comments, $readConnection is defined prior to calling the function. I can set it as a global variable and see if that works.
$resource = Mage::getSingleton('core/resource');
$readConnection = $resource->getConnection('core_read');
$writeConnection = $resource->getConnection('core_write');
I am trying to add more error outputs to my code as it is growing. But I am not sure how deep I need to go. For instance, if I send a PDO DELETE FROM command to a table, is it necessary for me to then query the table again to see if the row was deleted (and confirm to the user)? Or should I rely on error handling, as in, if there was no error, it was definitely successful.
Thanks.
You should wrap every SQL statement in a try and catch block to see if any SQL exceptions or any other exceptions occured.
Depending on how you execute your PDO queries you have two options.
You can execute a statement directly with the PDO class using the exec() function. This function will return the number of affected rows for each query. However this function does not return the number of affected rows for an SELECT query.
So for delete queries you would use
$connection = new PDO($dsn, $username, $password);
$affectedRows = $connection->exec('DELETE from users WHERE id = 1);
This way affectedRows will probably be 1 or 0 if the user doesn't exist. If any other errors occur the catch part of the try-catch block will make sure to fetch these.
If you want to execute an SELECT query and find out if it was successful I would recommend doing this
$connection = new PDO($dsn, $username, $password);
$statement = $connection->prepare(SELECT * FROM users);
$statement->execute();
$returnedUsers = $statement->fetchAll();
As you can see, calling the method $connection->prepare() will return a statement which is an object of the PDOStatement class.
If you have parameters set in the prepare query you would want to set these through the bindParam() methods of the PDOStatement class.
Keep in mind that when it's neccesary to execute multiple similar queries in one go use an object of PDOStatement and then set different parameters using the bindParam() method.
i have to do 2 MySql-queries:
SELECT id FROM X WHERE [...]
INSERT [...]
The second query should only be executed, if the first query returns an correct id.
Is it possible, to mix PHP conditions between both queries?
Eg.
try
{
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$stmt = $dbh->prepare("SELECT id FROM [...]");
$stmt->bindParam(1, [...]);
if($stmt->execute())
{
if($row = $stmt->fetch())
{
$matchID = $row['id'];
$checkD = $this->checkId($matchID);
if($checkD)
{
return '-1';
}
else
{
$stmt = $dbh->prepare("INSERT INTO [...]");
$stmt->bindParam(1,[...]);
$stmt->execute();
stmt = $dbh->prepare("DELETE [...]");
$stmt->bindParam(1,[...]);
$stmt->execute();
$dbh->commit();
return $matchID;
}
}
else
{
return '-1';
}
}
else
{
return '-1';
}
} catch(Exception $e)
{
$dbh->rollBack();
return '-1';
}
Is this correct? (i get zero errors)
If not: how can i realize it?
I want to be sure, that no other user could reach the INSERT query, when annother is performing the 1. query.
Transactions are isolated from the current data. How they behave exactly is dependent on the isolation level they use. For example a transaction with serializable isolation level completely lives in the past, and it knows nothing of the data changes have been made since the beginning of the transaction.
If you want to prevent anybody to do changes on the database while your script is working on something, then you have to lock your database, tables or rows. This is usually not necessary, with the proper code.
In your case
you can use read committed transaction isolation level (default) call the DELETE after the SELECT, and check whether there are affected rows by the DELETE before the INSERT
if you don't want to change the order of your queries then you can
simply throw an exception if the DELETE does not affect any row, and so the INSERT will be rolled back
add a constraint which prevents multiple INSERTs with the same data, so the duplicated INSERT will violate a constraint, and it will be rolled back
lock the rows with a SELECT FOR UPDATE
This rather makes no sense. there is no use for transactions at all. What you want to roll back? a SELECT query result?
I want to be sure, that no other user could reach the INSERT query, when another is performing the 1. query.
This is achieved via table locking, not transactions. Or, rather by the simple key uniqueness. Without knowing your business logic it's impossible to answer more. Better ask another question, explaining user experience, and avoiding technical terms like "transaction" at all.