Commit b9cd6fff authored by Steve Müller's avatar Steve Müller

fix dropping database with active connection on PostgreSQL

parent 2bd2232a
...@@ -72,4 +72,12 @@ class PostgreSQL92Platform extends PostgreSQL91Platform ...@@ -72,4 +72,12 @@ class PostgreSQL92Platform extends PostgreSQL91Platform
parent::initializeDoctrineTypeMappings(); parent::initializeDoctrineTypeMappings();
$this->doctrineTypeMapping['json'] = 'json_array'; $this->doctrineTypeMapping['json'] = 'json_array';
} }
/**
* {@inheritdoc}
*/
public function getCloseActiveDatabaseConnectionsSQL($database)
{
return "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$database'";
}
} }
...@@ -420,6 +420,34 @@ class PostgreSqlPlatform extends AbstractPlatform ...@@ -420,6 +420,34 @@ class PostgreSqlPlatform extends AbstractPlatform
return 'CREATE DATABASE ' . $name; return 'CREATE DATABASE ' . $name;
} }
/**
* Returns the SQL statement for disallowing new 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 disallow new connections for.
*
* @return string
*/
public function getDisallowDatabaseConnectionsSQL($database)
{
return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = '$database'";
}
/**
* Returns the SQL statement for closing 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 string
*/
public function getCloseActiveDatabaseConnectionsSQL($database)
{
return "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = '$database'";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
namespace Doctrine\DBAL\Schema; namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Type;
/** /**
...@@ -100,6 +101,33 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager ...@@ -100,6 +101,33 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
}); });
} }
/**
* {@inheritdoc}
*/
public function dropDatabase($database)
{
try {
parent::dropDatabase($database);
} catch (DriverException $exception) {
// If we have a SQLSTATE 55006, 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->getSQLState() !== '55006') {
throw $exception;
}
$this->_execSql(
array(
$this->_platform->getDisallowDatabaseConnectionsSQL($database),
$this->_platform->getCloseActiveDatabaseConnectionsSQL($database),
)
);
parent::dropDatabase($database);
}
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -42,6 +42,36 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest ...@@ -42,6 +42,36 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest
$this->_sm = $this->_conn->getSchemaManager(); $this->_sm = $this->_conn->getSchemaManager();
} }
/**
* @group DBAL-1220
*/
public function testDropsDatabaseWithActiveConnections()
{
if (! $this->_sm->getDatabasePlatform()->supportsCreateDropDatabase()) {
$this->markTestSkipped('Cannot drop Database client side with this Driver.');
}
$this->_sm->dropAndCreateDatabase('test_drop_database');
$this->assertContains('test_drop_database', $this->_sm->listDatabases());
$params = $this->_conn->getParams();
$params['dbname'] = 'test_drop_database';
$user = isset($params['user']) ? $params['user'] : null;
$password = isset($params['password']) ? $params['password'] : null;
$connection = $this->_conn->getDriver()->connect($params, $user, $password);
$this->assertInstanceOf('Doctrine\DBAL\Driver\Connection', $connection);
$this->_sm->dropDatabase('test_drop_database');
$this->assertNotContains('test_drop_database', $this->_sm->listDatabases());
unset($connection);
}
/** /**
* @group DBAL-195 * @group DBAL-195
*/ */
......
...@@ -28,6 +28,32 @@ class SqliteSchemaManagerTest extends SchemaManagerFunctionalTestCase ...@@ -28,6 +28,32 @@ class SqliteSchemaManagerTest extends SchemaManagerFunctionalTestCase
$this->assertEquals(false, file_exists($path)); $this->assertEquals(false, file_exists($path));
} }
/**
* @group DBAL-1220
*/
public function testDropsDatabaseWithActiveConnections()
{
$this->_sm->dropAndCreateDatabase('test_drop_database');
$this->assertFileExists('test_drop_database');
$params = $this->_conn->getParams();
$params['dbname'] = 'test_drop_database';
$user = isset($params['user']) ? $params['user'] : null;
$password = isset($params['password']) ? $params['password'] : null;
$connection = $this->_conn->getDriver()->connect($params, $user, $password);
$this->assertInstanceOf('Doctrine\DBAL\Driver\Connection', $connection);
$this->_sm->dropDatabase('test_drop_database');
$this->assertFileNotExists('test_drop_database');
unset($connection);
}
public function testRenameTable() public function testRenameTable()
{ {
$this->createTestTable('oldname'); $this->createTestTable('oldname');
......
...@@ -783,4 +783,26 @@ abstract class AbstractPostgreSqlPlatformTestCase extends AbstractPlatformTestCa ...@@ -783,4 +783,26 @@ abstract class AbstractPostgreSqlPlatformTestCase extends AbstractPlatformTestCa
'ALTER INDEX idx_foo RENAME TO idx_foo_renamed', 'ALTER INDEX idx_foo RENAME TO idx_foo_renamed',
); );
} }
/**
* @group DBAL-1220
*/
public function testReturnsDisallowDatabaseConnectionsSQL()
{
$this->assertSame(
"UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'foo'",
$this->_platform->getDisallowDatabaseConnectionsSQL('foo')
);
}
/**
* @group DBAL-1220
*/
public function testReturnsCloseActiveDatabaseConnectionsSQL()
{
$this->assertSame(
"SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = 'foo'",
$this->_platform->getCloseActiveDatabaseConnectionsSQL('foo')
);
}
} }
...@@ -56,4 +56,15 @@ class PostgreSQL92PlatformTest extends AbstractPostgreSqlPlatformTestCase ...@@ -56,4 +56,15 @@ class PostgreSQL92PlatformTest extends AbstractPostgreSqlPlatformTestCase
$this->assertTrue($this->_platform->hasDoctrineTypeMappingFor('json')); $this->assertTrue($this->_platform->hasDoctrineTypeMappingFor('json'));
$this->assertEquals('json_array', $this->_platform->getDoctrineTypeMapping('json')); $this->assertEquals('json_array', $this->_platform->getDoctrineTypeMapping('json'));
} }
/**
* @group DBAL-1220
*/
public function testReturnsCloseActiveDatabaseConnectionsSQL()
{
$this->assertSame(
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'foo'",
$this->_platform->getCloseActiveDatabaseConnectionsSQL('foo')
);
}
} }
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