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 ...@@ -1788,19 +1788,6 @@ abstract class AbstractPlatform
throw DBALException::notSupported(__METHOD__); 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, * 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 * even if it is a reserved word of the platform. This also detects identifier
...@@ -2678,6 +2665,18 @@ abstract class AbstractPlatform ...@@ -2678,6 +2665,18 @@ abstract class AbstractPlatform
throw DBALException::notSupported(__METHOD__); 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 * @param string $database
* *
......
...@@ -222,6 +222,14 @@ class PostgreSqlPlatform extends AbstractPlatform ...@@ -222,6 +222,14 @@ class PostgreSqlPlatform extends AbstractPlatform
return 'SELECT datname FROM pg_database'; 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} * {@inheritDoc}
*/ */
...@@ -660,14 +668,6 @@ class PostgreSqlPlatform extends AbstractPlatform ...@@ -660,14 +668,6 @@ class PostgreSqlPlatform extends AbstractPlatform
return 'CREATE SCHEMA ' . $schemaName; return 'CREATE SCHEMA ' . $schemaName;
} }
/**
* {@inheritDoc}
*/
public function schemaNeedsCreation($schemaName)
{
return !in_array($schemaName, array('default', 'public'));
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
......
...@@ -417,14 +417,6 @@ class SQLAnywherePlatform extends AbstractPlatform ...@@ -417,14 +417,6 @@ class SQLAnywherePlatform extends AbstractPlatform
return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index); return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index);
} }
/**
* {@inheritdoc}
*/
public function getCreateSchemaSQL($schemaName)
{
return 'CREATE SCHEMA AUTHORIZATION ' . $schemaName;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -519,14 +511,6 @@ class SQLAnywherePlatform extends AbstractPlatform ...@@ -519,14 +511,6 @@ class SQLAnywherePlatform extends AbstractPlatform
return 'DATE'; return 'DATE';
} }
/**
* {@inheritdoc}
*/
public function getDefaultSchemaName()
{
return 'DBA';
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -1179,14 +1163,6 @@ class SQLAnywherePlatform extends AbstractPlatform ...@@ -1179,14 +1163,6 @@ class SQLAnywherePlatform extends AbstractPlatform
return true; return true;
} }
/**
* {@inheritdoc}
*/
public function schemaNeedsCreation($schemaName)
{
return $schemaName !== 'DBA';
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -1203,14 +1179,6 @@ class SQLAnywherePlatform extends AbstractPlatform ...@@ -1203,14 +1179,6 @@ class SQLAnywherePlatform extends AbstractPlatform
return true; return true;
} }
/**
* {@inheritdoc}
*/
public function supportsSchemas()
{
return true;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -155,14 +155,6 @@ class SQLServerPlatform extends AbstractPlatform ...@@ -155,14 +155,6 @@ class SQLServerPlatform extends AbstractPlatform
return 'CREATE SCHEMA ' . $schemaName; return 'CREATE SCHEMA ' . $schemaName;
} }
/**
* {@inheritDoc}
*/
public function schemaNeedsCreation($schemaName)
{
return $schemaName !== 'dbo';
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
...@@ -1025,6 +1017,14 @@ class SQLServerPlatform extends AbstractPlatform ...@@ -1025,6 +1017,14 @@ class SQLServerPlatform extends AbstractPlatform
return 'SELECT * FROM SYS.DATABASES'; return 'SELECT * FROM SYS.DATABASES';
} }
/**
* {@inheritDoc}
*/
public function getListNamespacesSQL()
{
return "SELECT name FROM SYS.SCHEMAS WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')";
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
......
...@@ -115,6 +115,20 @@ abstract class AbstractSchemaManager ...@@ -115,6 +115,20 @@ abstract class AbstractSchemaManager
return $this->_getPortableDatabasesList($databases); 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. * Lists the available sequences for this connection.
* *
...@@ -651,6 +665,24 @@ abstract class AbstractSchemaManager ...@@ -651,6 +665,24 @@ abstract class AbstractSchemaManager
return $list; 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 * @param array $database
* *
...@@ -661,6 +693,18 @@ abstract class AbstractSchemaManager ...@@ -661,6 +693,18 @@ abstract class AbstractSchemaManager
return $database; 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 * @param array $functions
* *
...@@ -979,13 +1023,21 @@ abstract class AbstractSchemaManager ...@@ -979,13 +1023,21 @@ abstract class AbstractSchemaManager
*/ */
public function createSchema() public function createSchema()
{ {
$namespaces = array();
if ($this->_platform->supportsSchemas()) {
$namespaces = $this->listNamespaceNames();
}
$sequences = array(); $sequences = array();
if ($this->_platform->supportsSequences()) { if ($this->_platform->supportsSequences()) {
$sequences = $this->listSequences(); $sequences = $this->listSequences();
} }
$tables = $this->listTables(); $tables = $this->listTables();
return new Schema($tables, $sequences, $this->createSchemaConfig()); return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
} }
/** /**
......
...@@ -62,6 +62,18 @@ class Comparator ...@@ -62,6 +62,18 @@ class Comparator
$foreignKeysToTable = array(); $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) { foreach ($toSchema->getTables() as $table) {
$tableName = $table->getShortestName($toSchema->getName()); $tableName = $table->getShortestName($toSchema->getName());
if ( ! $fromSchema->hasTable($tableName)) { if ( ! $fromSchema->hasTable($tableName)) {
...@@ -377,7 +389,7 @@ class Comparator ...@@ -377,7 +389,7 @@ class Comparator
$changedProperties[] = $property; $changedProperties[] = $property;
} }
} }
if ($properties1['default'] != $properties2['default'] || if ($properties1['default'] != $properties2['default'] ||
// Null values need to be checked additionally as they tell whether to create or drop a default value. // 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. // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation.
......
...@@ -283,6 +283,14 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager ...@@ -283,6 +283,14 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
return $list; return $list;
} }
/**
* {@inheritdoc}
*/
protected function getPortableNamespaceDefinition(array $namespace)
{
return $namespace['nspname'];
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -180,6 +180,14 @@ class SQLServerSchemaManager extends AbstractSchemaManager ...@@ -180,6 +180,14 @@ class SQLServerSchemaManager extends AbstractSchemaManager
return $database['name']; return $database['name'];
} }
/**
* {@inheritdoc}
*/
protected function getPortableNamespaceDefinition(array $namespace)
{
return $namespace['name'];
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -54,6 +54,13 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; ...@@ -54,6 +54,13 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
*/ */
class Schema extends AbstractAsset class Schema extends AbstractAsset
{ {
/**
* The namespaces in this schema.
*
* @var array
*/
private $namespaces = array();
/** /**
* @var \Doctrine\DBAL\Schema\Table[] * @var \Doctrine\DBAL\Schema\Table[]
*/ */
...@@ -73,15 +80,24 @@ class Schema extends AbstractAsset ...@@ -73,15 +80,24 @@ class Schema extends AbstractAsset
* @param \Doctrine\DBAL\Schema\Table[] $tables * @param \Doctrine\DBAL\Schema\Table[] $tables
* @param \Doctrine\DBAL\Schema\Sequence[] $sequences * @param \Doctrine\DBAL\Schema\Sequence[] $sequences
* @param \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig * @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) { if ($schemaConfig == null) {
$schemaConfig = new SchemaConfig(); $schemaConfig = new SchemaConfig();
} }
$this->_schemaConfig = $schemaConfig; $this->_schemaConfig = $schemaConfig;
$this->_setName($schemaConfig->getName() ?: 'public'); $this->_setName($schemaConfig->getName() ?: 'public');
foreach ($namespaces as $namespace) {
$this->createNamespace($namespace);
}
foreach ($tables as $table) { foreach ($tables as $table) {
$this->_addTable($table); $this->_addTable($table);
} }
...@@ -108,11 +124,17 @@ class Schema extends AbstractAsset ...@@ -108,11 +124,17 @@ class Schema extends AbstractAsset
*/ */
protected function _addTable(Table $table) protected function _addTable(Table $table)
{ {
$namespaceName = $table->getNamespaceName();
$tableName = $table->getFullQualifiedName($this->getName()); $tableName = $table->getFullQualifiedName($this->getName());
if (isset($this->_tables[$tableName])) { if (isset($this->_tables[$tableName])) {
throw SchemaException::tableAlreadyExists($tableName); throw SchemaException::tableAlreadyExists($tableName);
} }
if ( ! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
$this->createNamespace($namespaceName);
}
$this->_tables[$tableName] = $table; $this->_tables[$tableName] = $table;
$table->setSchemaConfig($this->_schemaConfig); $table->setSchemaConfig($this->_schemaConfig);
} }
...@@ -126,13 +148,30 @@ class Schema extends AbstractAsset ...@@ -126,13 +148,30 @@ class Schema extends AbstractAsset
*/ */
protected function _addSequence(Sequence $sequence) protected function _addSequence(Sequence $sequence)
{ {
$namespaceName = $sequence->getNamespaceName();
$seqName = $sequence->getFullQualifiedName($this->getName()); $seqName = $sequence->getFullQualifiedName($this->getName());
if (isset($this->_sequences[$seqName])) { if (isset($this->_sequences[$seqName])) {
throw SchemaException::sequenceAlreadyExists($seqName); throw SchemaException::sequenceAlreadyExists($seqName);
} }
if ( ! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
$this->createNamespace($namespaceName);
}
$this->_sequences[$seqName] = $sequence; $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. * Gets all tables of this schema.
* *
...@@ -167,9 +206,8 @@ class Schema extends AbstractAsset ...@@ -167,9 +206,8 @@ class Schema extends AbstractAsset
*/ */
private function getFullQualifiedAssetName($name) private function getFullQualifiedAssetName($name)
{ {
if ($this->isIdentifierQuoted($name)) { $name = $this->getUnquotedAssetName($name);
$name = $this->trimQuotes($name);
}
if (strpos($name, ".") === false) { if (strpos($name, ".") === false) {
$name = $this->getName() . "." . $name; $name = $this->getName() . "." . $name;
} }
...@@ -177,6 +215,36 @@ class Schema extends AbstractAsset ...@@ -177,6 +215,36 @@ class Schema extends AbstractAsset
return strtolower($name); 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? * Does this schema have a table with the given name?
* *
...@@ -238,6 +306,26 @@ class Schema extends AbstractAsset ...@@ -238,6 +306,26 @@ class Schema extends AbstractAsset
return $this->_sequences; 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. * Creates a new table.
* *
......
...@@ -37,6 +37,20 @@ class SchemaDiff ...@@ -37,6 +37,20 @@ class SchemaDiff
*/ */
public $fromSchema; public $fromSchema;
/**
* All added namespaces.
*
* @var string[]
*/
public $newNamespaces = array();
/**
* All removed namespaces.
*
* @var string[]
*/
public $removedNamespaces = array();
/** /**
* All added tables. * All added tables.
* *
...@@ -132,6 +146,12 @@ class SchemaDiff ...@@ -132,6 +146,12 @@ class SchemaDiff
{ {
$sql = array(); $sql = array();
if ($platform->supportsSchemas()) {
foreach ($this->newNamespaces as $newNamespace) {
$sql[] = $platform->getCreateSchemaSQL($newNamespace);
}
}
if ($platform->supportsForeignKeyConstraints() && $saveMode == false) { if ($platform->supportsForeignKeyConstraints() && $saveMode == false) {
foreach ($this->orphanedForeignKeys as $orphanedForeignKey) { foreach ($this->orphanedForeignKeys as $orphanedForeignKey) {
$sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTableName()); $sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTableName());
......
...@@ -31,6 +31,7 @@ class SchemaException extends \Doctrine\DBAL\DBALException ...@@ -31,6 +31,7 @@ class SchemaException extends \Doctrine\DBAL\DBALException
const SEQUENCE_ALREADY_EXISTS = 80; const SEQUENCE_ALREADY_EXISTS = 80;
const INDEX_INVALID_NAME = 90; const INDEX_INVALID_NAME = 90;
const FOREIGNKEY_DOESNT_EXIST = 100; const FOREIGNKEY_DOESNT_EXIST = 100;
const NAMESPACE_ALREADY_EXISTS = 110;
/** /**
* @param string $tableName * @param string $tableName
...@@ -85,6 +86,19 @@ class SchemaException extends \Doctrine\DBAL\DBALException ...@@ -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); 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 * @param string $tableName
* *
......
...@@ -29,7 +29,7 @@ use Doctrine\DBAL\Schema\Index; ...@@ -29,7 +29,7 @@ use Doctrine\DBAL\Schema\Index;
/** /**
* Abstract Visitor with empty methods for easy extension. * Abstract Visitor with empty methods for easy extension.
*/ */
class AbstractVisitor implements Visitor class AbstractVisitor implements Visitor, NamespaceVisitor
{ {
/** /**
* @param \Doctrine\DBAL\Schema\Schema $schema * @param \Doctrine\DBAL\Schema\Schema $schema
...@@ -38,6 +38,13 @@ class AbstractVisitor implements Visitor ...@@ -38,6 +38,13 @@ class AbstractVisitor implements Visitor
{ {
} }
/**
* {@inheritdoc}
*/
public function acceptNamespace($namespaceName)
{
}
/** /**
* @param \Doctrine\DBAL\Schema\Table $table * @param \Doctrine\DBAL\Schema\Table $table
*/ */
......
...@@ -26,6 +26,11 @@ use Doctrine\DBAL\Schema\Sequence; ...@@ -26,6 +26,11 @@ use Doctrine\DBAL\Schema\Sequence;
class CreateSchemaSqlCollector extends AbstractVisitor class CreateSchemaSqlCollector extends AbstractVisitor
{ {
/**
* @var array
*/
private $createNamespaceQueries = array();
/** /**
* @var array * @var array
*/ */
...@@ -58,14 +63,22 @@ class CreateSchemaSqlCollector extends AbstractVisitor ...@@ -58,14 +63,22 @@ class CreateSchemaSqlCollector extends AbstractVisitor
/** /**
* {@inheritdoc} * {@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], * {@inheritdoc}
$this->platform->getCreateTableSQL($table) */
); public function acceptTable(Table $table)
{
$this->createTableQueries = array_merge($this->createTableQueries, (array) $this->platform->getCreateTableSQL($table));
} }
/** /**
...@@ -73,11 +86,9 @@ class CreateSchemaSqlCollector extends AbstractVisitor ...@@ -73,11 +86,9 @@ class CreateSchemaSqlCollector extends AbstractVisitor
*/ */
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint)
{ {
$namespace = $this->getNamespace($localTable);
if ($this->platform->supportsForeignKeyConstraints()) { if ($this->platform->supportsForeignKeyConstraints()) {
$this->createFkConstraintQueries[$namespace] = array_merge( $this->createFkConstraintQueries = array_merge(
$this->createFkConstraintQueries[$namespace], $this->createFkConstraintQueries,
(array) $this->platform->getCreateForeignKeySQL( (array) $this->platform->getCreateForeignKeySQL(
$fkConstraint, $localTable $fkConstraint, $localTable
) )
...@@ -90,41 +101,18 @@ class CreateSchemaSqlCollector extends AbstractVisitor ...@@ -90,41 +101,18 @@ class CreateSchemaSqlCollector extends AbstractVisitor
*/ */
public function acceptSequence(Sequence $sequence) public function acceptSequence(Sequence $sequence)
{ {
$namespace = $this->getNamespace($sequence); $this->createSequenceQueries = array_merge(
$this->createSequenceQueries,
$this->createSequenceQueries[$namespace] = array_merge(
$this->createSequenceQueries[$namespace],
(array)$this->platform->getCreateSequenceSQL($sequence) (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 * @return void
*/ */
public function resetQueries() public function resetQueries()
{ {
$this->createNamespaceQueries = array();
$this->createTableQueries = array(); $this->createTableQueries = array();
$this->createSequenceQueries = array(); $this->createSequenceQueries = array();
$this->createFkConstraintQueries = array(); $this->createFkConstraintQueries = array();
...@@ -139,23 +127,20 @@ class CreateSchemaSqlCollector extends AbstractVisitor ...@@ -139,23 +127,20 @@ class CreateSchemaSqlCollector extends AbstractVisitor
{ {
$sql = array(); $sql = array();
foreach (array_keys($this->createTableQueries) as $namespace) { foreach ($this->createNamespaceQueries as $schemaSql) {
if ($this->platform->supportsSchemas() && $this->platform->schemaNeedsCreation($namespace)) { $sql = array_merge($sql, (array) $schemaSql);
$query = $this->platform->getCreateSchemaSQL($namespace);
$sql[] = $query;
}
} }
foreach ($this->createTableQueries as $schemaSql) { foreach ($this->createTableQueries as $schemaSql) {
$sql = array_merge($sql, $schemaSql); $sql = array_merge($sql, (array) $schemaSql);
} }
foreach ($this->createSequenceQueries as $schemaSql) { foreach ($this->createSequenceQueries as $schemaSql) {
$sql = array_merge($sql, $schemaSql); $sql = array_merge($sql, (array) $schemaSql);
} }
foreach ($this->createFkConstraintQueries as $schemaSql) { foreach ($this->createFkConstraintQueries as $schemaSql) {
$sql = array_merge($sql, $schemaSql); $sql = array_merge($sql, (array) $schemaSql);
} }
return $sql; 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 ...@@ -506,14 +506,6 @@ abstract class AbstractPlatformTestCase extends \Doctrine\Tests\DbalTestCase
$this->_platform->getCreateSchemaSQL('schema'); $this->_platform->getCreateSchemaSQL('schema');
} }
/**
* @expectedException \Doctrine\DBAL\DBALException
*/
public function testSchemaNeedsCreation()
{
$this->_platform->schemaNeedsCreation('schema');
}
/** /**
* @group DBAL-585 * @group DBAL-585
*/ */
......
...@@ -387,19 +387,6 @@ abstract class AbstractPostgreSqlPlatformTestCase extends AbstractPlatformTestCa ...@@ -387,19 +387,6 @@ abstract class AbstractPostgreSqlPlatformTestCase extends AbstractPlatformTestCa
$this->assertEquals('CREATE SCHEMA ' . $schemaName, $sql); $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() public function testAlterDecimalPrecisionScale()
{ {
......
...@@ -472,18 +472,6 @@ abstract class AbstractSQLServerPlatformTestCase extends AbstractPlatformTestCas ...@@ -472,18 +472,6 @@ abstract class AbstractSQLServerPlatformTestCase extends AbstractPlatformTestCas
$this->assertEquals('CREATE SCHEMA ' . $schemaName, $sql); $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 * @group DBAL-543
*/ */
......
...@@ -110,30 +110,6 @@ class SQLAnywherePlatformTest extends AbstractPlatformTestCase ...@@ -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() public function testHasCorrectPlatformName()
{ {
$this->assertEquals('sqlanywhere', $this->_platform->getName()); $this->assertEquals('sqlanywhere', $this->_platform->getName());
...@@ -724,7 +700,7 @@ class SQLAnywherePlatformTest extends AbstractPlatformTestCase ...@@ -724,7 +700,7 @@ class SQLAnywherePlatformTest extends AbstractPlatformTestCase
public function testSupportsSchemas() public function testSupportsSchemas()
{ {
$this->assertTrue($this->_platform->supportsSchemas()); $this->assertFalse($this->_platform->supportsSchemas());
} }
public function testSupportsIndexes() public function testSupportsIndexes()
......
...@@ -806,6 +806,33 @@ class ComparatorTest extends \PHPUnit_Framework_TestCase ...@@ -806,6 +806,33 @@ class ComparatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, Comparator::compareSchemas($oldSchema, $newSchema)); $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 * @group DBAL-204
*/ */
...@@ -1035,4 +1062,49 @@ class ComparatorTest extends \PHPUnit_Framework_TestCase ...@@ -1035,4 +1062,49 @@ class ComparatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(), $comparator->diffColumn($column1, $column2)); $this->assertEquals(array(), $comparator->diffColumn($column1, $column2));
$this->assertEquals(array(), $comparator->diffColumn($column2, $column1)); $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 ...@@ -18,7 +18,7 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
$sql = $diff->toSql($platform); $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); $this->assertEquals($expected, $sql);
} }
...@@ -30,7 +30,7 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase ...@@ -30,7 +30,7 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
$sql = $diff->toSaveSql($platform); $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); $this->assertEquals($expected, $sql);
} }
...@@ -38,6 +38,10 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase ...@@ -38,6 +38,10 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
public function createPlatform($unsafe = false) public function createPlatform($unsafe = false)
{ {
$platform = $this->getMock('Doctrine\Tests\DBAL\Mocks\MockPlatform'); $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) { if ($unsafe) {
$platform->expects($this->exactly(1)) $platform->expects($this->exactly(1))
->method('getDropSequenceSql') ->method('getDropSequenceSql')
...@@ -76,6 +80,9 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase ...@@ -76,6 +80,9 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
->with($this->isInstanceof('Doctrine\DBAL\Schema\ForeignKeyConstraint'), $this->equalTo('local_table')) ->with($this->isInstanceof('Doctrine\DBAL\Schema\ForeignKeyConstraint'), $this->equalTo('local_table'))
->will($this->returnValue('drop_orphan_fk')); ->will($this->returnValue('drop_orphan_fk'));
} }
$platform->expects($this->exactly(1))
->method('supportsSchemas')
->will($this->returnValue(true));
$platform->expects($this->exactly(1)) $platform->expects($this->exactly(1))
->method('supportsSequences') ->method('supportsSequences')
->will($this->returnValue(true)); ->will($this->returnValue(true));
...@@ -88,6 +95,8 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase ...@@ -88,6 +95,8 @@ class SchemaDiffTest extends \PHPUnit_Framework_TestCase
public function createSchemaDiff() public function createSchemaDiff()
{ {
$diff = new SchemaDiff(); $diff = new SchemaDiff();
$diff->newNamespaces['foo_ns'] = 'foo_ns';
$diff->removedNamespaces['bar_ns'] = 'bar_ns';
$diff->changedSequences['foo_seq'] = new Sequence('foo_seq'); $diff->changedSequences['foo_seq'] = new Sequence('foo_seq');
$diff->newSequences['bar_seq'] = new Sequence('bar_seq'); $diff->newSequences['bar_seq'] = new Sequence('bar_seq');
$diff->removedSequences['baz_seq'] = new Sequence('baz_seq'); $diff->removedSequences['baz_seq'] = new Sequence('baz_seq');
......
...@@ -221,4 +221,127 @@ class SchemaTest extends \PHPUnit_Framework_TestCase ...@@ -221,4 +221,127 @@ class SchemaTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($schema->hasTable('`foo`')); $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 @@ ...@@ -2,36 +2,158 @@
namespace Doctrine\Tests\DBAL\Schema\Visitor; namespace Doctrine\Tests\DBAL\Schema\Visitor;
use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector; use \Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector;
class CreateSchemaSqlCollectorTest extends \PHPUnit_Framework_TestCase 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( $table = $this->createTableMock();
'Doctrine\DBAL\Platforms\PostgreSqlPlatform',
array('supportsSchemas', 'schemaNeedsCreation', 'getCreateTableSQL')
);
$platformMock->expects($this->any()) $this->visitor->acceptTable($table);
->method('supportsSchemas')
->will($this->returnValue(true));
$platformMock->expects($this->any()) $this->assertSame(array('foo'), $this->visitor->getQueries());
->method('schemaNeedsCreation') }
->will($this->returnValue(true));
$platformMock->expects($this->any()) public function testAcceptsForeignKey()
->method('getCreateTableSQL') {
->will($this->returnValue(array('foo'))); $this->platformMock->expects($this->at(0))
->method('supportsForeignKeyConstraints')
->will($this->returnValue(false));
$tableMock = $this->getMockBuilder('\Doctrine\DBAL\Schema\Table') $this->platformMock->expects($this->at(1))
->disableOriginalConstructor() ->method('supportsForeignKeyConstraints')
->getMock(); ->will($this->returnValue(true));
$sqlCollector = new CreateSchemaSqlCollector($platformMock); $table = $this->createTableMock();
$sqlCollector->acceptTable($tableMock); $foreignKey = $this->createForeignKeyConstraintMock();
$sql = $sqlCollector->getQueries();
$this->assertEquals('CREATE SCHEMA public', $sql[0]); $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