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

Merge pull request #444 from doctrine/hotfix/DBAL-669

DBAL-669 - Update SQL generated by the schema tool will also create schemas
parents 83f92c3f 1c9fe8df
......@@ -1788,19 +1788,6 @@ abstract class AbstractPlatform
throw DBALException::notSupported(__METHOD__);
}
/**
* Checks whether the schema $schemaName needs creating.
*
* @param string $schemaName
*
* @return boolean
* @throws \Doctrine\DBAL\DBALException If not supported on this platform.
*/
public function schemaNeedsCreation($schemaName)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Quotes a string so that it can be safely used as a table or column name,
* even if it is a reserved word of the platform. This also detects identifier
......@@ -2678,6 +2665,18 @@ abstract class AbstractPlatform
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL statement for retrieving the namespaces defined in the database.
*
* @return string
*
* @throws \Doctrine\DBAL\DBALException If not supported on this platform.
*/
public function getListNamespacesSQL()
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param string $database
*
......
......@@ -222,6 +222,14 @@ class PostgreSqlPlatform extends AbstractPlatform
return 'SELECT datname FROM pg_database';
}
/**
* {@inheritDoc}
*/
public function getListNamespacesSQL()
{
return "SELECT nspname FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema'";
}
/**
* {@inheritDoc}
*/
......@@ -660,14 +668,6 @@ class PostgreSqlPlatform extends AbstractPlatform
return 'CREATE SCHEMA ' . $schemaName;
}
/**
* {@inheritDoc}
*/
public function schemaNeedsCreation($schemaName)
{
return !in_array($schemaName, array('default', 'public'));
}
/**
* {@inheritDoc}
*/
......
......@@ -417,14 +417,6 @@ class SQLAnywherePlatform extends AbstractPlatform
return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index);
}
/**
* {@inheritdoc}
*/
public function getCreateSchemaSQL($schemaName)
{
return 'CREATE SCHEMA AUTHORIZATION ' . $schemaName;
}
/**
* {@inheritdoc}
*/
......@@ -519,14 +511,6 @@ class SQLAnywherePlatform extends AbstractPlatform
return 'DATE';
}
/**
* {@inheritdoc}
*/
public function getDefaultSchemaName()
{
return 'DBA';
}
/**
* {@inheritdoc}
*/
......@@ -1179,14 +1163,6 @@ class SQLAnywherePlatform extends AbstractPlatform
return true;
}
/**
* {@inheritdoc}
*/
public function schemaNeedsCreation($schemaName)
{
return $schemaName !== 'DBA';
}
/**
* {@inheritdoc}
*/
......@@ -1203,14 +1179,6 @@ class SQLAnywherePlatform extends AbstractPlatform
return true;
}
/**
* {@inheritdoc}
*/
public function supportsSchemas()
{
return true;
}
/**
* {@inheritdoc}
*/
......
......@@ -155,14 +155,6 @@ class SQLServerPlatform extends AbstractPlatform
return 'CREATE SCHEMA ' . $schemaName;
}
/**
* {@inheritDoc}
*/
public function schemaNeedsCreation($schemaName)
{
return $schemaName !== 'dbo';
}
/**
* {@inheritDoc}
*/
......@@ -1025,6 +1017,14 @@ class SQLServerPlatform extends AbstractPlatform
return 'SELECT * FROM SYS.DATABASES';
}
/**
* {@inheritDoc}
*/
public function getListNamespacesSQL()
{
return "SELECT name FROM SYS.SCHEMAS WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')";
}
/**
* {@inheritDoc}
*/
......
......@@ -115,6 +115,20 @@ abstract class AbstractSchemaManager
return $this->_getPortableDatabasesList($databases);
}
/**
* Returns a list of all namespaces in the current database.
*
* @return array
*/
public function listNamespaceNames()
{
$sql = $this->_platform->getListNamespacesSQL();
$namespaces = $this->_conn->fetchAll($sql);
return $this->getPortableNamespacesList($namespaces);
}
/**
* Lists the available sequences for this connection.
*
......@@ -651,6 +665,24 @@ abstract class AbstractSchemaManager
return $list;
}
/**
* Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition.
*
* @param array $namespaces The list of namespace names in the native DBMS data definition.
*
* @return array
*/
protected function getPortableNamespacesList(array $namespaces)
{
$namespacesList = array();
foreach ($namespaces as $namespace) {
$namespacesList[] = $this->getPortableNamespaceDefinition($namespace);
}
return $namespacesList;
}
/**
* @param array $database
*
......@@ -661,6 +693,18 @@ abstract class AbstractSchemaManager
return $database;
}
/**
* Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition.
*
* @param array $namespace The native DBMS namespace definition.
*
* @return mixed
*/
protected function getPortableNamespaceDefinition(array $namespace)
{
return $namespace;
}
/**
* @param array $functions
*
......@@ -979,13 +1023,21 @@ abstract class AbstractSchemaManager
*/
public function createSchema()
{
$namespaces = array();
if ($this->_platform->supportsSchemas()) {
$namespaces = $this->listNamespaceNames();
}
$sequences = array();
if ($this->_platform->supportsSequences()) {
$sequences = $this->listSequences();
}
$tables = $this->listTables();
return new Schema($tables, $sequences, $this->createSchemaConfig());
return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
}
/**
......
......@@ -62,6 +62,18 @@ class Comparator
$foreignKeysToTable = array();
foreach ($toSchema->getNamespaces() as $namespace) {
if ( ! $fromSchema->hasNamespace($namespace)) {
$diff->newNamespaces[$namespace] = $namespace;
}
}
foreach ($fromSchema->getNamespaces() as $namespace) {
if ( ! $toSchema->hasNamespace($namespace)) {
$diff->removedNamespaces[$namespace] = $namespace;
}
}
foreach ($toSchema->getTables() as $table) {
$tableName = $table->getShortestName($toSchema->getName());
if ( ! $fromSchema->hasTable($tableName)) {
......@@ -377,7 +389,7 @@ class Comparator
$changedProperties[] = $property;
}
}
if ($properties1['default'] != $properties2['default'] ||
// Null values need to be checked additionally as they tell whether to create or drop a default value.
// null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation.
......
......@@ -283,6 +283,14 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
return $list;
}
/**
* {@inheritdoc}
*/
protected function getPortableNamespaceDefinition(array $namespace)
{
return $namespace['nspname'];
}
/**
* {@inheritdoc}
*/
......
......@@ -180,6 +180,14 @@ class SQLServerSchemaManager extends AbstractSchemaManager
return $database['name'];
}
/**
* {@inheritdoc}
*/
protected function getPortableNamespaceDefinition(array $namespace)
{
return $namespace['name'];
}
/**
* {@inheritdoc}
*/
......
......@@ -54,6 +54,13 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
*/
class Schema extends AbstractAsset
{
/**
* The namespaces in this schema.
*
* @var array
*/
private $namespaces = array();
/**
* @var \Doctrine\DBAL\Schema\Table[]
*/
......@@ -73,15 +80,24 @@ class Schema extends AbstractAsset
* @param \Doctrine\DBAL\Schema\Table[] $tables
* @param \Doctrine\DBAL\Schema\Sequence[] $sequences
* @param \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig
* @param array $namespaces
*/
public function __construct(array $tables=array(), array $sequences=array(), SchemaConfig $schemaConfig=null)
{
public function __construct(
array $tables = array(),
array $sequences = array(),
SchemaConfig $schemaConfig = null,
array $namespaces = array()
) {
if ($schemaConfig == null) {
$schemaConfig = new SchemaConfig();
}
$this->_schemaConfig = $schemaConfig;
$this->_setName($schemaConfig->getName() ?: 'public');
foreach ($namespaces as $namespace) {
$this->createNamespace($namespace);
}
foreach ($tables as $table) {
$this->_addTable($table);
}
......@@ -108,11 +124,17 @@ class Schema extends AbstractAsset
*/
protected function _addTable(Table $table)
{
$namespaceName = $table->getNamespaceName();
$tableName = $table->getFullQualifiedName($this->getName());
if (isset($this->_tables[$tableName])) {
throw SchemaException::tableAlreadyExists($tableName);
}
if ( ! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
$this->createNamespace($namespaceName);
}
$this->_tables[$tableName] = $table;
$table->setSchemaConfig($this->_schemaConfig);
}
......@@ -126,13 +148,30 @@ class Schema extends AbstractAsset
*/
protected function _addSequence(Sequence $sequence)
{
$namespaceName = $sequence->getNamespaceName();
$seqName = $sequence->getFullQualifiedName($this->getName());
if (isset($this->_sequences[$seqName])) {
throw SchemaException::sequenceAlreadyExists($seqName);
}
if ( ! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
$this->createNamespace($namespaceName);
}
$this->_sequences[$seqName] = $sequence;
}
/**
* Returns the namespaces of this schema.
*
* @return array A list of namespace names.
*/
public function getNamespaces()
{
return $this->namespaces;
}
/**
* Gets all tables of this schema.
*
......@@ -167,9 +206,8 @@ class Schema extends AbstractAsset
*/
private function getFullQualifiedAssetName($name)
{
if ($this->isIdentifierQuoted($name)) {
$name = $this->trimQuotes($name);
}
$name = $this->getUnquotedAssetName($name);
if (strpos($name, ".") === false) {
$name = $this->getName() . "." . $name;
}
......@@ -177,6 +215,36 @@ class Schema extends AbstractAsset
return strtolower($name);
}
/**
* Returns the unquoted representation of a given asset name.
*
* @param string $assetName Quoted or unquoted representation of an asset name.
*
* @return string
*/
private function getUnquotedAssetName($assetName)
{
if ($this->isIdentifierQuoted($assetName)) {
return $this->trimQuotes($assetName);
}
return $assetName;
}
/**
* Does this schema have a namespace with the given name?
*
* @param string $namespaceName
*
* @return boolean
*/
public function hasNamespace($namespaceName)
{
$namespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
return isset($this->namespaces[$namespaceName]);
}
/**
* Does this schema have a table with the given name?
*
......@@ -238,6 +306,26 @@ class Schema extends AbstractAsset
return $this->_sequences;
}
/**
* Creates a new namespace.
*
* @param string $namespaceName The name of the namespace to create.
*
* @return \Doctrine\DBAL\Schema\Schema This schema instance.
*/
public function createNamespace($namespaceName)
{
$unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
if (isset($this->namespaces[$unquotedNamespaceName])) {
throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName);
}
$this->namespaces[$unquotedNamespaceName] = $namespaceName;
return $this;
}
/**
* Creates a new table.
*
......
......@@ -37,6 +37,20 @@ class SchemaDiff
*/
public $fromSchema;
/**
* All added namespaces.
*
* @var string[]
*/
public $newNamespaces = array();
/**
* All removed namespaces.
*
* @var string[]
*/
public $removedNamespaces = array();
/**
* All added tables.
*
......@@ -132,6 +146,12 @@ class SchemaDiff
{
$sql = array();
if ($platform->supportsSchemas()) {
foreach ($this->newNamespaces as $newNamespace) {
$sql[] = $platform->getCreateSchemaSQL($newNamespace);
}
}
if ($platform->supportsForeignKeyConstraints() && $saveMode == false) {
foreach ($this->orphanedForeignKeys as $orphanedForeignKey) {
$sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTableName());
......
......@@ -31,6 +31,7 @@ class SchemaException extends \Doctrine\DBAL\DBALException
const SEQUENCE_ALREADY_EXISTS = 80;
const INDEX_INVALID_NAME = 90;
const FOREIGNKEY_DOESNT_EXIST = 100;
const NAMESPACE_ALREADY_EXISTS = 110;
/**
* @param string $tableName
......@@ -85,6 +86,19 @@ class SchemaException extends \Doctrine\DBAL\DBALException
return new self("There is no column with name '$columnName' on table '$table'.", self::COLUMN_DOESNT_EXIST);
}
/**
* @param string $namespaceName
*
* @return \Doctrine\DBAL\Schema\SchemaException
*/
static public function namespaceAlreadyExists($namespaceName)
{
return new self(
sprintf("The namespace with name '%s' already exists.", $namespaceName),
self::NAMESPACE_ALREADY_EXISTS
);
}
/**
* @param string $tableName
*
......
......@@ -29,7 +29,7 @@ use Doctrine\DBAL\Schema\Index;
/**
* Abstract Visitor with empty methods for easy extension.
*/
class AbstractVisitor implements Visitor
class AbstractVisitor implements Visitor, NamespaceVisitor
{
/**
* @param \Doctrine\DBAL\Schema\Schema $schema
......@@ -38,6 +38,13 @@ class AbstractVisitor implements Visitor
{
}
/**
* {@inheritdoc}
*/
public function acceptNamespace($namespaceName)
{
}
/**
* @param \Doctrine\DBAL\Schema\Table $table
*/
......
......@@ -26,6 +26,11 @@ use Doctrine\DBAL\Schema\Sequence;
class CreateSchemaSqlCollector extends AbstractVisitor
{
/**
* @var array
*/
private $createNamespaceQueries = array();
/**
* @var array
*/
......@@ -58,14 +63,22 @@ class CreateSchemaSqlCollector extends AbstractVisitor
/**
* {@inheritdoc}
*/
public function acceptTable(Table $table)
public function acceptNamespace($namespaceName)
{
$namespace = $this->getNamespace($table);
if ($this->platform->supportsSchemas()) {
$this->createNamespaceQueries = array_merge(
$this->createNamespaceQueries,
(array) $this->platform->getCreateSchemaSQL($namespaceName)
);
}
}
$this->createTableQueries[$namespace] = array_merge(
$this->createTableQueries[$namespace],
$this->platform->getCreateTableSQL($table)
);
/**
* {@inheritdoc}
*/
public function acceptTable(Table $table)
{
$this->createTableQueries = array_merge($this->createTableQueries, (array) $this->platform->getCreateTableSQL($table));
}
/**
......@@ -73,11 +86,9 @@ class CreateSchemaSqlCollector extends AbstractVisitor
*/
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint)
{
$namespace = $this->getNamespace($localTable);
if ($this->platform->supportsForeignKeyConstraints()) {
$this->createFkConstraintQueries[$namespace] = array_merge(
$this->createFkConstraintQueries[$namespace],
$this->createFkConstraintQueries = array_merge(
$this->createFkConstraintQueries,
(array) $this->platform->getCreateForeignKeySQL(
$fkConstraint, $localTable
)
......@@ -90,41 +101,18 @@ class CreateSchemaSqlCollector extends AbstractVisitor
*/
public function acceptSequence(Sequence $sequence)
{
$namespace = $this->getNamespace($sequence);
$this->createSequenceQueries[$namespace] = array_merge(
$this->createSequenceQueries[$namespace],
$this->createSequenceQueries = array_merge(
$this->createSequenceQueries,
(array)$this->platform->getCreateSequenceSQL($sequence)
);
}
/**
* @param \Doctrine\DBAL\Schema\AbstractAsset $asset
*
* @return string
*/
private function getNamespace($asset)
{
$namespace = $asset->getNamespaceName();
if ( !isset($namespace)) {
$namespace = $this->platform->supportsSchemas() ? $this->platform->getDefaultSchemaName() : 'default';
}
if ( !isset($this->createTableQueries[$namespace])) {
$this->createTableQueries[$namespace] = array();
$this->createSequenceQueries[$namespace] = array();
$this->createFkConstraintQueries[$namespace] = array();
}
return $namespace;
}
/**
* @return void
*/
public function resetQueries()
{
$this->createNamespaceQueries = array();
$this->createTableQueries = array();
$this->createSequenceQueries = array();
$this->createFkConstraintQueries = array();
......@@ -139,23 +127,20 @@ class CreateSchemaSqlCollector extends AbstractVisitor
{
$sql = array();
foreach (array_keys($this->createTableQueries) as $namespace) {
if ($this->platform->supportsSchemas() && $this->platform->schemaNeedsCreation($namespace)) {
$query = $this->platform->getCreateSchemaSQL($namespace);
$sql[] = $query;
}
foreach ($this->createNamespaceQueries as $schemaSql) {
$sql = array_merge($sql, (array) $schemaSql);
}
foreach ($this->createTableQueries as $schemaSql) {
$sql = array_merge($sql, $schemaSql);
$sql = array_merge($sql, (array) $schemaSql);
}
foreach ($this->createSequenceQueries as $schemaSql) {
$sql = array_merge($sql, $schemaSql);
$sql = array_merge($sql, (array) $schemaSql);
}
foreach ($this->createFkConstraintQueries as $schemaSql) {
$sql = array_merge($sql, $schemaSql);
$sql = array_merge($sql, (array) $schemaSql);
}
return $sql;
......
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Schema\Visitor;
/**
* Visitor that can visit schema namespaces.
*
* @author Steve Müller <st.mueller@dzh-online.de>
* @link www.doctrine-project.org
* @since 2.5
*/
interface NamespaceVisitor
{
/**
* Accepts a schema namespace name.
*
* @param string $namespaceName The schema namespace name to accept.
*/
public function acceptNamespace($namespaceName);
}
......@@ -506,14 +506,6 @@ abstract class AbstractPlatformTestCase extends \Doctrine\Tests\DbalTestCase
$this->_platform->getCreateSchemaSQL('schema');
}
/**
* @expectedException \Doctrine\DBAL\DBALException
*/
public function testSchemaNeedsCreation()
{
$this->_platform->schemaNeedsCreation('schema');
}
/**
* @group DBAL-585
*/
......
......@@ -387,19 +387,6 @@ abstract class AbstractPostgreSqlPlatformTestCase extends AbstractPlatformTestCa
$this->assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
}
public function testSchemaNeedsCreation()
{
$schemaNames = array(
'default' => false,
'public' => false,
'schema' => true,
);
foreach ($schemaNames as $name => $expected) {
$actual = $this->_platform->schemaNeedsCreation($name);
$this->assertEquals($expected, $actual);
}
}
public function testAlterDecimalPrecisionScale()
{
......
......@@ -472,18 +472,6 @@ abstract class AbstractSQLServerPlatformTestCase extends AbstractPlatformTestCas
$this->assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
}
public function testSchemaNeedsCreation()
{
$schemaNames = array(
'dbo' => false,
'schema' => true,
);
foreach ($schemaNames as $name => $expected) {
$actual = $this->_platform->schemaNeedsCreation($name);
$this->assertEquals($expected, $actual);
}
}
/**
* @group DBAL-543
*/
......
......@@ -110,30 +110,6 @@ class SQLAnywherePlatformTest extends AbstractPlatformTestCase
);
}
public function testGetCreateSchemaSQL()
{
$schemaName = 'schema';
$sql = $this->_platform->getCreateSchemaSQL($schemaName);
$this->assertEquals('CREATE SCHEMA AUTHORIZATION ' . $schemaName, $sql);
}
public function testReturnsDefaultSchemaName()
{
$this->assertSame('DBA', $this->_platform->getDefaultSchemaName());
}
public function testSchemaNeedsCreation()
{
$schemaNames = array(
'DBA' => false,
'schema' => true,
);
foreach ($schemaNames as $name => $expected) {
$actual = $this->_platform->schemaNeedsCreation($name);
$this->assertEquals($expected, $actual);
}
}
public function testHasCorrectPlatformName()
{
$this->assertEquals('sqlanywhere', $this->_platform->getName());
......@@ -724,7 +700,7 @@ class SQLAnywherePlatformTest extends AbstractPlatformTestCase
public function testSupportsSchemas()
{
$this->assertTrue($this->_platform->supportsSchemas());
$this->assertFalse($this->_platform->supportsSchemas());
}
public function testSupportsIndexes()
......
......@@ -806,6 +806,33 @@ class ComparatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema));
}
/**
* @group DBAL-669
*/
public function testNamespacesComparison()
{
$config = new SchemaConfig();
$config->setName("schemaName");
$oldSchema = new Schema(array(), array(), $config);
$oldSchema->createTable('taz');
$oldSchema->createTable('war.tab');
$newSchema= new Schema(array(), array(), $config);
$newSchema->createTable('bar.tab');
$newSchema->createTable('baz.tab');
$newSchema->createTable('war.tab');
$expected = new SchemaDiff();
$expected->fromSchema = $oldSchema;
$expected->newNamespaces = array('bar' => 'bar', 'baz' => 'baz');
$diff = Comparator::compareSchemas($oldSchema, $newSchema);
$this->assertEquals(array('bar' => 'bar', 'baz' => 'baz'), $diff->newNamespaces);
$this->assertCount(2, $diff->newTables);
}
/**
* @group DBAL-204
*/
......@@ -1035,4 +1062,49 @@ class ComparatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(), $comparator->diffColumn($column1, $column2));
$this->assertEquals(array(), $comparator->diffColumn($column2, $column1));
}
/**
* @group DBAL-669
*/
public function testComparesNamespaces()
{
$comparator = new Comparator();
$fromSchema = $this->getMock('Doctrine\DBAL\Schema\Schema', array('getNamespaces', 'hasNamespace'));
$toSchema = $this->getMock('Doctrine\DBAL\Schema\Schema', array('getNamespaces', 'hasNamespace'));
$fromSchema->expects($this->once())
->method('getNamespaces')
->will($this->returnValue(array('foo', 'bar')));
$fromSchema->expects($this->at(0))
->method('hasNamespace')
->with('bar')
->will($this->returnValue(true));
$fromSchema->expects($this->at(1))
->method('hasNamespace')
->with('baz')
->will($this->returnValue(false));
$toSchema->expects($this->once())
->method('getNamespaces')
->will($this->returnValue(array('bar', 'baz')));
$toSchema->expects($this->at(1))
->method('hasNamespace')
->with('foo')
->will($this->returnValue(false));
$toSchema->expects($this->at(2))
->method('hasNamespace')
->with('bar')
->will($this->returnValue(true));
$expected = new SchemaDiff();
$expected->fromSchema = $fromSchema;
$expected->newNamespaces = array('baz' => 'baz');
$expected->removedNamespaces = array('foo' => 'foo');
$this->assertEquals($expected, $comparator->compare($fromSchema, $toSchema));
}
}
......@@ -18,7 +18,7 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
$sql = $diff->toSql($platform);
$expected = array('drop_orphan_fk', 'alter_seq', 'drop_seq', 'create_seq', 'create_table', 'create_foreign_key', 'drop_table', 'alter_table');
$expected = array('create_schema', 'drop_orphan_fk', 'alter_seq', 'drop_seq', 'create_seq', 'create_table', 'create_foreign_key', 'drop_table', 'alter_table');
$this->assertEquals($expected, $sql);
}
......@@ -30,7 +30,7 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
$sql = $diff->toSaveSql($platform);
$expected = array('alter_seq', 'create_seq', 'create_table', 'create_foreign_key', 'alter_table');
$expected = array('create_schema', 'alter_seq', 'create_seq', 'create_table', 'create_foreign_key', 'alter_table');
$this->assertEquals($expected, $sql);
}
......@@ -38,6 +38,10 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
public function createPlatform($unsafe = false)
{
$platform = $this->getMock('Doctrine\Tests\DBAL\Mocks\MockPlatform');
$platform->expects($this->exactly(1))
->method('getCreateSchemaSQL')
->with('foo_ns')
->will($this->returnValue('create_schema'));
if ($unsafe) {
$platform->expects($this->exactly(1))
->method('getDropSequenceSql')
......@@ -76,6 +80,9 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
->with($this->isInstanceof('Doctrine\DBAL\Schema\ForeignKeyConstraint'), $this->equalTo('local_table'))
->will($this->returnValue('drop_orphan_fk'));
}
$platform->expects($this->exactly(1))
->method('supportsSchemas')
->will($this->returnValue(true));
$platform->expects($this->exactly(1))
->method('supportsSequences')
->will($this->returnValue(true));
......@@ -88,6 +95,8 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
public function createSchemaDiff()
{
$diff = new SchemaDiff();
$diff->newNamespaces['foo_ns'] = 'foo_ns';
$diff->removedNamespaces['bar_ns'] = 'bar_ns';
$diff->changedSequences['foo_seq'] = new Sequence('foo_seq');
$diff->newSequences['bar_seq'] = new Sequence('bar_seq');
$diff->removedSequences['baz_seq'] = new Sequence('baz_seq');
......
......@@ -221,4 +221,127 @@ class SchemaTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($schema->hasTable('`foo`'));
}
/**
* @group DBAL-669
*/
public function testHasNamespace()
{
$schema = new Schema();
$this->assertFalse($schema->hasNamespace('foo'));
$schema->createTable('foo');
$this->assertFalse($schema->hasNamespace('foo'));
$schema->createTable('bar.baz');
$this->assertFalse($schema->hasNamespace('baz'));
$this->assertTrue($schema->hasNamespace('bar'));
$this->assertFalse($schema->hasNamespace('tab'));
$schema->createTable('tab.taz');
$this->assertTrue($schema->hasNamespace('tab'));
}
/**
* @group DBAL-669
*/
public function testCreatesNamespace()
{
$schema = new Schema();
$this->assertFalse($schema->hasNamespace('foo'));
$schema->createNamespace('foo');
$this->assertTrue($schema->hasNamespace('foo'));
$this->assertTrue($schema->hasNamespace('FOO'));
$this->assertTrue($schema->hasNamespace('`foo`'));
$this->assertTrue($schema->hasNamespace('`FOO`'));
$schema->createNamespace('`bar`');
$this->assertTrue($schema->hasNamespace('bar'));
$this->assertTrue($schema->hasNamespace('BAR'));
$this->assertTrue($schema->hasNamespace('`bar`'));
$this->assertTrue($schema->hasNamespace('`BAR`'));
$this->assertSame(array('foo' => 'foo', 'bar' => '`bar`'), $schema->getNamespaces());
}
/**
* @group DBAL-669
*
* @expectedException \Doctrine\DBAL\Schema\SchemaException
*/
public function testThrowsExceptionOnCreatingNamespaceTwice()
{
$schema = new Schema();
$schema->createNamespace('foo');
$schema->createNamespace('foo');
}
/**
* @group DBAL-669
*/
public function testCreatesNamespaceThroughAddingTableImplicitly()
{
$schema = new Schema();
$this->assertFalse($schema->hasNamespace('foo'));
$schema->createTable('baz');
$this->assertFalse($schema->hasNamespace('foo'));
$this->assertFalse($schema->hasNamespace('baz'));
$schema->createTable('foo.bar');
$this->assertTrue($schema->hasNamespace('foo'));
$this->assertFalse($schema->hasNamespace('bar'));
$schema->createTable('`baz`.bloo');
$this->assertTrue($schema->hasNamespace('baz'));
$this->assertFalse($schema->hasNamespace('bloo'));
$schema->createTable('`baz`.moo');
$this->assertTrue($schema->hasNamespace('baz'));
$this->assertFalse($schema->hasNamespace('moo'));
}
/**
* @group DBAL-669
*/
public function testCreatesNamespaceThroughAddingSequenceImplicitly()
{
$schema = new Schema();
$this->assertFalse($schema->hasNamespace('foo'));
$schema->createSequence('baz');
$this->assertFalse($schema->hasNamespace('foo'));
$this->assertFalse($schema->hasNamespace('baz'));
$schema->createSequence('foo.bar');
$this->assertTrue($schema->hasNamespace('foo'));
$this->assertFalse($schema->hasNamespace('bar'));
$schema->createSequence('`baz`.bloo');
$this->assertTrue($schema->hasNamespace('baz'));
$this->assertFalse($schema->hasNamespace('bloo'));
$schema->createSequence('`baz`.moo');
$this->assertTrue($schema->hasNamespace('baz'));
$this->assertFalse($schema->hasNamespace('moo'));
}
}
......@@ -2,36 +2,158 @@
namespace Doctrine\Tests\DBAL\Schema\Visitor;
use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector;
use \Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector;
class CreateSchemaSqlCollectorTest extends \PHPUnit_Framework_TestCase
{
public function testGetQueriesForPsql()
/**
* @var \Doctrine\DBAL\Platforms\AbstractPlatform|\PHPUnit_Framework_MockObject_MockObject
*/
private $platformMock;
/**
* @var \Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector
*/
private $visitor;
/**
* {@inheritdoc}
*/
protected function setUp()
{
parent::setUp();
$this->platformMock = $this->getMockBuilder('Doctrine\DBAL\Platforms\AbstractPlatform')
->setMethods(
array(
'getCreateForeignKeySQL',
'getCreateSchemaSQL',
'getCreateSequenceSQL',
'getCreateTableSQL',
'supportsForeignKeyConstraints',
'supportsSchemas'
)
)
->getMockForAbstractClass();
$this->visitor = new CreateSchemaSqlCollector($this->platformMock);
foreach (array('getCreateSchemaSQL', 'getCreateTableSQL', 'getCreateForeignKeySQL', 'getCreateSequenceSQL') as $method) {
$this->platformMock->expects($this->any())
->method($method)
->will($this->returnValue('foo'));
}
}
public function testAcceptsNamespace()
{
$this->platformMock->expects($this->at(0))
->method('supportsSchemas')
->will($this->returnValue(false));
$this->platformMock->expects($this->at(1))
->method('supportsSchemas')
->will($this->returnValue(true));
$this->visitor->acceptNamespace('foo');
$this->assertEmpty($this->visitor->getQueries());
$this->visitor->acceptNamespace('foo');
$this->assertSame(array('foo'), $this->visitor->getQueries());
}
public function testAcceptsTable()
{
$platformMock = $this->getMock(
'Doctrine\DBAL\Platforms\PostgreSqlPlatform',
array('supportsSchemas', 'schemaNeedsCreation', 'getCreateTableSQL')
);
$table = $this->createTableMock();
$platformMock->expects($this->any())
->method('supportsSchemas')
->will($this->returnValue(true));
$this->visitor->acceptTable($table);
$platformMock->expects($this->any())
->method('schemaNeedsCreation')
->will($this->returnValue(true));
$this->assertSame(array('foo'), $this->visitor->getQueries());
}
$platformMock->expects($this->any())
->method('getCreateTableSQL')
->will($this->returnValue(array('foo')));
public function testAcceptsForeignKey()
{
$this->platformMock->expects($this->at(0))
->method('supportsForeignKeyConstraints')
->will($this->returnValue(false));
$tableMock = $this->getMockBuilder('\Doctrine\DBAL\Schema\Table')
->disableOriginalConstructor()
->getMock();
$this->platformMock->expects($this->at(1))
->method('supportsForeignKeyConstraints')
->will($this->returnValue(true));
$sqlCollector = new CreateSchemaSqlCollector($platformMock);
$sqlCollector->acceptTable($tableMock);
$sql = $sqlCollector->getQueries();
$this->assertEquals('CREATE SCHEMA public', $sql[0]);
$table = $this->createTableMock();
$foreignKey = $this->createForeignKeyConstraintMock();
$this->visitor->acceptForeignKey($table, $foreignKey);
$this->assertEmpty($this->visitor->getQueries());
$this->visitor->acceptForeignKey($table, $foreignKey);
$this->assertSame(array('foo'), $this->visitor->getQueries());
}
public function testAcceptsSequences()
{
$sequence = $this->createSequenceMock();
$this->visitor->acceptSequence($sequence);
$this->assertSame(array('foo'), $this->visitor->getQueries());
}
public function testResetsQueries()
{
foreach (array('supportsSchemas', 'supportsForeignKeys') as $method) {
$this->platformMock->expects($this->any())
->method($method)
->will($this->returnValue(true));
}
$table = $this->createTableMock();
$foreignKey = $this->createForeignKeyConstraintMock();
$sequence = $this->createSequenceMock();
$this->visitor->acceptNamespace('foo');
$this->visitor->acceptTable($table);
$this->visitor->acceptForeignKey($table, $foreignKey);
$this->visitor->acceptSequence($sequence);
$this->assertNotEmpty($this->visitor->getQueries());
$this->visitor->resetQueries();
$this->assertEmpty($this->visitor->getQueries());
}
/**
* @return \Doctrine\DBAL\Schema\ForeignKeyConstraint|\PHPUnit_Framework_MockObject_MockObject
*/
private function createForeignKeyConstraintMock()
{
return $this->getMockBuilder('Doctrine\DBAL\Schema\ForeignKeyConstraint')
->disableOriginalConstructor()
->getMock();
}
/**
* @return \Doctrine\DBAL\Schema\Sequence|\PHPUnit_Framework_MockObject_MockObject
*/
private function createSequenceMock()
{
return $this->getMockBuilder('Doctrine\DBAL\Schema\Sequence')
->disableOriginalConstructor()
->getMock();
}
/**
* @return \Doctrine\DBAL\Schema\Table|\PHPUnit_Framework_MockObject_MockObject
*/
private function createTableMock()
{
return $this->getMockBuilder('Doctrine\DBAL\Schema\Table')
->disableOriginalConstructor()
->getMock();
}
}
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