mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-11 12:03:14 +00:00
Fix #1705 : MySQL connection might be lost after long operations
This commit is contained in:
@@ -163,6 +163,7 @@ class Manage extends Helper
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute(array(':email' => $email));
|
||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
$count = count($row);
|
||||
|
||||
if (!is_array($row) || $count == 0) {
|
||||
|
@@ -1130,6 +1130,7 @@ class ACL implements cache_cacheableInterface
|
||||
throw new Exception('Error while deleteing some rights');
|
||||
}
|
||||
}
|
||||
$stmt_del->closeCursor();
|
||||
$this->delete_data_from_cache(self::CACHE_RIGHTS_BAS);
|
||||
|
||||
return $this;
|
||||
@@ -1188,6 +1189,7 @@ class ACL implements cache_cacheableInterface
|
||||
if (!$this->has_access_to_sbas($sbas_id))
|
||||
$stmt_ins->execute(array(':sbas_id' => $sbas_id, ':usr_id' => $usr_id));
|
||||
}
|
||||
$stmt_ins->closeCursor();
|
||||
$this->delete_data_from_cache(self::CACHE_RIGHTS_SBAS);
|
||||
|
||||
return $this;
|
||||
@@ -1305,6 +1307,7 @@ class ACL implements cache_cacheableInterface
|
||||
if (!$stmt_up->execute($params)) {
|
||||
throw new Exception('Error while updating some rights');
|
||||
}
|
||||
$stmt_up->closeCursor();
|
||||
$this->delete_data_from_cache(self::CACHE_RIGHTS_SBAS);
|
||||
|
||||
return $this;
|
||||
|
@@ -1420,6 +1420,7 @@ class User_Adapter implements User_Interface, cache_cacheableInterface
|
||||
':prop' => $prop,
|
||||
':value' => $value
|
||||
));
|
||||
$stmt->closeCursor();
|
||||
$this->delete_data_from_cache();
|
||||
} catch (\Exception $e) {
|
||||
|
||||
|
@@ -81,6 +81,7 @@ class collection implements cache_cacheableInterface
|
||||
$stmt = $connbas->prepare($sql);
|
||||
$stmt->execute(array(':coll_id' => $this->coll_id));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
|
||||
if ( ! $row)
|
||||
throw new Exception('Unknown collection ' . $this->coll_id . ' on ' . $this->databox->get_dbname());
|
||||
|
@@ -167,6 +167,7 @@ class connection
|
||||
public static function close_PDO_connection($name)
|
||||
{
|
||||
if (isset(self::$_PDO_instance[$name])) {
|
||||
self::$_PDO_instance[$name]->disconnect();
|
||||
self::$_PDO_instance[$name] = null;
|
||||
unset(self::$_PDO_instance[$name]);
|
||||
}
|
||||
|
@@ -15,11 +15,12 @@
|
||||
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
|
||||
* @link www.phraseanet.com
|
||||
*/
|
||||
abstract class connection_abstract extends PDO
|
||||
abstract class connection_abstract
|
||||
{
|
||||
protected $name;
|
||||
protected $credentials = array();
|
||||
protected $multi_db = true;
|
||||
protected $connection;
|
||||
|
||||
public function get_credentials()
|
||||
{
|
||||
@@ -42,8 +43,12 @@ abstract class connection_abstract extends PDO
|
||||
|
||||
public function ping()
|
||||
{
|
||||
if (null === $this->connection) {
|
||||
$this->initConn();
|
||||
}
|
||||
|
||||
try {
|
||||
$this->query('SELECT 1');
|
||||
$this->connection->query('SELECT 1');
|
||||
} catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
@@ -51,41 +56,16 @@ abstract class connection_abstract extends PDO
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $statement
|
||||
* @param array $driver_options
|
||||
* @return PDOStatement
|
||||
*/
|
||||
public function prepare($statement, $driver_options = array())
|
||||
{
|
||||
return parent::prepare($statement, $driver_options);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
return parent::beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
return parent::commit();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function server_info()
|
||||
{
|
||||
return parent::getAttribute(constant("PDO::ATTR_SERVER_VERSION"));
|
||||
if (null === $this->connection) {
|
||||
$this->initConn();
|
||||
}
|
||||
|
||||
return $this->connection->getAttribute(constant("PDO::ATTR_SERVER_VERSION"));
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
interface connection_interface
|
||||
{
|
||||
|
||||
public function ping();
|
||||
|
||||
public function get_name();
|
||||
@@ -28,11 +27,5 @@ interface connection_interface
|
||||
|
||||
public function close();
|
||||
|
||||
public function prepare($statement, $driver_options = array());
|
||||
|
||||
public function beginTransaction();
|
||||
|
||||
public function commit();
|
||||
|
||||
public function server_info();
|
||||
}
|
||||
|
@@ -36,42 +36,45 @@ class connection_pdo extends connection_abstract implements connection_interface
|
||||
{
|
||||
$this->debug = $debug;
|
||||
$this->name = $name;
|
||||
if ($dbname)
|
||||
$dsn = 'mysql:dbname=' . $dbname . ';host=' . $hostname . ';port=' . $port . ';';
|
||||
else
|
||||
$dsn = 'mysql:host=' . $hostname . ';port=' . $port . ';';
|
||||
|
||||
$this->credentials['hostname'] = $hostname;
|
||||
$this->credentials['port'] = $port;
|
||||
$this->credentials['user'] = $user;
|
||||
$this->credentials['password'] = $passwd;
|
||||
|
||||
if ($dbname)
|
||||
$this->credentials['dbname'] = $dbname;
|
||||
|
||||
parent::__construct($dsn, $user, $passwd, $options);
|
||||
|
||||
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->query("
|
||||
SET character_set_results = 'utf8', character_set_client = 'utf8',
|
||||
character_set_connection = 'utf8', character_set_database = 'utf8',
|
||||
character_set_server = 'utf8'");
|
||||
$this->initConn();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function initConn()
|
||||
{
|
||||
$this->connection = null;
|
||||
|
||||
if (isset($this->credentials['dbname']))
|
||||
$dsn = 'mysql:dbname=' . $this->credentials['dbname'] . ';host=' . $this->credentials['hostname'] . ';port=' . $this->credentials['port'] . ';';
|
||||
else
|
||||
$dsn = 'mysql:host=' . $this->credentials['hostname'] . ';port=' . $this->credentials['port'] . ';';
|
||||
|
||||
$this->connection = new \PDO($dsn, $this->credentials['user'], $this->credentials['password'], array());
|
||||
|
||||
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->connection->exec("
|
||||
SET character_set_results = 'utf8', character_set_client = 'utf8',
|
||||
character_set_connection = 'utf8', character_set_database = 'utf8',
|
||||
character_set_server = 'utf8'");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type $statement
|
||||
* @param type $driver_options
|
||||
* @return PDOStatement
|
||||
* @return void
|
||||
*/
|
||||
public function prepare($statement, $driver_options = array())
|
||||
public function disconnect()
|
||||
{
|
||||
if ($this->debug) {
|
||||
return new connection_pdoStatementDebugger(parent::prepare($statement, $driver_options));
|
||||
} else {
|
||||
return parent::prepare($statement, $driver_options);
|
||||
}
|
||||
$this->connection = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,6 +86,48 @@ class connection_pdo extends connection_abstract implements connection_interface
|
||||
connection::close_PDO_connection($this->name);
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if (null === $this->connection) {
|
||||
$this->initConn();
|
||||
}
|
||||
|
||||
if (!method_exists($this->connection, $method)) {
|
||||
throw new \BadMethodCallException(sprintf('Method %s does not exist', $method));
|
||||
}
|
||||
|
||||
$tries = 0;
|
||||
|
||||
do {
|
||||
$tries++;
|
||||
try {
|
||||
set_error_handler(function ($errno, $errstr) {
|
||||
if (false !== strpos($errstr, 'Error while sending QUERY packet')) {
|
||||
throw new \Exception('MySQL server has gone away');
|
||||
}
|
||||
throw new \Exception($errstr);
|
||||
});
|
||||
|
||||
if ('prepare' === $method && $this->debug) {
|
||||
$ret = new connection_pdoStatementDebugger(call_user_func_array(array($this->connection, $method), $args));
|
||||
} else {
|
||||
$ret = call_user_func_array(array($this->connection, $method), $args);
|
||||
}
|
||||
restore_error_handler();
|
||||
|
||||
return $ret;
|
||||
} catch (\Exception $e) {
|
||||
restore_error_handler();
|
||||
|
||||
$found = (false !== strpos($e->getMessage(), 'MySQL server has gone away')) || (false !== strpos($e->getMessage(), 'errno=32 Broken pipe'));
|
||||
if ($tries >= 2 || !$found) {
|
||||
throw $e;
|
||||
}
|
||||
$this->initConn();
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $message
|
||||
|
@@ -1009,6 +1009,7 @@ class databox extends base
|
||||
unset($e);
|
||||
}
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
$user->ACL()->give_access_to_base($base_ids);
|
||||
foreach ($base_ids as $base_id) {
|
||||
@@ -1022,8 +1023,6 @@ class databox extends base
|
||||
);
|
||||
}
|
||||
|
||||
$stmt->closeCursor();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@@ -413,6 +413,7 @@ class databox_field implements cache_cacheableInterface
|
||||
|
||||
$stmt = $connbas->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$stmt->closeCursor();
|
||||
|
||||
if ($this->renamed) {
|
||||
caption_field::rename_all_metadatas($this);
|
||||
|
@@ -65,6 +65,7 @@ class patch_370alpha7a implements patchInterface
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute();
|
||||
$rs = $stmt->fetchAll();
|
||||
$stmt->closeCursor();
|
||||
} catch (\PDOException $e) {
|
||||
// table not found
|
||||
if ($e->getCode() == '42S02') {
|
||||
|
@@ -72,8 +72,8 @@ class patch_370alpha8a implements patchInterface
|
||||
if (($stmt = $conn->prepare($sql)) !== FALSE) {
|
||||
$stmt->execute();
|
||||
$ttasks = $row = $stmt->fetchAll();
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
$tdom = array(); // key = period
|
||||
$taskstodel = array();
|
||||
|
@@ -74,7 +74,7 @@ class patchthesaurus_204
|
||||
|
||||
$sql2 = "UPDATE record SET status=((status | 15) & ~2)";
|
||||
|
||||
$stmt = $connbas->prepare($sql);
|
||||
$stmt = $connbas->prepare($sql2);
|
||||
$stmt->execute();
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
|
@@ -1055,6 +1055,7 @@ class record_adapter implements record_Interface, cache_cacheableInterface
|
||||
':record_id' => $this->record_id
|
||||
)
|
||||
);
|
||||
$stmt->closeCursor();
|
||||
|
||||
$this->reindex();
|
||||
|
||||
@@ -1181,6 +1182,7 @@ class record_adapter implements record_Interface, cache_cacheableInterface
|
||||
$sql = 'UPDATE record SET jeton=(jeton | ' . JETON_MAKE_SUBDEF . ') WHERE record_id = :record_id';
|
||||
$stmt = $connbas->prepare($sql);
|
||||
$stmt->execute(array(':record_id' => $this->get_record_id()));
|
||||
$stmt->closeCursor();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -1197,6 +1199,7 @@ class record_adapter implements record_Interface, cache_cacheableInterface
|
||||
WHERE record_id= :record_id';
|
||||
$stmt = $connbas->prepare($sql);
|
||||
$stmt->execute(array(':record_id' => $this->record_id));
|
||||
$stmt->closeCursor();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -1264,6 +1267,7 @@ class record_adapter implements record_Interface, cache_cacheableInterface
|
||||
':originalname' => null,
|
||||
':mime' => null,
|
||||
));
|
||||
$stmt->closeCursor();
|
||||
|
||||
$story_id = $databox->get_connection()->lastInsertId();
|
||||
|
||||
@@ -1319,6 +1323,7 @@ class record_adapter implements record_Interface, cache_cacheableInterface
|
||||
':originalname' => $file->getOriginalName(),
|
||||
':mime' => $file->getFile()->getMimeType(),
|
||||
));
|
||||
$stmt->closeCursor();
|
||||
|
||||
$record_id = $databox->get_connection()->lastInsertId();
|
||||
|
||||
|
@@ -761,10 +761,10 @@ class task_period_RecordMover extends task_appboxAbstract
|
||||
while (($row = $stmt->fetch(PDO::FETCH_ASSOC))) {
|
||||
$result['rids'][] = $row['record_id'];
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
} else {
|
||||
$result['err'] = $connbas->last_error();
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
@@ -70,6 +70,7 @@ class oauthv2_application_test extends \PhraseanetWebTestCaseAuthenticatedAbstra
|
||||
$t = array(':id' => $app->get_id());
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute($t);
|
||||
$stmt->closeCursor();
|
||||
$sql = '
|
||||
DELETE FROM api_accounts
|
||||
WHERE api_account_id = :id
|
||||
@@ -78,6 +79,7 @@ class oauthv2_application_test extends \PhraseanetWebTestCaseAuthenticatedAbstra
|
||||
$t = array(':id' => $acc->get_id());
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute($t);
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +121,7 @@ class oauthv2_application_test extends \PhraseanetWebTestCaseAuthenticatedAbstra
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute($t);
|
||||
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
|
||||
return $result;
|
||||
}
|
||||
@@ -131,6 +134,7 @@ class oauthv2_application_test extends \PhraseanetWebTestCaseAuthenticatedAbstra
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->execute($t);
|
||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
|
||||
return new \API_OAuth2_Account(self::$DI['app'], $row["api_account_id"]);
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ namespace Alchemy\Tests\Phrasea\Authentication\Phrasea;
|
||||
use Alchemy\Phrasea\Authentication\Phrasea\NativeAuthentication;
|
||||
use Alchemy\Phrasea\Authentication\Exception\AccountLockedException;
|
||||
|
||||
class NativeAuthenticationTest extends \PHPUnit_Framework_TestCase
|
||||
class NativeAuthenticationTest extends \PhraseanetPHPUnitAbstract
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideReservedUsernames
|
||||
@@ -16,7 +16,7 @@ class NativeAuthenticationTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$encoder = $this->getEncoderMock();
|
||||
$oldEncoder = $this->getOldEncoderMock();
|
||||
$conn = $this->getMock('connection_interface');
|
||||
$conn = $this->createConnectionMock();
|
||||
$request = $this->getRequestMock();
|
||||
|
||||
$auth = new NativeAuthentication($encoder, $oldEncoder, $conn);
|
||||
@@ -180,7 +180,7 @@ class NativeAuthenticationTest extends \PHPUnit_Framework_TestCase
|
||||
$encoder = $this->getEncoderMock();
|
||||
$oldEncoder = $this->getOldEncoderMock();
|
||||
|
||||
$conn = $this->getMock('connection_interface');
|
||||
$conn = $this->createConnectionMock();
|
||||
|
||||
$statement = $this->getMock('PDOStatement');
|
||||
$statement
|
||||
@@ -248,7 +248,7 @@ class NativeAuthenticationTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
private function getConnectionMock($username, $row = null)
|
||||
{
|
||||
$conn = $this->getMock('connection_interface');
|
||||
$conn = $this->createConnectionMock();
|
||||
|
||||
$statement = $this->getMock('PDOStatement');
|
||||
|
||||
|
@@ -90,6 +90,7 @@ class PhraseaEngineTest extends SearchEngineAbstractTest
|
||||
|
||||
break;
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
$date = new \DateTime('-1 months');
|
||||
|
||||
|
@@ -107,6 +107,8 @@ abstract class PhraseanetPHPUnitAbstract extends WebTestCase
|
||||
|
||||
parent::setUp();
|
||||
|
||||
connection::close_connections();
|
||||
|
||||
\PHPUnit_Framework_Error_Warning::$enabled = true;
|
||||
\PHPUnit_Framework_Error_Notice::$enabled = true;
|
||||
|
||||
@@ -989,4 +991,9 @@ abstract class PhraseanetPHPUnitAbstract extends WebTestCase
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function createConnectionMock()
|
||||
{
|
||||
return $this->getMock('connection_interface', array('close', 'get_credentials', 'server_info', 'prepare', 'beginTransaction', 'commit', 'ping', 'get_name', 'is_multi_db'));
|
||||
}
|
||||
}
|
||||
|
@@ -136,10 +136,12 @@ abstract class PhraseanetWebTestCaseAuthenticatedAbstract extends PhraseanetPHPU
|
||||
->get_connection()
|
||||
->prepare('DROP DATABASE IF EXISTS `unit_test_db`');
|
||||
$stmt->execute();
|
||||
$stmt->closeCursor();
|
||||
$stmt = self::$DI['app']['phraseanet.appbox']
|
||||
->get_connection()
|
||||
->prepare('DELETE FROM sbas WHERE dbname = "unit_test_db"');
|
||||
$stmt->execute();
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
|
||||
protected function createDatabase()
|
||||
|
@@ -47,6 +47,7 @@ class Session_LoggerTest extends PhraseanetPHPUnitAbstract
|
||||
$stmt->execute($params);
|
||||
$this->assertEquals(1, $stmt->rowCount());
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$stmt->closeCursor();
|
||||
$this->assertEquals($this->object->get_id(), $row['id']);
|
||||
$log_id = $this->object->get_id();
|
||||
$ses_id = self::$DI['app']['session']->get('session_id');
|
||||
@@ -67,5 +68,6 @@ class Session_LoggerTest extends PhraseanetPHPUnitAbstract
|
||||
$this->assertEquals(1, $stmt->rowCount());
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$this->assertEquals($log_id, $row['id']);
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
}
|
||||
|
13
tests/classes/connectionTest.php
Normal file
13
tests/classes/connectionTest.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
class connectionTest extends \PhraseanetPHPUnitAbstract
|
||||
{
|
||||
public function testMysqlTimeoutIsHandled()
|
||||
{
|
||||
$conn = connection::getPDOConnection(self::$DI['app']);
|
||||
$conn->exec('SET @@local.wait_timeout= 1');
|
||||
usleep(1200000);
|
||||
$conn->exec('SHOW DATABASES');
|
||||
connection::close_connections();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user