Implement handling lost connection on MySQL

parent 4cb9e36d
......@@ -13,6 +13,7 @@ use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\PingableConnection;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Exception\ConnectionLost;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
......@@ -614,7 +615,7 @@ class Connection implements DriverConnection
return $stmt->fetch(FetchMode::ASSOCIATIVE);
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -641,7 +642,7 @@ class Connection implements DriverConnection
return $stmt->fetch(FetchMode::NUMERIC);
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -668,7 +669,7 @@ class Connection implements DriverConnection
return $stmt->fetch(FetchMode::COLUMN);
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -952,7 +953,7 @@ class Connection implements DriverConnection
return $stmt->fetchAll(FetchMode::NUMERIC);
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -978,7 +979,7 @@ class Connection implements DriverConnection
return $stmt->fetchAll(FetchMode::ASSOCIATIVE);
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -1004,7 +1005,7 @@ class Connection implements DriverConnection
return $stmt->fetchAll(FetchMode::COLUMN);
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -1032,7 +1033,7 @@ class Connection implements DriverConnection
}
}
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -1060,7 +1061,7 @@ class Connection implements DriverConnection
}
}
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -1088,7 +1089,7 @@ class Connection implements DriverConnection
}
}
} catch (Throwable $e) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $e, $query);
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
}
......@@ -1105,8 +1106,8 @@ class Connection implements DriverConnection
{
try {
$stmt = new Statement($statement, $this);
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement);
} catch (Throwable $e) {
$this->handleExceptionDuringQuery($e, $statement);
}
$stmt->setFetchMode($this->defaultFetchMode);
......@@ -1156,8 +1157,8 @@ class Connection implements DriverConnection
} else {
$stmt = $connection->query($query);
}
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types));
} catch (Throwable $e) {
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
$stmt->setFetchMode($this->defaultFetchMode);
......@@ -1263,8 +1264,8 @@ class Connection implements DriverConnection
try {
$statement = $connection->query(...$args);
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $args[0]);
} catch (Throwable $e) {
$this->handleExceptionDuringQuery($e, $args[0]);
}
$statement->setFetchMode($this->defaultFetchMode);
......@@ -1316,8 +1317,8 @@ class Connection implements DriverConnection
} else {
$result = $connection->exec($query);
}
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types));
} catch (Throwable $e) {
$this->handleExceptionDuringQuery($e, $query, $params, $types);
}
if ($logger) {
......@@ -1347,8 +1348,8 @@ class Connection implements DriverConnection
try {
$result = $connection->exec($statement);
} catch (Throwable $ex) {
throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement);
} catch (Throwable $e) {
$this->handleExceptionDuringQuery($e, $statement);
}
if ($logger) {
......@@ -1954,4 +1955,59 @@ class Connection implements DriverConnection
return false;
}
}
/**
* @internal
*
* @param array<int, mixed>|array<string, mixed> $params
* @param array<int, int|string>|array<string, int|string> $types
*
* @throws DBALException
*
* @psalm-return never-return
*/
public function handleExceptionDuringQuery(Throwable $e, string $sql, array $params = [], array $types = []): void
{
$this->throw(
DBALException::driverExceptionDuringQuery(
$this->_driver,
$e,
$sql,
$this->resolveParams($params, $types)
)
);
}
/**
* @internal
*
* @throws DBALException
*
* @psalm-return never-return
*/
public function handleDriverException(Throwable $e): void
{
$this->throw(
DBALException::driverException(
$this->_driver,
$e
)
);
}
/**
* @internal
*
* @throws DBALException
*
* @psalm-return never-return
*/
private function throw(DBALException $e): void
{
if ($e instanceof ConnectionLost) {
$this->close();
}
throw $e;
}
}
......@@ -7,6 +7,7 @@ use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\ConnectionLost;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
......@@ -108,6 +109,9 @@ abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver,
case '2005':
return new ConnectionException($message, $exception);
case '2006':
return new ConnectionLost($message, $exception);
case '1048':
case '1121':
case '1138':
......
<?php
namespace Doctrine\DBAL\Exception;
/**
* @psalm-immutable
*/
final class ConnectionLost extends ConnectionException
{
}
......@@ -161,12 +161,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
$logger->stopQuery();
}
throw DBALException::driverExceptionDuringQuery(
$this->conn->getDriver(),
$ex,
$this->sql,
$this->conn->resolveParams($this->params, $this->types)
);
$this->conn->handleExceptionDuringQuery($ex, $this->sql, $this->params, $this->types);
}
if ($logger) {
......@@ -297,7 +292,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
return $this->stmt->fetch(FetchMode::NUMERIC);
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......@@ -315,7 +310,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
return $this->stmt->fetch(FetchMode::ASSOCIATIVE);
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......@@ -333,7 +328,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
return $this->stmt->fetch(FetchMode::COLUMN);
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......@@ -351,7 +346,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
return $this->stmt->fetchAll(FetchMode::NUMERIC);
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......@@ -369,7 +364,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
return $this->stmt->fetchAll(FetchMode::ASSOCIATIVE);
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......@@ -387,7 +382,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
return $this->stmt->fetchAll(FetchMode::COLUMN);
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......@@ -411,7 +406,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
}
}
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......@@ -435,7 +430,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
}
}
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......@@ -459,7 +454,7 @@ class Statement implements IteratorAggregate, DriverStatement, Result
}
}
} catch (Exception $e) {
throw DBALException::driverException($this->conn->getDriver(), $e);
$this->conn->handleDriverException($e);
}
}
......
......@@ -7,6 +7,10 @@ parameters:
reportUnmatchedIgnoredErrors: false
checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false
earlyTerminatingMethodCalls:
Doctrine\DBAL\Connection:
- handleDriverException
- handleExceptionDuringQuery
ignoreErrors:
# extension not available
- '~^(Used )?(Function|Constant) sasql_\S+ not found\.\z~i'
......
<?php
namespace Doctrine\Tests\DBAL\Functional;
use Doctrine\DBAL\Exception\ConnectionLost;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\Tests\DbalFunctionalTestCase;
use function sleep;
class ConnectionLostTest extends DbalFunctionalTestCase
{
protected function setUp(): void
{
parent::setUp();
if ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
return;
}
$this->markTestSkipped('Currently only supported with MySQL');
}
protected function tearDown(): void
{
$this->resetSharedConn();
parent::tearDown();
}
public function testConnectionLost(): void
{
$this->connection->query('SET SESSION wait_timeout=1');
sleep(2);
$query = $this->connection->getDatabasePlatform()
->getDummySelectSQL();
try {
// in addition to the error, PHP 7.3 will generate a warning that needs to be
// suppressed in order to not let PHPUnit handle it before the actual error
@$this->connection->executeQuery($query);
} catch (ConnectionLost $e) {
self::assertEquals(1, $this->connection->fetchOne($query));
return;
}
self::fail('The connection should have lost');
}
}
......@@ -131,10 +131,9 @@ class StatementTest extends DbalTestCase
->method('getSQLLogger')
->will($this->returnValue($logger));
// Needed to satisfy construction of DBALException
$this->conn->expects($this->any())
->method('resolveParams')
->will($this->returnValue([]));
->method('handleExceptionDuringQuery')
->will($this->throwException(new DBALException()));
$logger->expects($this->once())
->method('startQuery');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment