Commit 9e00fec3 authored by Steve Müller's avatar Steve Müller Committed by Marco Pivetta

fix dropping inuse databases on SQL Server

parent d153ef03
......@@ -19,9 +19,10 @@
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\DBALException;
class SQLSrvException extends DBALException
use Doctrine\DBAL\Driver\AbstractDriverException;
class SQLSrvException extends AbstractDriverException
{
/**
* Helper method to turn sql server errors into exception.
......@@ -32,13 +33,24 @@ class SQLSrvException extends DBALException
{
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
$message = "";
$sqlState = null;
$errorCode = null;
foreach ($errors as $error) {
$message .= "SQLSTATE [".$error['SQLSTATE'].", ".$error['code']."]: ". $error['message']."\n";
if (null === $sqlState) {
$sqlState = $error['SQLSTATE'];
}
if (null === $errorCode) {
$errorCode = $error['code'];
}
}
if ( ! $message) {
$message = "SQL Server error occurred but no error message was retrieved from driver.";
}
return new self(rtrim($message));
return new self(rtrim($message), $sqlState, $errorCode);
}
}
......@@ -19,7 +19,8 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Driver\SQLSrv\SQLSrvException;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Types\Type;
/**
......@@ -34,6 +35,34 @@ use Doctrine\DBAL\Types\Type;
*/
class SQLServerSchemaManager extends AbstractSchemaManager
{
/**
* {@inheritdoc}
*/
public function dropDatabase($database)
{
try {
parent::dropDatabase($database);
} catch (DBALException $exception) {
$exception = $exception->getPrevious();
if (! $exception instanceof DriverException) {
throw $exception;
}
// If we have a error code 3702, the drop database operation failed
// because of active connections on the database.
// To force dropping the database, we first have to close all active connections
// on that database and issue the drop database operation again.
if ($exception->getErrorCode() !== 3702) {
throw $exception;
}
$this->closeActiveDatabaseConnections($database);
parent::dropDatabase($database);
}
}
/**
* {@inheritdoc}
*/
......@@ -212,7 +241,7 @@ class SQLServerSchemaManager extends AbstractSchemaManager
} else {
throw $e;
}
} catch (SQLSrvException $e) {
} catch (DBALException $e) {
if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) {
return array();
} else {
......@@ -258,4 +287,25 @@ class SQLServerSchemaManager extends AbstractSchemaManager
WHERE Col.[Name] = " . $this->_conn->quote($column) ." AND Tab.[Name] = " . $this->_conn->quote($table) . "
ORDER BY Col.[Name]";
}
/**
* Closes currently active connections on the given database.
*
* This is useful to force DROP DATABASE operations which could fail because of active connections.
*
* @param string $database The name of the database to close currently active connections for.
*
* @return void
*/
private function closeActiveDatabaseConnections($database)
{
$database = new Identifier($database);
$this->_execSql(
sprintf(
'ALTER DATABASE %s SET SINGLE_USER WITH ROLLBACK IMMEDIATE',
$database->getQuotedName($this->_platform)
)
);
}
}
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