[2.0] DDC-214, DDC-303, DDC-304 - Fix several errors with Schema Inference from Database and Metadata and Comparisons, aswell as related bugs in DatabaseDriver. DDC-305 - Abstracted TRUNCATE command for all platforms.
This commit is contained in:
parent
1d7946b7d1
commit
ac4c33c371
@ -810,12 +810,12 @@ abstract class AbstractPlatform
|
||||
|
||||
$sql = array();
|
||||
if ($this->supportsForeignKeyConstraints()) {
|
||||
foreach ($diff->addedForeignKeys AS $foreignKey) {
|
||||
$sql[] = $this->getCreateForeignKeySql($foreignKey, $tableName);
|
||||
}
|
||||
foreach ($diff->removedForeignKeys AS $foreignKey) {
|
||||
$sql[] = $this->getDropForeignKeySql($foreignKey, $tableName);
|
||||
}
|
||||
foreach ($diff->addedForeignKeys AS $foreignKey) {
|
||||
$sql[] = $this->getCreateForeignKeySql($foreignKey, $tableName);
|
||||
}
|
||||
foreach ($diff->changedForeignKeys AS $foreignKey) {
|
||||
$sql[] = $this->getDropForeignKeySql($foreignKey, $tableName);
|
||||
$sql[] = $this->getCreateForeignKeySql($foreignKey, $tableName);
|
||||
@ -1831,4 +1831,19 @@ abstract class AbstractPlatform
|
||||
{
|
||||
return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Truncate Table SQL statement for a given table.
|
||||
*
|
||||
* Cascade is not supported on many platforms but would optionally cascade the truncate by
|
||||
* following the foreign keys.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param bool $cascade
|
||||
* @return string
|
||||
*/
|
||||
public function getTruncateTableSql($tableName, $cascade = false)
|
||||
{
|
||||
return 'TRUNCATE '.$tableName;
|
||||
}
|
||||
}
|
||||
|
@ -505,4 +505,12 @@ class MsSqlPlatform extends AbstractPlatform
|
||||
{
|
||||
return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTruncateTableSql($tableName, $cascade = false)
|
||||
{
|
||||
return 'TRUNCATE TABLE '.$tableName;
|
||||
}
|
||||
}
|
||||
|
@ -203,8 +203,12 @@ class MySqlPlatform extends AbstractPlatform
|
||||
|
||||
public function getListTableForeignKeysSql($table, $database = null)
|
||||
{
|
||||
$sql = "SELECT `CONSTRAINT_NAME`, `COLUMN_NAME`, `REFERENCED_TABLE_NAME`, ".
|
||||
"`REFERENCED_COLUMN_NAME` FROM information_schema.key_column_usage WHERE table_name = '" . $table . "'";
|
||||
$sql = "SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ".
|
||||
"k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ".
|
||||
"FROM information_schema.key_column_usage k /*!50116 ".
|
||||
"INNER JOIN information_schema.referential_constraints c ON k.`CONSTRAINT_NAME` = c.constraint_name AND ".
|
||||
" c.constraint_name = k.constraint_name AND ".
|
||||
" c.table_name = k.table_name */ WHERE k.table_name = '$table'";
|
||||
|
||||
if ( ! is_null($database)) {
|
||||
$sql .= " AND table_schema = '$database'";
|
||||
|
@ -418,18 +418,27 @@ END;';
|
||||
{
|
||||
$table = strtoupper($table);
|
||||
|
||||
return "SELECT rel.constraint_name, rel.position, col.column_name AS local_column, ".
|
||||
" rel.table_name, rel.column_name AS foreign_column, cc.delete_rule ".
|
||||
"FROM (user_tab_columns col ".
|
||||
"JOIN user_cons_columns con ".
|
||||
" ON col.table_name = con.table_name ".
|
||||
" AND col.column_name = con.column_name ".
|
||||
"JOIN user_constraints cc ".
|
||||
" ON con.constraint_name = cc.constraint_name ".
|
||||
"JOIN user_cons_columns rel ".
|
||||
" ON cc.r_constraint_name = rel.constraint_name ".
|
||||
" AND con.position = rel.position) ".
|
||||
"WHERE cc.constraint_type = 'R' AND col.table_name = '".$table."'";
|
||||
return "SELECT alc.constraint_name,
|
||||
alc.DELETE_RULE,
|
||||
alc.search_condition,
|
||||
cols.column_name \"local_column\",
|
||||
cols.position,
|
||||
r_alc.table_name \"references_table\",
|
||||
r_cols.column_name \"foreign_column\"
|
||||
FROM all_cons_columns cols
|
||||
LEFT JOIN all_constraints alc
|
||||
ON alc.constraint_name = cols.constraint_name
|
||||
AND alc.owner = cols.owner
|
||||
LEFT JOIN all_constraints r_alc
|
||||
ON alc.r_constraint_name = r_alc.constraint_name
|
||||
AND alc.r_owner = r_alc.owner
|
||||
LEFT JOIN all_cons_columns r_cols
|
||||
ON r_alc.constraint_name = r_cols.constraint_name
|
||||
AND r_alc.owner = r_cols.owner
|
||||
AND cols.position = r_cols.position
|
||||
WHERE alc.constraint_name = cols.constraint_name
|
||||
AND alc.constraint_type = 'R'
|
||||
AND alc.table_name = '".$table."'";
|
||||
}
|
||||
|
||||
public function getListTableConstraintsSql($table)
|
||||
@ -458,6 +467,24 @@ END;';
|
||||
return 'DROP SEQUENCE ' . $sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ForeignKeyConstraint|string $foreignKey
|
||||
* @param Table|string $table
|
||||
* @return string
|
||||
*/
|
||||
public function getDropForeignKeySql($foreignKey, $table)
|
||||
{
|
||||
if ($foreignKey instanceof \Doctrine\DBAL\Schema\ForeignKeyConstraint) {
|
||||
$foreignKey = $foreignKey->getName();
|
||||
}
|
||||
|
||||
if ($table instanceof \Doctrine\DBAL\Schema\Table) {
|
||||
$table = $table->getName();
|
||||
}
|
||||
|
||||
return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
|
||||
}
|
||||
|
||||
public function getDropDatabaseSql($database)
|
||||
{
|
||||
return 'DROP USER ' . $database . ' CASCADE';
|
||||
@ -627,4 +654,12 @@ END;';
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTruncateTableSql($tableName, $cascade = false)
|
||||
{
|
||||
return 'TRUNCATE TABLE '.$tableName;
|
||||
}
|
||||
}
|
||||
|
@ -784,4 +784,12 @@ class PostgreSqlPlatform extends AbstractPlatform
|
||||
{
|
||||
return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTruncateTableSql($tableName, $cascade = false)
|
||||
{
|
||||
return 'TRUNCATE '.$tableName.' '.($cascade)?'CASCADE':'';
|
||||
}
|
||||
}
|
||||
|
@ -456,4 +456,12 @@ class SqlitePlatform extends AbstractPlatform
|
||||
{
|
||||
return 'sqlite';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTruncateTableSql($tableName, $cascade = false)
|
||||
{
|
||||
return 'DELETE FROM '.$tableName;
|
||||
}
|
||||
}
|
||||
|
@ -238,6 +238,20 @@ abstract class AbstractSchemaManager
|
||||
return $this->_getPortableTableIndexesList($tableIndexes, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all tables in the current database
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listTableNames()
|
||||
{
|
||||
$sql = $this->_platform->getListTablesSql();
|
||||
|
||||
$tables = $this->_conn->fetchAll($sql);
|
||||
|
||||
return $this->_getPortableTablesList($tables);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the tables for this connection
|
||||
*
|
||||
@ -245,13 +259,9 @@ abstract class AbstractSchemaManager
|
||||
*/
|
||||
public function listTables()
|
||||
{
|
||||
$sql = $this->_platform->getListTablesSql();
|
||||
$tableNames = $this->listTableNames();
|
||||
|
||||
$tables = $this->_conn->fetchAll($sql);
|
||||
|
||||
$tableNames = $this->_getPortableTablesList($tables);
|
||||
$tables = array();
|
||||
|
||||
foreach ($tableNames AS $tableName) {
|
||||
$columns = $this->listTableColumns($tableName);
|
||||
$foreignKeys = array();
|
||||
|
@ -272,11 +272,11 @@ class Comparator
|
||||
*/
|
||||
public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2)
|
||||
{
|
||||
if ($key1->getLocalColumns() != $key2->getLocalColumns()) {
|
||||
if (array_map('strtolower', $key1->getLocalColumns()) != array_map('strtolower', $key2->getLocalColumns())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($key1->getForeignColumns() != $key2->getForeignColumns()) {
|
||||
if (array_map('strtolower', $key1->getForeignColumns()) != array_map('strtolower', $key2->getForeignColumns())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -266,12 +266,22 @@ class MySqlSchemaManager extends AbstractSchemaManager
|
||||
{
|
||||
$tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER);
|
||||
|
||||
if ($tableForeignKey['delete_rule'] == "RESTRICT") {
|
||||
$tableForeignKey['delete_rule'] = null;
|
||||
}
|
||||
if ($tableForeignKey['update_rule'] == "RESTRICT") {
|
||||
$tableForeignKey['update_rule'] = null;
|
||||
}
|
||||
|
||||
return new ForeignKeyConstraint(
|
||||
(array)$tableForeignKey['column_name'],
|
||||
$tableForeignKey['referenced_table_name'],
|
||||
(array)$tableForeignKey['referenced_column_name'],
|
||||
$tableForeignKey['constraint_name'],
|
||||
array()
|
||||
array(
|
||||
'onUpdate' => $tableForeignKey['update_rule'],
|
||||
'onDelete' => $tableForeignKey['delete_rule'],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -22,11 +22,12 @@
|
||||
namespace Doctrine\DBAL\Schema;
|
||||
|
||||
/**
|
||||
* xxx
|
||||
* Oracle Schema Manager
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @version $Revision$
|
||||
* @since 2.0
|
||||
*/
|
||||
@ -198,11 +199,15 @@ class OracleSchemaManager extends AbstractSchemaManager
|
||||
foreach ($tableForeignKeys as $key => $value) {
|
||||
$value = \array_change_key_case($value, CASE_LOWER);
|
||||
if (!isset($list[$value['constraint_name']])) {
|
||||
if ($value['delete_rule'] == "NO ACTION") {
|
||||
$value['delete_rule'] = null;
|
||||
}
|
||||
|
||||
$list[$value['constraint_name']] = array(
|
||||
'name' => $value['constraint_name'],
|
||||
'local' => array(),
|
||||
'foreign' => array(),
|
||||
'foreignTable' => $value['table_name'],
|
||||
'foreignTable' => $value['references_table'],
|
||||
'onDelete' => $value['delete_rule'],
|
||||
);
|
||||
}
|
||||
|
@ -201,6 +201,30 @@ class Table extends AbstractAsset
|
||||
return $this->_createIndex($columnNames, $indexName, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an index begins in the order of the given columns.
|
||||
*
|
||||
* @param array $columnsNames
|
||||
* @return bool
|
||||
*/
|
||||
public function columnsAreIndexed(array $columnsNames)
|
||||
{
|
||||
foreach ($this->getIndexes() AS $index) {
|
||||
$indexColumns = $index->getColumns();
|
||||
$areIndexed = true;
|
||||
for ($i = 0; $i < count($columnsNames); $i++) {
|
||||
if ($columnsNames[$i] != $indexColumns[$i]) {
|
||||
$areIndexed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($areIndexed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $columnNames
|
||||
@ -585,7 +609,7 @@ class Table extends AbstractAsset
|
||||
$visitor->acceptTable($this);
|
||||
|
||||
foreach ($this->getColumns() AS $column) {
|
||||
$visitor->acceptColunn($this, $column);
|
||||
$visitor->acceptColumn($this, $column);
|
||||
}
|
||||
|
||||
foreach ($this->getIndexes() AS $index) {
|
||||
|
@ -81,7 +81,7 @@ class CreateSchemaSqlCollector implements Visitor
|
||||
);
|
||||
}
|
||||
|
||||
public function acceptColunn(Table $table, Column $column)
|
||||
public function acceptColumn(Table $table, Column $column)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ class DropSchemaSqlCollector implements Visitor
|
||||
/**
|
||||
* @param Column $column
|
||||
*/
|
||||
public function acceptColunn(Table $table, Column $column)
|
||||
public function acceptColumn(Table $table, Column $column)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class FixSchema implements Visitor
|
||||
/**
|
||||
* @param Column $column
|
||||
*/
|
||||
public function acceptColunn(Table $table, Column $column)
|
||||
public function acceptColumn(Table $table, Column $column)
|
||||
{
|
||||
|
||||
}
|
||||
@ -54,7 +54,12 @@ class FixSchema implements Visitor
|
||||
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint)
|
||||
{
|
||||
if ($this->_addExplicitIndexForForeignKey) {
|
||||
$localTable->addIndex($fkConstraint->getColumns());
|
||||
$columns = $fkConstraint->getColumns();
|
||||
if ($localTable->columnsAreIndexed($columns)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$localTable->addIndex($columns);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ interface Visitor
|
||||
/**
|
||||
* @param Column $column
|
||||
*/
|
||||
public function acceptColunn(Table $table, Column $column);
|
||||
public function acceptColumn(Table $table, Column $column);
|
||||
|
||||
/**
|
||||
* @param Table $localTable
|
||||
|
@ -90,7 +90,7 @@ class DatabaseDriver implements Driver
|
||||
$fieldMapping['id'] = true;
|
||||
}
|
||||
|
||||
$fieldMapping['fieldName'] = Inflector::camelize($column->getName());
|
||||
$fieldMapping['fieldName'] = Inflector::camelize(strtolower($column->getName()));
|
||||
$fieldMapping['columnName'] = $column->getName();
|
||||
$fieldMapping['type'] = strtolower((string) $column->getType());
|
||||
|
||||
|
@ -109,8 +109,12 @@ class SchemaTool
|
||||
{
|
||||
$processedClasses = array(); // Reminder for processed classes, used for hierarchies
|
||||
|
||||
$metadataSchemaConfig = new \Doctrine\DBAL\Schema\SchemaConfig();
|
||||
$metadataSchemaConfig->setExplicitForeignKeyIndexes(false);
|
||||
$metadataSchemaConfig->setMaxIdentifierLength(63);
|
||||
|
||||
$sm = $this->_em->getConnection()->getSchemaManager();
|
||||
$schema = new \Doctrine\DBAL\Schema\Schema(array(), array(), $sm->createSchemaConfig());
|
||||
$schema = new \Doctrine\DBAL\Schema\Schema(array(), array(), $metadataSchemaConfig);
|
||||
|
||||
foreach ($classes as $class) {
|
||||
if (isset($processedClasses[$class->name]) || $class->isMappedSuperclass) {
|
||||
@ -235,6 +239,11 @@ class SchemaTool
|
||||
{
|
||||
$discrColumn = $class->discriminatorColumn;
|
||||
|
||||
if (!isset($discrColumn['type']) || (strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null)) {
|
||||
$discrColumn['type'] = 'string';
|
||||
$discrColumn['length'] = 255;
|
||||
}
|
||||
|
||||
$table->createColumn(
|
||||
$class->getQuotedDiscriminatorColumnName($this->_platform),
|
||||
$discrColumn['type'],
|
||||
@ -329,11 +338,8 @@ class SchemaTool
|
||||
* This includes the SQL for foreign key constraints and join tables.
|
||||
*
|
||||
* @param ClassMetadata $class
|
||||
* @param array $sql The sequence of SQL statements where any new statements should be appended.
|
||||
* @param array $columns The list of columns in the class's primary table where any additional
|
||||
* columns required by relations should be appended.
|
||||
* @param array $constraints The constraints of the table where any additional constraints
|
||||
* required by relations should be appended.
|
||||
* @param \Doctrine\DBAL\Schema\Table $table
|
||||
* @param \Doctrine\DBAL\Schema\Schema $schema
|
||||
* @return void
|
||||
*/
|
||||
private function _gatherRelationsSql($class, $table, $schema)
|
||||
@ -423,6 +429,9 @@ class SchemaTool
|
||||
$columnDef = $fieldMapping['columnDefinition'];
|
||||
}
|
||||
$columnOptions = array('notnull' => false, 'columnDefinition' => $columnDef);
|
||||
if (isset($joinColumn['nullable'])) {
|
||||
$columnOptions['notnull'] = !$joinColumn['nullable'];
|
||||
}
|
||||
|
||||
$theJoinTable->createColumn(
|
||||
$columnName, $class->getTypeOfColumn($joinColumn['referencedColumnName']), $columnOptions
|
||||
|
@ -22,6 +22,10 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
public function testCreateSimpleYamlFromDatabase()
|
||||
{
|
||||
if (!$this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
||||
$this->markTestSkipped('Platform does not support foreign keys.');
|
||||
}
|
||||
|
||||
$table = new \Doctrine\DBAL\Schema\Table("dbdriver_foo");
|
||||
$table->createColumn('id', 'integer');
|
||||
$table->setPrimaryKey(array('id'));
|
||||
@ -92,7 +96,7 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$output = false;
|
||||
|
||||
foreach ($metadatas AS $metadata) {
|
||||
if ($metadata->name == $className) {
|
||||
if (strtolower($metadata->name) == strtolower($className)) {
|
||||
return $metadata;
|
||||
}
|
||||
}
|
||||
|
79
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC214Test.php
Normal file
79
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC214Test.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\Tools;
|
||||
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$conn = $this->_em->getConnection();
|
||||
|
||||
if (strpos($conn->getDriver()->getName(), "sqlite") !== false) {
|
||||
$this->markTestSkipped('SQLite does not support ALTER TABLE statements.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-214
|
||||
*/
|
||||
public function testCmsAddressModel()
|
||||
{
|
||||
$classes = array(
|
||||
'Doctrine\Tests\Models\CMS\CmsUser',
|
||||
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
|
||||
'Doctrine\Tests\Models\CMS\CmsAddress',
|
||||
'Doctrine\Tests\Models\CMS\CmsGroup',
|
||||
'Doctrine\Tests\Models\CMS\CmsArticle'
|
||||
);
|
||||
|
||||
$this->assertCreatedSchemaNeedsNoUpdates($classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-214
|
||||
*/
|
||||
public function testCompanyModel()
|
||||
{
|
||||
$classes = array(
|
||||
'Doctrine\Tests\Models\Company\CompanyPerson',
|
||||
'Doctrine\Tests\Models\Company\CompanyEmployee',
|
||||
'Doctrine\Tests\Models\Company\CompanyManager',
|
||||
'Doctrine\Tests\Models\Company\CompanyOrganization',
|
||||
'Doctrine\Tests\Models\Company\CompanyEvent',
|
||||
'Doctrine\Tests\Models\Company\CompanyAuction',
|
||||
'Doctrine\Tests\Models\Company\CompanyRaffle',
|
||||
'Doctrine\Tests\Models\Company\CompanyCar'
|
||||
);
|
||||
|
||||
$this->assertCreatedSchemaNeedsNoUpdates($classes);
|
||||
}
|
||||
|
||||
public function assertCreatedSchemaNeedsNoUpdates($classes)
|
||||
{
|
||||
$classMetadata = array();
|
||||
foreach ($classes AS $class) {
|
||||
$classMetadata[] = $this->_em->getClassMetadata($class);
|
||||
}
|
||||
|
||||
$schemaTool = new Tools\SchemaTool($this->_em);
|
||||
$schemaTool->dropSchema($classMetadata);
|
||||
$schemaTool->createSchema($classMetadata);
|
||||
|
||||
$sm = $this->_em->getConnection()->getSchemaManager();
|
||||
|
||||
$fromSchema = $sm->createSchema();
|
||||
$toSchema = $schemaTool->getSchemaFromMetadata($classMetadata);
|
||||
|
||||
$comparator = new \Doctrine\DBAL\Schema\Comparator();
|
||||
$schemaDiff = $comparator->compare($fromSchema, $toSchema);
|
||||
|
||||
$sql = $schemaDiff->toSql($this->_em->getConnection()->getDatabasePlatform());
|
||||
$this->assertEquals(0, count($sql));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user