PHP OCI: how to roll back ANY open connections - php

I'm using PHP's OCI library to communicate with Oracle. In every function there is a try...catch to catch any exceptions. In the catch part I call my own error handling function.
When an exception occurs I want to rollback ALL open connections to oracle, preferably in my error handling function. Is this possible?
Thanks
#
#
It's a bit hacky, but here's how I solved the problem.
In my connect() function which connects to Oracle, I put this code:
if(!isset($GLOBALS['OPEN_CONNECTIONS'])){
$GLOBALS['OPEN_CONNECTIONS'] = array();
$GLOBALS['OPEN_CONNECTIONS'][] = $this;
}else{
$GLOBALS['OPEN_CONNECTIONS'][] = $this;
}
This stores a list of all open connections in the global scope.
In my error handling function, I do this:
//rollback all open connections
if(isset($GLOBALS['OPEN_CONNECTIONS'])){
foreach($GLOBALS['OPEN_CONNECTIONS'] as $con){
$con->rollback();
}
}
This way, I can rollback all open connections without having to pass them into my error handler.

Related

DB2 connection with PDO throws General Error -7008 when Inserting or Deleting

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.

die() or try/catch when interacting with MySql database in PHP?

A lot of tutorials and books I have been over and read have used the die() method to catch an exception when interacting with a local MySQL database
For example:
mysql_connect($dbhost, $dbuser, $dbpass)or die(mysql_error());
Would a try/catch block be more beneficial over the die() method or is that just the standard way that exception handling works with db connections?
or die() is an extremely primitive way to "handle" errors and only for examples or debugging at best. In practice, it depends on how you handle your errors. You may want to return false from a function call or you may want to throw your own exception instead; e.g.:
if (!$con = mysql_connect(..)) {
throw new DatabaseConnectionError(mysql_error());
}
try..catch will do exactly nothing with mysql, since mysql never throws any exceptions. It only ever returns false on failure.
You will have to have your own error handling strategy. You'll probably want to log errors and display a user friendly error page instead of cryptic error messages. mysql is not concerned with that part. It only gives you a way to check whether an operation was successful or not (check if it returns false); what you do with this information is up to you. die kills the entire application and at least doesn't allow the problem to propagate further; but it certainly does not display any user friendly error pages.
Having said all this, mysql is old and deprecated. If you'd use something newer like PDO instead, it can properly throw exceptions itself.
The mysql_connect method does not throw exceptions and thus die() is used by many applications to terminate when there is no connection available.
You can use the solution mentioned here: how to use throw exception in mysql database connect
Included for completeness:
try
{
if ($db = mysqli_connect($hostname_db, $username_db, $password_db))
{
//do something
}
else
{
throw new Exception('Unable to connect');
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
Alternatively use the new and more OOP styled database access: http://php.net/manual/en/book.pdo.php
The reason a lot of applications uses die is from the fact that they are so reliant on the database that continuing without a connection is utterly fruitless.
Edit As mentioned in the comments, the code example above is for illustrational purposes. Catching right after throwing is pointless.

resetting CDbConnection in Yii

I am running a php gearman worker which establishes the connection to the database. However, the problem is that after around 8 hours, the mysql connection disconnects, and my worker crashes. So, I wanted to disconnect and make a new connection to the database again.
I am using CDbConnection to connect to the database in Yii, and was expecting "setActive(false)" to do the trick for me. Here below I am "explicitly disconnecting" and the making a db query....expecting my query to throw an exception, but I am surprised to see that "setActive" makes no impact at all and my query goes successfully.
//if it fails then reconnect to the database
Yii::app()->db->setActive(false);
try {
$model = MyModel::model()->findByPk(10);
var_dump($model);
} catch (exception $e) {
echo "got exception -- ".$e->getMessage()."\n";
Yii::app()->db->setActive(false);
Yii::app()->db->setActive(true);
// I also tried Yii::app()->db->active = true/false
$model = MyModel::model()->findByPk(10);
var_dump($model);
}
How do I disconnect and reconnect to my database with CdbConnection?
Try looking in the trace log, I'm pretty sure it actually closes the connection, but then reopens it in CDbConnection->createCommand() when you execute a query.

What is the best way to handle "Warning: mysql_connect(): Too many connections" with Kohana 3?

I've got a web application that is used by a company for logging their employees' work.
A lot of people are often logged in at once.
The application runs on a shared host.
I sometimes receive...
Warning: mysql_connect() [function.mysql-connect]: Too many connections
Which then lets further errors cascade... like errors with mysql_select_db(), mysql_error(), mysql_errnon() and finally the uncaught Database_Eexception.
When I run my main request, I wrap it in a try and capture any exception and display a not found page. This is because usually my controllers throw exceptions if a resource is not found (though the route may be valid) e.g. http://example.com/products/30 is a valid route, but product #30 doesn't exist.
What is the best way to handle the too many connections? Ideally I'd like to capture that exception separately, then display a nice page that informs the employee to try again in 5 minutes.
The code that runs my main request in application/bootstrap.php looks like this...
$request = Request::instance();
try {
$request->execute();
} catch (Exception $e) {
if (Kohana::$environment === Kohana::DEVELOPMENT) throw $e;
// Log the error
Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
// Create a 404 response
$request->status = 404;
$request->response = Request::factory(Route::get('catch_all')->uri(array('path' => 'errors/404')))->execute();
}
$request->send_headers();
echo $request->response;
Thanks for any help!
I just created such file to handle all the errors:
<?php
class Kohana extends Kohana_Core
{
/**
* Redirect to custom exception_handler
*/
public static function exception_handler(Exception $e)
{
if (Kohana::DEVELOPMENT === Kohana::$environment)
{
// Pass to Kohana if we're in the development environment
parent::exception_handler($e);
}
else
{
Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
// Default route
$route = Route::url('default', array('controller' => 'error', 'action' => '404'));
// Error sub-request.
echo Request::factory($route)
->execute()
->send_headers()
->response;
}
}
}
For now it is just a sketch, but it could give you some ideas.
ps: my bootstrap is not modified
In /etc/my.cnf under the [mysqld] section add:
max_connections = 500
Then either executeSET GLOBAL max_connections = 500; in MySQL or restart MySQL.
You might want to begin by checking to see what your MySQL system property max_connections is currently set to, and see how that compares usage requirements. If you don't have a good handle on usage requirements then you could do worse than instrument your code to log data about simultaneous connections; or your a live database profiling tool to monitor this.
You could also look to see if your code is hogging connections for too long (ie. unnecessarily) and correct this. Perhaps investigate connection pooling.

Catch database exception in Kohana

I'm using Kohana 2. I would like to catch a database exception to prevent an error page when no connection to the server can be established.
The error displayed is
system/libraries/drivers/Database/Mysql.php [61]:
mysql_connect() [function.mysql-connect]: Lost connection to MySQL server at
'reading initial communication packet', system error: 110
The database server is not reachable at all at this point.
I'm doing this from a model. I tried both
public function __construct()
{
// load database library into $this->db
try
{
parent::__construct();
}
catch (Exception $e)
{
die('Database error occured');
}
}
as well as
try
{
$hoststatus = $this->db->query('SELECT x FROM y WHERE z;');
}
catch (Exception $e)
{
die('Database error occured');
}
...but none of them seemed to work. It seems as if no exception gets passed on from the main model. Is there another way to catch the database error and use my own error handling?
Kohana 2 does not convert errors into exceptions. You will either need to attach your own error handler, or use error_reporting() to turn off the error (temporarily) then do some kind of handling yourself.
You can catch the exception, but you are probably trying to catch it in the wrong place. The problem with trying to catch that low-level of an exception is that it can be spawned from many different sources.
For example, if you use the database driver for your sessions that exception will be thrown from instantiation of the database driver in the session library (which is instantiated in a call to session which will probably happen before you instantiate any models).
Catching that exception can happen from the model, but it is more likely to happen from another source - in which case you would probably have to extend a few libraries, or be sure you are wrapping a base model parent::__construct call and the session library in a try-catch block.
(I would personally extend the Model library to do that instead of putting it in a base model)
Hope that helps.
I don't know Kohana, but a try .. catch block will not catch normal errors, only Exceptions. Are you sure Kohana throws Exceptions where you expect to receive Exceptions?
Edit based on you comments:
Well, first of all, in a production environment (meaning your live application) you should always disable the displaying of PHP errors to the screen. Displaying of these errors to the screen should only be done in a development environment to inform you, the developer. Visitors of your live application however have no business in knowing/reading PHP errors, as it might disclose sensitive information about your environment. You should however log the errors to a log file.
Furthermore, I just took a quick peek at Kohana, and indeed see that here and there Exceptions are thrown, but it doesn't seem to do this in a consistent manner.
If you want php errors to be treated as Exceptions have a look example #1 in this documentation.

Categories