From 07dbc4a527e41020781965c960c974de26543253 Mon Sep 17 00:00:00 2001 From: Reuben Helms Date: Mon, 3 Dec 2012 13:58:57 +1000 Subject: [PATCH] Cater for length part of TEXT field in indexes for MySQL Cater for length on TEXT column in index for _alterIndexes Handle nested arrays in schemas Used when writing indexes containg TEXT field for MySQL Change comment style from code review Remove stray space Only generate length part of TEXT index column field when necessary Override buildIndex() in Mysql instead of name() Revert DboSource::buildIndex to previous state Update Mysql::_alterIndexes for code reuse Update MysqlTest to handle quoted index names. Make code clearer, as per code review Adjust function comments, as per code review --- lib/Cake/Model/CakeSchema.php | 10 +- lib/Cake/Model/Datasource/Database/Mysql.php | 93 +++++++++++++++---- lib/Cake/Model/Datasource/DboSource.php | 2 +- .../Model/Datasource/Database/MysqlTest.php | 64 ++++++++++--- 4 files changed, 133 insertions(+), 36 deletions(-) diff --git a/lib/Cake/Model/CakeSchema.php b/lib/Cake/Model/CakeSchema.php index 2fda5f75e87..58302e78138 100644 --- a/lib/Cake/Model/CakeSchema.php +++ b/lib/Cake/Model/CakeSchema.php @@ -574,13 +574,17 @@ protected function _values($values) { if (is_array($values)) { foreach ($values as $key => $val) { if (is_array($val)) { - $vals[] = "'{$key}' => array('" . implode("', '", $val) . "')"; - } elseif (!is_numeric($key)) { + $vals[] = "'{$key}' => array(" . implode(", ", $this->_values($val)) . ")"; + } else { $val = var_export($val, true); if ($val === 'NULL') { $val = 'null'; } - $vals[] = "'{$key}' => {$val}"; + if (!is_numeric($key)) { + $vals[] = "'{$key}' => {$val}"; + } else { + $vals[] = "{$val}"; + } } } } diff --git a/lib/Cake/Model/Datasource/Database/Mysql.php b/lib/Cake/Model/Datasource/Database/Mysql.php index f96dfa42026..d002c50df05 100644 --- a/lib/Cake/Model/Datasource/Database/Mysql.php +++ b/lib/Cake/Model/Datasource/Database/Mysql.php @@ -443,6 +443,12 @@ public function index($model) { $col[] = $idx->Column_name; $index[$idx->Key_name]['column'] = $col; } + if (!empty($idx->Sub_part)) { + if (!isset($index[$idx->Key_name]['length'])) { + $index[$idx->Key_name]['length'] = array(); + } + $index[$idx->Key_name]['length'][$idx->Column_name] = $idx->Sub_part; + } } // @codingStandardsIgnoreEnd $indices->closeCursor(); @@ -543,6 +549,55 @@ protected function _alterTableParameters($table, $parameters) { return array(); } +/** + * Format indexes for create table + * + * @param array $indexes An array of indexes to generate SQL from + * @param string $table Optional table name, not used + * @return array An array of SQL statements for indexes + * @see DboSource::buildIndex() + */ + public function buildIndex($indexes, $table = null) { + $join = array(); + foreach ($indexes as $name => $value) { + $out = ''; + if ($name === 'PRIMARY') { + $out .= 'PRIMARY '; + $name = null; + } else { + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + $name = $this->startQuote . $name . $this->endQuote; + } + // length attribute only used for MySQL datasource, for TEXT/BLOB index columns + $out .= 'KEY ' . $name . ' ('; + if (is_array($value['column'])) { + if (isset($value['length'])) { + $vals = array(); + foreach ($value['column'] as $column) { + $name = $this->name($column); + if (isset($value['length'])) { + $name .= $this->_buildIndexSubPart($value['length'], $column); + } + $vals[] = $name; + } + $out .= implode(', ', $vals); + } else { + $out .= implode(', ', array_map(array(&$this, 'name'), $value['column'])); + } + } else { + $out .= $this->name($value['column']); + if (isset($value['length'])) { + $out .= $this->_buildIndexSubPart($value['length'], $value['column']); + } + } + $out .= ')'; + $join[] = $out; + } + return $join; + } + /** * Generate MySQL index alteration statements for a table. * @@ -558,33 +613,37 @@ protected function _alterIndexes($table, $indexes) { if ($name == 'PRIMARY') { $out .= 'PRIMARY KEY'; } else { - $out .= 'KEY ' . $name; + $out .= 'KEY ' . $this->startQuote . $name . $this->endQuote; } $alter[] = $out; } } if (isset($indexes['add'])) { - foreach ($indexes['add'] as $name => $value) { - $out = 'ADD '; - if ($name == 'PRIMARY') { - $out .= 'PRIMARY '; - $name = null; - } else { - if (!empty($value['unique'])) { - $out .= 'UNIQUE '; - } - } - if (is_array($value['column'])) { - $out .= 'KEY ' . $name . ' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')'; - } else { - $out .= 'KEY ' . $name . ' (' . $this->name($value['column']) . ')'; - } - $alter[] = $out; + $add = $this->buildIndex($indexes['add']); + foreach ($add as $index) { + $alter[] = 'ADD ' . $index; } } return $alter; } +/** + * Format length for text indexes + * + * @param array $lengths An array of lengths for a single index + * @param string $column The column for which to generate the index length + * @return string Formatted length part of an index field + */ + protected function _buildIndexSubPart($lengths, $column) { + if (is_null($lengths)) { + return ''; + } + if (!isset($lengths[$column])) { + return ''; + } + return '(' . $lengths[$column] . ')'; + } + /** * Returns an detailed array of sources (tables) in the database. * diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 0923bf16670..9cb615a67c6 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -830,7 +830,7 @@ public function name($data) { } if (preg_match('/^([\w-]+)\((.*)\)$/', $data, $matches)) { // Functions return $this->cacheMethod(__FUNCTION__, $cacheKey, - $matches[1] . '(' . $this->name($matches[2]) . ')' + $matches[1] . '(' . $this->name($matches[2]) . ')' ); } if ( diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php index 7ae74926824..0a3739c3c67 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php @@ -313,6 +313,26 @@ public function testIndexDetection() { $result = $this->Dbo->index('with_multiple_compound_keys', false); $this->Dbo->rawQuery('DROP TABLE ' . $name); $this->assertEquals($expected, $result); + + $name = $this->Dbo->fullTableName('with_text_index'); + $this->Dbo->rawQuery('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, text_field text, primary key(id), KEY `text_index` ( `text_field`(20) ));'); + $expected = array( + 'PRIMARY' => array('column' => 'id', 'unique' => 1), + 'text_index' => array('column' => 'text_field', 'unique' => 0, 'length' => array('text_field' => 20)), + ); + $result = $this->Dbo->index('with_text_index', false); + $this->Dbo->rawQuery('DROP TABLE ' . $name); + $this->assertEquals($expected, $result); + + $name = $this->Dbo->fullTableName('with_compound_text_index'); + $this->Dbo->rawQuery('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, text_field1 text, text_field2 text, primary key(id), KEY `text_index` ( `text_field1`(20), `text_field2`(20) ));'); + $expected = array( + 'PRIMARY' => array('column' => 'id', 'unique' => 1), + 'text_index' => array('column' => array('text_field1', 'text_field2'), 'unique' => 0, 'length' => array('text_field1' => 20, 'text_field2' => 20)), + ); + $result = $this->Dbo->index('with_compound_text_index', false); + $this->Dbo->rawQuery('DROP TABLE ' . $name); + $this->assertEquals($expected, $result); } /** @@ -548,9 +568,9 @@ public function testAlterSchemaIndexes() { $result = $this->Dbo->alterSchema($schemaB->compare($schemaA)); $this->assertContains("ALTER TABLE $table", $result); - $this->assertContains('ADD KEY name_idx (`name`),', $result); - $this->assertContains('ADD KEY group_idx (`group1`),', $result); - $this->assertContains('ADD KEY compound_idx (`group1`, `group2`),', $result); + $this->assertContains('ADD KEY `name_idx` (`name`),', $result); + $this->assertContains('ADD KEY `group_idx` (`group1`),', $result); + $this->assertContains('ADD KEY `compound_idx` (`group1`, `group2`),', $result); $this->assertContains('ADD PRIMARY KEY (`id`);', $result); //Test that the string is syntactically correct @@ -576,13 +596,13 @@ public function testAlterSchemaIndexes() { $result = $this->Dbo->alterSchema($schemaC->compare($schemaB)); $this->assertContains("ALTER TABLE $table", $result); $this->assertContains('DROP PRIMARY KEY,', $result); - $this->assertContains('DROP KEY name_idx,', $result); - $this->assertContains('DROP KEY group_idx,', $result); - $this->assertContains('DROP KEY compound_idx,', $result); - $this->assertContains('ADD KEY id_name_idx (`id`, `name`),', $result); - $this->assertContains('ADD UNIQUE KEY name_idx (`name`),', $result); - $this->assertContains('ADD KEY group_idx (`group2`),', $result); - $this->assertContains('ADD KEY compound_idx (`group2`, `group1`);', $result); + $this->assertContains('DROP KEY `name_idx`,', $result); + $this->assertContains('DROP KEY `group_idx`,', $result); + $this->assertContains('DROP KEY `compound_idx`,', $result); + $this->assertContains('ADD KEY `id_name_idx` (`id`, `name`),', $result); + $this->assertContains('ADD UNIQUE KEY `name_idx` (`name`),', $result); + $this->assertContains('ADD KEY `group_idx` (`group2`),', $result); + $this->assertContains('ADD KEY `compound_idx` (`group2`, `group1`);', $result); $query = $this->Dbo->getConnection()->prepare($result); $this->assertEquals($query->queryString, $result); @@ -594,10 +614,10 @@ public function testAlterSchemaIndexes() { $result = $this->Dbo->alterSchema($schemaA->compare($schemaC)); $this->assertContains("ALTER TABLE $table", $result); - $this->assertContains('DROP KEY name_idx,', $result); - $this->assertContains('DROP KEY group_idx,', $result); - $this->assertContains('DROP KEY compound_idx,', $result); - $this->assertContains('DROP KEY id_name_idx;', $result); + $this->assertContains('DROP KEY `name_idx`,', $result); + $this->assertContains('DROP KEY `group_idx`,', $result); + $this->assertContains('DROP KEY `compound_idx`,', $result); + $this->assertContains('DROP KEY `id_name_idx`;', $result); $query = $this->Dbo->getConnection()->prepare($result); $this->assertEquals($query->queryString, $result); @@ -2902,6 +2922,20 @@ public function testBuildIndex() { $result = $this->Dbo->buildIndex($data); $expected = array('UNIQUE KEY `MyIndex` (`id`, `name`)'); $this->assertEquals($expected, $result); + + $data = array( + 'MyTextIndex' => array('column' => 'text_field', 'length' => array('text_field' => 20)) + ); + $result = $this->Dbo->buildIndex($data); + $expected = array('KEY `MyTextIndex` (`text_field`(20))'); + $this->assertEquals($expected, $result); + + $data = array( + 'MyMultiTextIndex' => array('column' => array('text_field1', 'text_field2'), 'length' => array('text_field1' => 20, 'text_field2' => 20)) + ); + $result = $this->Dbo->buildIndex($data); + $expected = array('KEY `MyMultiTextIndex` (`text_field1`(20), `text_field2`(20))'); + $this->assertEquals($expected, $result); } /** @@ -2972,7 +3006,7 @@ public function testBuildColumn2() { 'type' => 'timestamp', 'default' => 'current_timestamp', 'null' => false, - ); + ); $result = $this->Dbo->buildColumn($data); $expected = '`created` timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL'; $this->assertEquals($expected, $result);