I have a script that does a lot of legwork nightly.
It uses a PDO prepared statement that executes in a loop.
The first few are running fine, but then I get to a point where they all fail with the error:
"MySQL server has gone away".
We run MySQL 5.0.77.
PHP Version 5.2.12
The rest of the site runs fine.
Most likely you sent a packet to the server that is longer than the maximum allowed packet.
When you try to insert a BLOB that exceeds your server's maximum packet size, even on a local server you will see the following error message on clientside:
MySQL server has gone away
And the following error message in the server log: (if error logging is enabled)
Error 1153 Got a packet bigger than 'max_allowed_packet' bytes
To fix this, you need to decide what is the size of the largest BLOB that you will ever insert, and set max_allowed_packet in my.ini accordingly, for example:
[mysqld]
...
max_allowed_packet = 200M
...
The B.5.2.9. MySQL server has gone away section of the MySQL manual has a list of possible causes for this error.
Maybe you are in one of those situations ? -- Especially considering you are running a long operation, the point about wait_timeout might be interesting...
I had the same problem where the hosting server administration kills connection if there is a timeout.
Since I have used the query in major part I wrote a code which instead of using PDO class we can include the below class and replace the classname to "ConnectionManagerPDO". I just wrapped the PDO class.
final class ConnectionManagerPDO
{
private $dsn;
private $username;
private $passwd;
private $options;
private $db;
private $shouldReconnect;
const RETRY_ATTEMPTS = 3;
public function __construct($dsn, $username, $passwd, $options = array())
{
$this->dsn = $dsn;
$this->username = $username;
$this->passwd = $passwd;
$this->options = $options;
$this->shouldReconnect = true;
try {
$this->connect();
} catch (PDOException $e) {
throw $e;
}
}
/**
* #param $method
* #param $args
* #return mixed
* #throws Exception
* #throws PDOException
*/
public function __call($method, $args)
{
$has_gone_away = false;
$retry_attempt = 0;
try_again:
try {
if (is_callable(array($this->db, $method))) {
return call_user_func_array(array($this->db, $method), $args);
} else {
trigger_error("Call to undefined method '{$method}'");
/*
* or
*
* throw new Exception("Call to undefined method.");
*
*/
}
} catch (\PDOException $e) {
$exception_message = $e->getMessage();
if (
($this->shouldReconnect)
&& strpos($exception_message, 'server has gone away') !== false
&& $retry_attempt <= self::RETRY_ATTEMPTS
) {
$has_gone_away = true;
} else {
/*
* What are you going to do with it... Throw it back.. FIRE IN THE HOLE
*/
throw $e;
}
}
if ($has_gone_away) {
$retry_attempt++;
$this->reconnect();
goto try_again;
}
}
/**
* Connects to DB
*/
private function connect()
{
$this->db = new PDO($this->dsn, $this->username, $this->passwd, $this->options);
/*
* I am manually setting to catch error as exception so that the connection lost can be handled.
*/
$this->db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
/**
* Reconnects to DB
*/
private function reconnect()
{
$this->db = null;
$this->connect();
}
}
Then use can start using the above class as you do in PDO.
try {
$db = new ConnectionManagerPDO("mysql:host=localhost;dbname=dummy_test", "root", "");
$query = $db->query("select * from test");
$query->setFetchMode(PDO::FETCH_ASSOC);
}
catch(PDOException $e){
/*
handle the exception throw in ConnectionManagerPDO
*/
}
Try using PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true) on your pod instance(s). Dont know that it will help but with no log data its all i got.
It's likely that either your connection has been killed (e.g. by wait_timeout or another thread issuing a KILL command), the server has crashed or you've violated the mysql protocol in some way.
The latter is likely to be a bug in PDO, which is extremely likely if you're using server-side prepared statements or multi-results (hint: Don't)
A server crash will need to be investigated; look at the server logs.
If you still don't know what's going on, use a network packet dumper (e.g. tcpdump) to dump out the contents of the connection.
You can also enable the general query log - but do it very carefully in production.
Nathan H, below is php class for pdo reconnection + code usage sample.
Screenshot is attached.
<?php
# set errors reporting level
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
# set pdo connection
include('db.connection.pdo.php');
/* # this is "db.connection.pdo.php" content
define('DB_HOST', 'localhost');
define('DB_NAME', '');
define('DB_USER', '');
define('DB_PWD', '');
define('DB_PREFIX', '');
define('DB_SHOW_ERRORS', 1);
# connect to db
try {
$dbh = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PWD);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e) {
# echo $e->getMessage()."<br />";
# exit;
exit("Site is temporary unavailable."); #
}
*/
$reconnection = new PDOReconnection($dbh);
$reconnection->getTimeout();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 10 seconds..'.PHP_EOL;
sleep(10);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 35 seconds..'.PHP_EOL;
sleep(35);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 55 seconds..'.PHP_EOL;
sleep(55);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 300 seconds..'.PHP_EOL;
sleep(300);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
# *************************************************************************************************
# Class for PDO reconnection
class PDOReconnection
{
private $dbh;
# constructor
public function __construct($dbh)
{
$this->dbh = $dbh;
}
# *************************************************************************************************
# get mysql variable "wait_timeout" value
public function getTimeout()
{
$timeout = $this->dbh->query('show variables like "wait_timeout"')->fetch(); # print_r($timeout);
echo '========================'.PHP_EOL.'mysql variable "wait_timeout": '.$timeout['Value'].' seconds.'.PHP_EOL.'========================'.PHP_EOL;
}
# *************************************************************************************************
# check mysql connection
public function checkConnection()
{
try {
$this->dbh->query('select 1')->fetchColumn();
echo 'old connection works..'.PHP_EOL.'========================'.PHP_EOL;
} catch (PDOException $Exception) {
# echo 'there is no connection.'.PHP_EOL;
$this->dbh = $this->reconnect();
echo 'connection was lost, reconnect..'.PHP_EOL.'========================'.PHP_EOL;
}
return $this->dbh;
}
# *************************************************************************************************
# reconnect to mysql
public function reconnect()
{
$dbh = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PWD);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbh;
}
}
# /Class for PDO reconnection
# *************************************************************************************************
I got this same error this morning after changing my DB properties in Laravel. I'd commented out the old settings and pasted in new ones. The problem was that the new settings where missing the DB_CONNECTION variable:
DB_CONNECTION=pgsql
Obviously you need to add whatever connection type you are using: sqlite, mysql, ...
I had the exact same problem.
I resolved this issue by doing unset on the PDO object instead of seting it to NULL.
For example:
function connectdb($dsn,$username,$password,$driver_options) {
try {
$dbh = new PDO($dsn,$username,$password,$driver_options);
return $dbh;
}
catch(PDOException $e)
{
print "DB Error: ".$e->getMessage()."<br />";
die();
}
}
function closedb(&$dbh) {
unset($dbh); // use this line instead of $dbh = NULL;
}
Also, it is highly recommended to unset all your PDO objects. That includes variables that contain prepared statements.
$pdo = new PDO(
$dsn,
$config['username'],
$config['password'],
array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
)
);
try this. It may work
Related
Im getting the follow error from my elastic beanstalk application:
Fatal error: Uncaught Error: Class 'App\PDO' not found in
/var/app/current/crawler/app/SQLConnection.php:32 Stack trace: #0
/var/app/current/crawler/init.php(18): App\SQLConnection->connect() #1
{main} thrown in /var/app/current/crawler/app/SQLConnection.php on
line 32
The app runs perfectly locally (as is usually the case with errors).
All libraries and dependencies are included in the app I uploaded.
Can anyone see where I went wrong please?
The initialzation script to create DB tables:
<?php
require 'vendor/autoload.php';
require (__DIR__.'/app/SQLConnection.php');
require (__DIR__.'/app/SQLCreateTable.php');
require (__DIR__.'/app/SQLInsert.php');
require (__DIR__.'/app/SQLRetrieve.php');
require (__DIR__.'/app/SQLDelete.php');
use App\SQLConnection;
use App\SQLCreateTable;
use App\SQLInsert;
use App\SQLRetrieve;
use App\SQLDelete;
try {
//connect to DB
$sql = new SQLCreateTable((new SQLConnection())->connect());
//get list of tables
$tables = $sql->getTableList();
//check if required tables excist
if(in_array('results',$tables) && in_array('logs',$tables)){
echo "Tables already created. You will be redirected to the home page";
header( "refresh:5;url=/index.php" );
}else {
//create tables
try {
$sql->createTables();
echo "Tables succesfully created! You will be redirected to the home page";
header( "refresh:5;url=/index.php" );
} catch (\PDOException $e) {
echo "Failed to create tables: " . $e->getMessage();
}
}
} catch (\PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
Then the SQL connection script:
<?php
namespace App;
/**
* SQL connnection
*/
class SQLConnection {
/**
* PDO instance
* #var type
*/
private $pdo;
/**
* return in instance of the PDO object that connects to the SQLite database
* #return \PDO
*/
public function connect() {
$dbhost = $_SERVER['RDS_HOSTNAME'];
$dbport = $_SERVER['RDS_PORT'];
$dbname = $_SERVER['RDS_DB_NAME'];
$charset = 'utf8' ;
$dsn = "mysql:host={$dbhost};port={$dbport};dbname={$dbname};charset={$charset}";
$username = $_SERVER['RDS_USERNAME'];
$password = $_SERVER['RDS_PASSWORD'];
if ($this->pdo == null) {
try {
// $this->pdo = new \PDO("mysql:host=localhost;dbname=crawler", "root", "");
$this->pdo = new PDO($dsn, $username, $password);
// set the PDO error mode to exception
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
} catch (\PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
}
return $this->pdo;
}
}
The 'Init' script is at root level and the SQLconnection script is in a folder called "app".
Thanks guys!
Found the solution!
$this->pdo = new PDO($dsn, $username, $password);
Needs to change
$this->pdo = new \PDO($dsn, $username, $password);
If anyone knows exactly why this is the case, please share
Why do I get a Apache seg fault when I use the $factory and how can I fix it?
I'm running the code from Linux web servers (Ubuntu and CentOS) using PHP7 with unixODBC, FreeTDS, and DBLib as the driver. I've been using the DB class below and it has been working fine...
<?php
class DB {
private static $factory;
private $database;
public static function getFactory() {
// if (!self::$factory) {
if (!static::$factory) {
static::$factory = new DB();
}
return static::$factory;
}
public function getConnection() {
if (!$this->database) {
try {
$options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
$this->database = new PDO('dblib:host=localhost;dbname=DB_NAME;port=3306;charset=utf8', 'DB_USER', 'DB_PASS', $options);
} catch (PDOException $e) {
echo 'Error code: ' . $e->getCode();
exit;
}
}
return $this->database;
}
}// end of class
?>
The following query runs without issue...
<?php
$db = DB::getFactory()->getConnection();
$sql = "INSERT INTO ".Config::get('TBL_PREFIX')."resourceTypes (name) VALUES (:name)";
$query = $db->prepare($sql);
$query->execute(array(':name' => $name));
die('query complete.');
?>
However, when I decided to use PDO's beginTransaction(), the query below fails and Apache throws a seg fault ([core:notice] [pid 1219] AH00051: child pid 31779 exit signal Segmentation fault (11), possible coredump in /etc/apache2). Below is the code with the beginTransaction()...
<?php
$db = DB::getFactory()->getConnection();
$db->beginTransaction();// Start our transaction.
try {
$sql = "INSERT INTO ".Config::get('TBL_PREFIX')."sched_resourceTypes (name) VALUES (:name)";
$query = $db->prepare($sql);
$query->execute(array(':name' => $name));
$db->commit();// No exception, so commit the changes.
die('committed...');
} catch(Exception $e){// Handle any exceptions that are thrown.
echo $e->getMessage();// Print out the error message.
$db->rollBack();// Rollback the transaction.
die('rolling back...');
}
?>
If I modify my DB class to look like the following code, the above query will work without the a seg fault.
<?php
class DB {
private static $database;
public static function getConnection() {
if (!Self::$database) {
try {
$options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
Self::$database = new PDO('dblib:host=localhost;dbname=DB_NAME;port=3306;charset=utf8', 'DB_USER', 'DB_PASS', $options);
} catch (PDOException $e) {
echo 'Error code: ' . $e->getCode();
exit;
}
}
return Self::$database;
}
}// End of class
?>
What is the issue?
Why does the factory create a seg fault in Apache?
And most importantly, how can I correct this in the proper way?
I am trying to define my own function in one of the php login libraries.
There some constants are defined which works perfectly fine until I call the databaseConnection() function on my own.
private function databaseConnection()
{
// if connection already exists
if ($this->db_connection != null) {
return true;
} else {
try {
// Generate a database connection, using the PDO connector
// #see http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
// Also important: We include the charset, as leaving it out seems to be a security issue:
// #see http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers#Connecting_to_MySQL says:
// "Adding the charset to the DSN is very important for security reasons,
// most examples you'll see around leave it out. MAKE SURE TO INCLUDE THE CHARSET!"
$this->db_connection = new PDO('mysql:host='. DB_HOST .';dbname='. DB_NAME . ';charset=utf8', DB_USER, DB_PASS);
return true;
} catch (PDOException $e) {
$this->errors[] = MESSAGE_DATABASE_ERROR . $e->getMessage();
}
}
// default return
return false;
}
this is the function I defined..
private function updateLastLoginDate($user_name)
{
if($this->databaseConnection())
{
$sth = $this->db_connection->prepare('UPDATE users '
. 'SET last_login_date = UTC_TIMESTAMP()'
. 'WHERE user_email =:user_name OR user_name =:user_name');
$sth->execute(array(':user_name' => $user_name));
}
}
when I call this function the error says that all the constants in the databaseConntion functions are not defined.. but they work perfectly fine other than the call I make to my defined function...
I am not good with pdo though..
So, I'm working on a project that requires a database connection. I've chosen to use PDO for its versatility and need to figure out how to set up the connection. Currently I'm going for something like this:
class Database {
private static $db;
static function initDB() {
if(!is_object(self::$db) || get_class(self::$db) != 'PDO') {
include('core/db.php');
try {
$db = new PDO($database, $username, $password);
} catch(PDOException $e) {
print("<br />Could not establish database connection. Error message: ".$e->getMessage()."<br />");
die();
}
}
//Try the transaction
/*
if($transaction = $db::query(PDO::quote($value)))
$db::query(PDO::quote("INSERT INTO log VALUES ('".Authorization::$user."','".PDO::quote($value)."', 'Success')"));
else
$db::query(PDO::quote("INSERT INTO log VALUES ('".Authorization::$user."','".PDO::quote($value)."', 'Failure')"));*/
}
}
So, this pretty much reveals one of the concepts I don't really know: singletons and static classes/objects. Any way to set up a database connection using OO best practices that initializes with the script via some kind of __construct method?
A database connection should not be either static or a singleton. This merely introduces another form of global state, which is bad for unit-testing and does hide obvious dependencies.
The right way here would be is to inject an instance of PDO into the classes that need it. You adhere the Single-Responsibility Principle and Dependency Injection.
Note, you should never log errors and do include() inside PDOAdapter constructor because its masked violation of the Single-Responsibility Principle
So, this would look like this:
final class PDO_Provider extends PDO
{
/**
* Constructor. Inits PDO
*
* #param array $params
* #return void
*/
public function __construct(array $params)
{
try {
extract($params);
parent::__construct(sprintf('mysql: host=%s; dbname=%s', $host, $database), $user, $password);
$this->setAttribute(parent::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES UTF8');
$this->setAttribute(parent::ATTR_ERRMODE, parent::ERRMODE_EXCEPTION);
$this->setAttribute(parent::ATTR_EMULATE_PREPARES, false);
$this->setAttribute(parent::ATTR_DEFAULT_FETCH_MODE, parent::FETCH_ASSOC);
} catch(PDOException $e) {
die($e->getMessage());
}
}
}
And you would use it like this,
<?php
$sql_config = array(
'host' => 'localhost',
'user' => 'root',
'password' => '',
'database' => '_DB_NAME_',
);
// <- Or you can include that, like
$sql_config = include(__DIR__ . '/core/db_params.php');
$pdoProvider = new PDO_Provider($sql_config);
$user = new User_Login($pdoProvider); // the point here is to inject an instance of $pdoProvider. User_Login is actually irrelevant
if you want to use a normal object instead of sigleton, try something like this:
class PDOConnector{
protected $connection;
function __construct($host, $user, $pass, $db_name)
{
//create database connection
try{
$this->connection = new PDO('mysql:host='.$this->host.';dbname='.$this->db_name.';charset=utf8', $this->user, $this->pass,array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
}
catch(PDOException $ex) {
echo "An Error occured : ".$ex->getMessage();
}
}
}
Here is Database.php:
<?php
/*Data Base Class
* MySQL - InnoDB
* PHP - PDO (PHP Data Object -So we could change databases if needed)
*/
class Database extends PDO{
private $DBH;
function __construct($host, $dbname, $user, $pass){
try {
$this->DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
} catch (PDOException $e) {
return $e->getMessage();
}
}
public function alteration_query($sql){
/* Begin a transaction, turning off autocommit */
$this->DBH->beginTransaction();
try{
$count = $this->DBH->exec($sql);
$this->DBH->commit();
return $count;
}catch (PDOException $e) {
$this->DBH->rollback();
return $e;
}
}
}
?>
Here is test.php:
<?php
require('Database.php');
$dbo = new Database('***.***.com','***','***','***');
echo $dbo->alteration_query('DELETE * from T_Table');
?>
For some reason, it won't give me an error or delete the contents of the T_table.
EDIT: The problem in your case is that the argument is called $sql, but you're using $query to execute it (in the alteration_query method). Next time, please enable error reporting, and/or use a decent IDE, that can show you these errors. Like so:
EDIT2: Set PDO's error mode to exception, this way any error would throw an exception. See updated code.
Don't catch your exceptions inside of the functions, do it outside:
<?php
/*Data Base Class
* MySQL - InnoDB
* PHP - PDO (PHP Data Object -So we could change databases if needed)
*/
class Database
{
private $DBH;
function __construct($host, $dbname, $user, $pass)
{
$this->DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
//Set PDO to throw exceptions on errors!
$this->DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function alteration_query($sql)
{
/* Begin a transaction, turning off autocommit */
$this->DBH->beginTransaction();
$count = $this->DBH->exec($query);
$this->DBH->commit();
return $count;
}
}
try {
$pdo = new Database("localhost", "dbname", "user", "pass");
$pdo->alteration_query("SELECT * FROM wrong_table");
}
catch (PDOException $e) {
die("An error has occured! " . $e->getMessage());
}
This way, you can catch the error exactly where you need it, and not force it inside of the function (which kinda beats the point of the exception).
Also, from the manual:
When the script ends or when a connection is about to be closed, if
you have an outstanding transaction, PDO will automatically roll it
back. This is a safety measure to help avoid inconsistency in the
cases where the script terminates unexpectedly--if you didn't
explicitly commit the transaction, then it is assumed that something
went awry, so the rollback is performed for the safety of your data.
Exceptions halt the execution of the function, meaning the commit() never happens, and it rolls back.
Try this line :
$this->DBH = new parent::__construct("mysql:host=$host;dbname=$dbname", $user, $pass);
Reason : __construct overwrite the main class function. I think that's why, really not sure.
If it's not working, try parrent::PDO. I am pretty sure it an overwrite problem.