1
0
mirror of synced 2024-12-13 06:46:03 +03:00

[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:
beberlei 2010-02-07 12:36:30 +00:00
parent 1d7946b7d1
commit ac4c33c371
19 changed files with 267 additions and 43 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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'";

View File

@ -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;
}
}

View File

@ -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':'';
}
}

View File

@ -456,4 +456,12 @@ class SqlitePlatform extends AbstractPlatform
{
return 'sqlite';
}
/**
* @inheritdoc
*/
public function getTruncateTableSql($tableName, $cascade = false)
{
return 'DELETE FROM '.$tableName;
}
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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'],
)
);
}
}

View File

@ -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'],
);
}

View File

@ -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) {

View File

@ -81,7 +81,7 @@ class CreateSchemaSqlCollector implements Visitor
);
}
public function acceptColunn(Table $table, Column $column)
public function acceptColumn(Table $table, Column $column)
{
}

View File

@ -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)
{
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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());

View File

@ -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

View File

@ -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;
}
}

View 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));
}
}