This is my (with little pseudo-) code:
$this->db = new mysqli ( $host, $user, $dbpw, $database );
try {
$this->db->mysqli->autocommit(FALSE);
$sql = 'SOME SQL STATEMENT';
$this->db->execute( $sql );
$this->db->mysqli->commit();
}
catch (Exception $e)
{
echo($e);
$this->db->mysqli->rollback();
}
db-class has some enhancements i need but is capable of using the complete mysqli-class.
i have 2 questions.
a. why cant i start the transaction with
$this->db->mysqli->begin_transaction();
its throwing unknown method error. i really dont understand that.
b. do i have to use
$this->db->mysqli->autocommit(TRUE);
?
a. Because mysqli::begin_transaction appeared in PHP 5.5.0. Your PHP version is lower it seems.
b. Depends on your requirements. Enabling autocommit will make MySQL commit transaction automatically and begin another one after executing each SQL statement.
Related
We're developing a PHP application that connects both to a PostgreSQL server and an IBM i server with DB2. While the PDO connection to PGSQL works just fine, the connection to DB2 can only fetch from tables; trying to Insert or Delete results in the following error:
SQLSTATE[HY000]: General error: -7008 (SQLExecute[4294960288] at /build/php7.0-ltLrbJ/php7.0-7.0.33/ext/pdo_odbc/odbc_stmt.c:260)
This error happens both on our development and production environments. Both servers are Ubuntu (different versions, but not by much); I'm using the ODBC driver for PDO.
We tried to connect to other IBM i servers, and with different users, but the exact same problem still arises. Can Select, but not Insert. Googling the error code doesn't yield any useful result, and as you can see the error message itself it's as unhelpful as can be. The code in the SQLExecute particularly doesn't appears anywhere, not even a single result (there is a result from an IBM page, but it's actually a different error code).
The code is pretty simple, but perhaps there is some obvious and glaring error there.
The test script:
include("DB2.php");
$oDAO = new DAO();
$res = $oDAO->ejecuta("INSERT INTO <Library>.<File> VALUES (1,0,1)");
The DAO:
class DAO{
var $link;
public function __construct(){
// función constructora, inicia la conexión
$this->link = new PDO("odbc:DRIVER={IBM i Access ODBC Driver};SYSTEM=<System>;PROTOCOL=TCPIP",
'<user>', '<pass>');
$this->link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
private function begin() { $this->link->beginTransaction(); }
private function rollback() { $this->link->rollBack(); }
private function commit() { $this->link->commit(); }
public function ejecuta($query){
try{
$this->begin();
$oResult = $this->link->query($query);
if($oResult){
$bResult = true;
$this->commit();
}else{
$bResult = false;
$this->rollback();
}
}
catch (Exception $e){
echo $e->getMessage();
$bResult = false;
$this->rollback();
}
return $bResult;
}
}
Frankly, we're out of options and I already wasted two weeks with this. We just need to insert and delete records. So any help is welcome.
The symptoms you describe are consistent with attempting to modify the database under commitment control, but without journaling enabled.
There are three common ways to deal with this:
Turn journaling on. This is pretty extreme, since the folks who administer the database would have to do this, and if they've got journaling turned off, it's likely they either don't really know how to deal with journals, or don't want to. But it's the only practical way to have full commitment control on Db2 for i.
Connect with autocommit on. This will add an implicit commit to any database-modifying SQL statements executed with this connection. In my experience, this is the most common and convenient way to handle the situation.
Add WITH NC to each relevant SQL statement. In principle, this gives you statement-by-statement control over whether to suspend commitment control. In practice, if you are thinking of doing this in the first place, you probably don't have journaling enabled, and thus you will have to do this on each and every database-modifying SQL statement. This is why most people gravitate toward option 2.
i wonder how does transaction work.
Question is do I need to rollback if any error/exception occurs, where the Exception will be thrown and never reach the commit() call? If so why and how? and what rollback does exactly?
consider the followings PHP code (some Java code does similar things like so):
public function deleteAll():void{
$stmt = $this->con->prepare(
'DELETE FROM dd WHERE 1'
);
$this->con->begin_transaction();
if(!$stmt->execute()){
throw new Exception($stmt->error);
}
$this->con->commit();
}
public function insert(array $list):void{
$stmt = $this->con->prepare(
'INSERT INTO dd(cc) VALUES(?)'
);
$stmt->bind_param('s', $v);
$this->con->begin_transaction();
foreach($list as $v){
if(!$stmt->execute()){
throw new Exception($stmt->error);
}
}
$this->con->commit();
}
Note:
$this->con is the mysqli connection object (not using PDO, although different but PDO and mysqli will do similar thing like above).
database is MySQL, table using InnoDB engine.
Some people code shows they catch the Exception and do rollback. But I did not since the commit() is never executed if Exception is thrown. Then this let me wonder why does it need to rollback at all.
My assumption is if there are multiple queries to perform at once like the following:
public function replaceAllWith(array $list):void{
$this->con->begin_transaction();
try{
//do both previous queries at once
$this->deleteAll();
$this->insert($list);
$this->con->commit();
}catch(Exception $e){
//error
$this->con->rollback();
}
}
but then it still cannot rollback because each method/function is done and committed by different method and different transaction.
Second Question: Do I do each transaction in each method call, or only invoke transaction in the 'boss/parent' function which only call and coordinate 'subordinate/helper' functions to do work?
Not sure if I'm asking the right questions.
Database Transaction is confusing, many online resources show only the procedural process (commit only or then immediately rollback which make no differences to normal direct query) without OO concepts involved. Please any pros guide and give some advice. tq.
I've browsed the web quite a bit before asking here; I noticed that some people have the same problem as me, but none of the answers that were given to the others didn't solve my problem, so....
I have a basic PDO Update Statement inside a public function:
public function editRank($name, $rank){
$query = "UPDATE `chat_mod` SET `chat_mod_rank` = :rank WHERE `chat_mod_ign` = :username";
$prepare = $this->_db->prepare($query);
$array = array(
':rank' => $rank,
':username' => $name
);
try {
$prepare->execute($array);
} catch (PDOException $e){
echo 'Error: ' . $e->getMessage();
return false;
}
return true; // If no PDO Exception is thrown..
}
No exception is thrown, so the function always returns true; but the rows are not being updated. Yes, I've checked that the rows are named properly and the values are not nulls.
Thanks,
Tom.
P.S Other queries like Select, Add & Delete work fine.
You are catching PDO exceptions but did you tell PDO to throw them?
To make PDO throw exceptions you have to configure PDO errmode. Note that setting this mode as a connection option will let PDO throw exceptions on connection errors too, which is very important.
So, here is an example for creating a PDO connection right way:
$dsn = "mysql:host=$host;dbname=$db;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// other options
);
$pdo = new PDO($dsn, $user, $pass, $opt);
Connecting this way, you will be always notified of all database errors, occurred during query execution. Note that you have to be able to see PHP errors in general. On a live site you have to peek into error logs, so, settings have to be
error_reporting(E_ALL);
ini_set('display_errors',0);
ini_set('log_errors',1);
while on a local development server it's ok to make errors on screen:
error_reporting(E_ALL);
ini_set('display_errors',1);
and of course you should never ever use error suppression operator (#) in front of your PDO statements.
Also, due to many bad examples telling you to wrap every PDO statement into try..catch block, I have to make a distinct note:
DO NOT use try..catch operator just to echo an error message. Uncaught exception is already excellent for this purpose, as it will act just the same way as other PHP errors - so, you can define the behavior using site-wide settings - so, you will have your error message without this useless code. While unconditionally echoed error message may reveal some sensitive information to a potential attacker, yet confuse a honest visitor.
A custom exception handler could be added later, but not required. Especially for new users, it is recommended to use unhandled exceptions, as they are extremely informative, helpful and secure.
Use try..catch only if you are going to handle the error itself - say, to rollback a transaction.
Below are my queries
$db= new mysqli('localhost','user','passcode','dbname');
try {
// First of all, let's begin a transaction
$db->beginTransaction();
// A set of queries; if one fails, an exception should be thrown
$query1="insert into table1(id,name) values('1','Dan')";
$query2="insert into table2(id,name) values('2','Anaba')";
// If we arrive here, it means that no exception was thrown
// i.e. no query has failed, and we can commit the transaction
$db->commit();
} catch (Exception $e) {
// An exception has been thrown
// We must rollback the transaction
$db->rollback();
}
Please I am trying to implement transaction in mysql queries through php, I would like to fail all queries when one of the queries fail. The above code throws an error("Fatal error: Call to undefined method mysqli::beginTransaction()").Please is there any better solution or idea to solve the problem.Thanks for your help
$db->begin_transaction();
not
$db->beginTransaction();
http://www.php.net/manual/en/mysqli.begin-transaction.php
If earlier than PHP 5.5, then use
$db->autocommit(true);
instead
According to the documentation, begin_transaction() is new in PHP 5.5. (just released a couple weeks ago)
If you're getting an error saying that it doesn't exist, it probably means you're using an older version of PHP.
If you think you are running PHP 5.5, please verify your PHP version by running this earlier in your same script:
echo(phpversion());
Sometimes there's multiple versions of PHP present on a machine, and your script may not actually be executing in the version that you think it is.
You could implement a $count, something like that:
$err_count = 0;
$query1 = "insert into table1(id,name) values('1','Dan')";
if(!$query1)
$err_count++;
if($err_count>0)
throw new Exception("error msg");
I think the question itself is pretty self-explanatory. The code is given below -
<?php
$PDO = NULL;
$pdo_dsn = 'mysql:host=localhost;dbname=pdo_test';
$pdo_persistence = array( PDO::ATTR_PERSISTENT => true );
$db_user = 'root';
$db_pass = '';
$db_query = "INSERT INTO person(name, address)
VALUES ('Mamsi Mamsi', 'Katabon')";
try
{
$PDO = new PDO($pdo_dsn, $db_user, $db_pass,
$pdo_persistence);
}
catch(PDOException $e)
{
echo "Error occured: ". $e->getMessage();
die();
}
$PDO->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
$PDO->setAttribute(PDO::ATTR_AUTOCOMMIT, false);
try
{
$PDO->beginTransaction();
$PDO->exec($db_query);
throw new PDOException('Generated Exception');
$PDO->commit();
}
catch(PDOException $e)
{
echo "An error occured while doing a database transaction. The
error message is : ".$e->getMessage();
$PDO->rollBack();
die();
}
?>
Even if I am rolling back the transaction inside the catch block, data are still being inserted into the database. Why?
EDIT
I am adding the following few lines from the documentation for further clarification -
Unfortunately, not every database supports transactions, so PDO needs
to run in what is known as "auto-commit" mode when you first open the
connection. Auto-commit mode means that every query that you run has
its own implicit transaction, if the database supports it, or no
transaction if the database doesn't support transactions. If you need
a transaction, you must use the PDO::beginTransaction() method to
initiate one. If the underlying driver does not support transactions,
a PDOException will be thrown (regardless of your error handling
settings: this is always a serious error condition). Once you are in
a transaction, you may use PDO::commit() or PDO::rollBack() to finish
it, depending on the success of the code you run during the
transaction.
Also, the following lines from this page -
bool PDO::beginTransaction ( void )
Turns off autocommit mode. While autocommit mode is turned off,
changes made to the database via the PDO object instance are not
committed until you end the transaction by calling PDO::commit().
Calling PDO::rollBack() will roll back all changes to the database
and return the connection to autocommit mode.
Some databases, including MySQL, automatically issue an implicit
COMMIT when a database definition language (DDL) statement such as
DROP TABLE or CREATE TABLE is issued within a transaction. The
implicit COMMIT will prevent you from rolling back any other changes
within the transaction boundary.
You should check that you are using INNODB as your database type. MyISAM does not support transactions.