From b0bbe281f37f0507abee6d764eadeea06c9cc4a1 Mon Sep 17 00:00:00 2001 From: beberlei Date: Thu, 3 Dec 2009 22:10:13 +0000 Subject: [PATCH] [2.0] DDC-169 - Introduced handling of schema / database case-sensitivity differences and to allow compability between different platforms. --- lib/Doctrine/DBAL/Schema/AbstractAsset.php | 54 ++++++++++++++++++- .../DBAL/Schema/AbstractSchemaManager.php | 14 ++++- .../DBAL/Schema/ForeignKeyConstraint.php | 8 +-- lib/Doctrine/DBAL/Schema/Index.php | 5 +- lib/Doctrine/DBAL/Schema/Schema.php | 4 ++ lib/Doctrine/DBAL/Schema/SchemaException.php | 5 ++ lib/Doctrine/DBAL/Schema/Table.php | 22 ++++++-- .../Visitor/CreateSchemaSqlCollector.php | 9 ---- .../Schema/Visitor/DropSchemaSqlCollector.php | 9 ---- lib/Doctrine/DBAL/Schema/Visitor/Visitor.php | 6 --- .../SchemaManagerFunctionalTestCase.php | 26 +++++---- 11 files changed, 115 insertions(+), 47 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/AbstractAsset.php b/lib/Doctrine/DBAL/Schema/AbstractAsset.php index 60acfc276..1e2f3da3b 100644 --- a/lib/Doctrine/DBAL/Schema/AbstractAsset.php +++ b/lib/Doctrine/DBAL/Schema/AbstractAsset.php @@ -35,11 +35,20 @@ namespace Doctrine\DBAL\Schema; */ abstract class AbstractAsset { + const CASE_UPPER = "upper"; + const CASE_LOWER = "lower"; + const CASE_KEEP = "keep"; + /** * @var string */ protected $_name; + /** + * @var int + */ + protected $_caseMode = self::CASE_KEEP; + /** * Set name of this asset * @@ -57,7 +66,7 @@ abstract class AbstractAsset */ public function getName() { - return $this->_name; + return $this->_foldIdentifier($this->_name); } /** @@ -82,4 +91,47 @@ abstract class AbstractAsset $parts[] = $postfix; return trim(implode("_", $parts), '_'); } + + /** + * Set the case mode of this asset. + * + * @param string $caseMode + * @return void + */ + public function setCaseMode($caseMode) + { + if (!in_array($caseMode, array(self::CASE_KEEP, self::CASE_LOWER, self::CASE_UPPER))) { + throw SchemaException::invalidCaseModeGiven($caseMode); + } + $this->_caseMode = $caseMode; + } + + /** + * Fold the case of the identifier based on the CASE_* constants. + * + * This has never to be applied on write operation, only on read! This ensures that you can change + * the case at any point. For the keys of arrays however we always store them in lower-case which + * makes it easy to access them. This affects the maps in Schema and Table instances. + * + * @param string $identifier + * @return string + */ + protected function _foldIdentifier($identifier) + { + if ($this->_caseMode == self::CASE_UPPER) { + return strtoupper($identifier); + } else if ($this->_caseMode == self::CASE_LOWER) { + return strtolower($identifier); + } + return $identifier; + } + + /** + * @param array $identifiers + * @return array + */ + protected function _foldIdentifiers($identifiers) + { + return array_map(array($this, '_foldIdentifier'), $identifiers); + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php index 370f2189b..b30e5191f 100644 --- a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -213,6 +213,8 @@ abstract class AbstractSchemaManager /** * List the indexes for a given table returning an array of Index instances. * + * Keys of the portable indexes list are all lower-cased. + * * @param string $table The name of the table * @return Index[] $tableIndexes */ @@ -830,12 +832,21 @@ abstract class AbstractSchemaManager return $tableConstraint; } + /** + * Independent of the database the keys of the column list result are lowercased. + * + * The name of the created column instance however is kept in its case. + * + * @param array $tableColumns + * @return array + */ protected function _getPortableTableColumnList($tableColumns) { $list = array(); foreach ($tableColumns as $key => $column) { if ($column = $this->_getPortableTableColumnDefinition($column)) { - $list[$column->getName()] = $column; + $name = strtolower($column->getName()); + $list[$name] = $column; } } return $list; @@ -864,6 +875,7 @@ abstract class AbstractSchemaManager if($tableIndex['primary']) { $keyName = 'primary'; } + $keyName = strtolower($keyName); if(!isset($result[$keyName])) { $result[$keyName] = array( diff --git a/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php b/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php index 74c1e4d8c..46d25b116 100644 --- a/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php +++ b/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php @@ -72,12 +72,12 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint */ public function getLocalColumns() { - return $this->_localColumnNames; + return $this->_foldIdentifiers($this->_localColumnNames); } public function getColumns() { - return $this->_localColumnNames; + return $this->_foldIdentifiers($this->_localColumnNames); } /** @@ -85,7 +85,7 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint */ public function getForeignTableName() { - return $this->_foreignTableName; + return $this->_foldIdentifier($this->_foreignTableName); } /** @@ -93,7 +93,7 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint */ public function getForeignColumns() { - return $this->_foreignColumnNames; + return $this->_foldIdentifiers($this->_foreignColumnNames); } public function hasOption($name) diff --git a/lib/Doctrine/DBAL/Schema/Index.php b/lib/Doctrine/DBAL/Schema/Index.php index 29b0837f3..68f0aaa14 100644 --- a/lib/Doctrine/DBAL/Schema/Index.php +++ b/lib/Doctrine/DBAL/Schema/Index.php @@ -76,7 +76,7 @@ class Index extends AbstractAsset implements Constraint */ public function getColumns() { - return $this->_columns; + return $this->_foldIdentifiers($this->_columns); } /** @@ -102,6 +102,7 @@ class Index extends AbstractAsset implements Constraint */ public function hasColumnAtPosition($columnName, $pos=0) { - return \array_search($columnName, $this->_columns) === $pos; + $columnName = $this->_foldIdentifier($columnName); + return \array_search($columnName, $this->getColumns()) === $pos; } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Schema/Schema.php b/lib/Doctrine/DBAL/Schema/Schema.php index e1b90a3ff..d7a19403f 100644 --- a/lib/Doctrine/DBAL/Schema/Schema.php +++ b/lib/Doctrine/DBAL/Schema/Schema.php @@ -65,6 +65,8 @@ class Schema extends AbstractAsset */ protected function _addTable(Table $table) { + $table->setCaseMode($this->_caseMode); + $tableName = strtolower($table->getName()); if(isset($this->_tables[$tableName])) { throw SchemaException::tableAlreadyExists($tableName); @@ -78,6 +80,8 @@ class Schema extends AbstractAsset */ protected function _addSequence(Sequence $sequence) { + $sequence->setCaseMode($this->_caseMode); + $seqName = strtolower($sequence->getName()); if (isset($this->_sequences[$seqName])) { throw SchemaException::sequenceAlreadyExists($seqName); diff --git a/lib/Doctrine/DBAL/Schema/SchemaException.php b/lib/Doctrine/DBAL/Schema/SchemaException.php index 9c7f062e5..3a76397f6 100644 --- a/lib/Doctrine/DBAL/Schema/SchemaException.php +++ b/lib/Doctrine/DBAL/Schema/SchemaException.php @@ -109,4 +109,9 @@ class SchemaException extends \Doctrine\DBAL\DBALException { return new self("There exists no foreign key with the name '".$fkName."'.", self::FOREIGNKEY_DOESNT_EXIST); } + + static public function invalidCaseModeGiven() + { + return new self("Invalid case mode given to Schema Asset."); + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index d50586557..165f071ab 100644 --- a/lib/Doctrine/DBAL/Schema/Table.php +++ b/lib/Doctrine/DBAL/Schema/Table.php @@ -96,7 +96,7 @@ class Table extends AbstractAsset */ public function __construct($tableName, array $columns=array(), array $indexes=array(), array $fkConstraints=array(), $idGeneratorType=self::ID_NONE, array $options=array()) { - $this->_name = $tableName; + $this->_setName($tableName); $this->_idGeneratorType = $idGeneratorType; foreach ($columns AS $column) { @@ -215,7 +215,6 @@ class Table extends AbstractAsset */ public function renameColumn($oldColumnName, $newColumnName) { - $columnName = strtolower($columnName); $column = $this->getColumn($oldColumnName); $this->dropColumn($oldColumnName); @@ -232,7 +231,6 @@ class Table extends AbstractAsset */ public function changeColumn($columnName, array $options) { - $columnName = strtolower($columnName); $column = $this->getColumn($columnName); $column->setOptions($options); return $this; @@ -339,9 +337,13 @@ class Table extends AbstractAsset */ protected function _addColumn(Column $column) { - $columnName = strtolower($column->getName()); + $column->setCaseMode($this->_caseMode); + + $columnName = $column->getName(); + $columnName = strtolower($columnName); + if (isset($this->_columns[$columnName])) { - throw SchemaException::columnAlreadyExists($this->_name, $columnName); + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); } $this->_columns[$columnName] = $column; @@ -355,7 +357,10 @@ class Table extends AbstractAsset */ protected function _addIndex(Index $index) { + $index->setCaseMode($this->_caseMode); + $indexName = $index->getName(); + $indexName = strtolower($indexName); if (isset($this->_indexes[$indexName]) || ($this->_primaryKeyName != false && $index->isPrimary())) { throw SchemaException::indexAlreadyExists($indexName); @@ -374,6 +379,8 @@ class Table extends AbstractAsset */ protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) { + $constraint->setCaseMode($this->_caseMode); + if(strlen($constraint->getName())) { $name = $constraint->getName(); } else { @@ -381,6 +388,7 @@ class Table extends AbstractAsset array_merge((array)$this->getName(), $constraint->getLocalColumns()), "fk" ); } + $name = strtolower($name); $this->_fkConstraints[$name] = $constraint; } @@ -393,6 +401,7 @@ class Table extends AbstractAsset */ public function hasForeignKey($constraintName) { + $constraintName = strtolower($constraintName); return isset($this->_fkConstraints[$constraintName]); } @@ -402,6 +411,7 @@ class Table extends AbstractAsset */ public function getForeignKey($constraintName) { + $constraintName = strtolower($constraintName); if(!$this->hasForeignKey($constraintName)) { throw SchemaException::foreignKeyDoesNotExist($constraintName); } @@ -476,6 +486,7 @@ class Table extends AbstractAsset */ public function hasIndex($indexName) { + $indexName = strtolower($indexName); return (isset($this->_indexes[$indexName])); } @@ -485,6 +496,7 @@ class Table extends AbstractAsset */ public function getIndex($indexName) { + $indexName = strtolower($indexName); if (!$this->hasIndex($indexName)) { throw SchemaException::indexDoesNotExist($indexName); } diff --git a/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php b/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php index aa6957b29..2ad288a2d 100644 --- a/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php +++ b/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php @@ -109,15 +109,6 @@ class CreateSchemaSqlCollector implements Visitor } - /** - * @param Table $table - * @param Constraint $constraint - */ - public function acceptCheckConstraint(Table $table, Constraint $constraint) - { - - } - /** * @param Sequence $sequence */ diff --git a/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php b/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php index 2b1bbb4dd..c455c419c 100644 --- a/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php +++ b/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php @@ -107,15 +107,6 @@ class DropSchemaSqlCollector implements Visitor ); } - /** - * @param Table $table - * @param Constraint $constraint - */ - public function acceptCheckConstraint(Table $table, Constraint $constraint) - { - - } - /** * @param Table $table * @param Index $index diff --git a/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php b/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php index 668b5fa5b..b0a732f6a 100644 --- a/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php +++ b/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php @@ -62,12 +62,6 @@ interface Visitor */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint); - /** - * @param Table $table - * @param Constraint $constraint - */ - public function acceptCheckConstraint(Table $table, Constraint $constraint); - /** * @param Table $table * @param Index $index diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php index 118f17d21..c9a234cf0 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -69,7 +69,7 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $foundTable = false; foreach ($tables AS $table) { $this->assertType('Doctrine\DBAL\Schema\Table', $table); - if (strtolower($table->getName()) == 'list_tables_test') { + if ($table->getName() == 'list_tables_test') { $foundTable = true; $this->assertTrue($table->hasColumn('id')); @@ -94,10 +94,12 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $columns = $this->_sm->listTableColumns('list_table_columns'); - $columns = \array_change_key_case($columns, CASE_LOWER); + foreach ($columns AS $column) { + $column->setCaseMode("lower"); + } $this->assertArrayHasKey('id', $columns); - $this->assertEquals('id', strtolower($columns['id']->getname())); + $this->assertEquals('id', $columns['id']->getname()); $this->assertType('Doctrine\DBAL\Types\IntegerType', $columns['id']->gettype()); $this->assertEquals(false, $columns['id']->getunsigned()); $this->assertEquals(true, $columns['id']->getnotnull()); @@ -105,7 +107,7 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->assertType('array', $columns['id']->getPlatformOptions()); $this->assertArrayHasKey('test', $columns); - $this->assertEquals('test', strtolower($columns['test']->getname())); + $this->assertEquals('test', $columns['test']->getname()); $this->assertType('Doctrine\DBAL\Types\StringType', $columns['test']->gettype()); $this->assertEquals(255, $columns['test']->getlength()); $this->assertEquals(false, $columns['test']->getfixed()); @@ -113,7 +115,7 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->assertEquals(null, $columns['test']->getdefault()); $this->assertType('array', $columns['test']->getPlatformOptions()); - $this->assertEquals('foo', strtolower($columns['foo']->getname())); + $this->assertEquals('foo', ($columns['foo']->getname())); $this->assertType('Doctrine\DBAL\Types\TextType', $columns['foo']->gettype()); $this->assertEquals(null, $columns['foo']->getlength()); $this->assertEquals(false, $columns['foo']->getunsigned()); @@ -122,7 +124,7 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->assertEquals(null, $columns['foo']->getdefault()); $this->assertType('array', $columns['foo']->getPlatformOptions()); - $this->assertEquals('bar', strtolower($columns['bar']->getname())); + $this->assertEquals('bar', ($columns['bar']->getname())); $this->assertType('Doctrine\DBAL\Types\DecimalType', $columns['bar']->gettype()); $this->assertEquals(null, $columns['bar']->getlength()); $this->assertEquals(10, $columns['bar']->getprecision()); @@ -133,19 +135,19 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->assertEquals(null, $columns['bar']->getdefault()); $this->assertType('array', $columns['bar']->getPlatformOptions()); - $this->assertEquals('baz1', strtolower($columns['baz1']->getname())); + $this->assertEquals('baz1', ($columns['baz1']->getname())); $this->assertType('Doctrine\DBAL\Types\DateTimeType', $columns['baz1']->gettype()); $this->assertEquals(true, $columns['baz1']->getnotnull()); $this->assertEquals(null, $columns['baz1']->getdefault()); $this->assertType('array', $columns['baz1']->getPlatformOptions()); - $this->assertEquals('baz2', strtolower($columns['baz2']->getname())); + $this->assertEquals('baz2', ($columns['baz2']->getname())); $this->assertContains($columns['baz2']->gettype()->getName(), array('Time', 'Date', 'DateTime')); $this->assertEquals(true, $columns['baz2']->getnotnull()); $this->assertEquals(null, $columns['baz2']->getdefault()); $this->assertType('array', $columns['baz2']->getPlatformOptions()); - $this->assertEquals('baz3', strtolower($columns['baz3']->getname())); + $this->assertEquals('baz3', ($columns['baz3']->getname())); $this->assertContains($columns['baz2']->gettype()->getName(), array('Time', 'Date', 'DateTime')); $this->assertEquals(true, $columns['baz3']->getnotnull()); $this->assertEquals(null, $columns['baz3']->getdefault()); @@ -212,11 +214,15 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $fkeys = $this->_sm->listTableForeignKeys('test_create_fk1'); + foreach ($fkeys AS $fkey) { + $fkey->setCaseMode("lower"); + } + $this->assertEquals(1, count($fkeys)); $this->assertType('Doctrine\DBAL\Schema\ForeignKeyConstraint', $fkeys[0]); $this->assertEquals(array('foreign_key_test'), array_map('strtolower', $fkeys[0]->getLocalColumns())); $this->assertEquals(array('id'), array_map('strtolower', $fkeys[0]->getForeignColumns())); - $this->assertEquals('test_create_fk2', strtolower($fkeys[0]->getForeignTableName())); + $this->assertEquals('test_create_fk2', ($fkeys[0]->getForeignTableName())); if($fkeys[0]->hasOption('onUpdate')) { $this->assertEquals('CASCADE', $fkeys[0]->getOption('onUpdate'));