From 001ecae35ae90a91965ff9b03113771a7e73c2b2 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sun, 24 Mar 2013 21:59:49 -0400 Subject: [PATCH] Start adding columnType() SqlDialects will need a way to convert from platform specific types to the abstract ones used in the rest of the framework. --- .../Database/Dialect/MysqlDialectTrait.php | 53 +++++- .../Datasource/Database/Driver/MysqlTest.php | 153 +++++++++++++++++- 2 files changed, 200 insertions(+), 6 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Dialect/MysqlDialectTrait.php b/lib/Cake/Model/Datasource/Database/Dialect/MysqlDialectTrait.php index 22cb430a235..855d16ca3f0 100644 --- a/lib/Cake/Model/Datasource/Database/Dialect/MysqlDialectTrait.php +++ b/lib/Cake/Model/Datasource/Database/Dialect/MysqlDialectTrait.php @@ -17,6 +17,8 @@ */ namespace Cake\Model\Datasource\Database\Dialect; +use Cake\Error; + /** * Contains functions that encapsulates the SQL dialect used by MySQL, * including query translators and schema introspection. @@ -30,7 +32,7 @@ trait MysqlDialectTrait { * getting tables from. * @return array An array of (sql, params) to execute. */ - public function listTablesSql($config) { + public function listTablesSql(array $config) { return ["SHOW TABLES FROM " . $this->quoteIdentifier($config['database']), []]; } @@ -41,7 +43,54 @@ public function listTablesSql($config) { * @return array An array of (sql, params) to execute. */ public function describeTableSql($table) { - return ["SHOW TABLES FROM " . $this->quoteIdentifier($table), []]; + return ["SHOW FULL COLUMNS FROM " . $this->quoteIdentifier($table), []]; } +/** + * Convert a MySQL column type into an abstract type. + * + * @param string $column The column type + length + * @return array List of (type, length) + */ + public function columnType($column) { + preg_match('/([a-z]+)(?:\(([0-9,]+)\))?/i', $column, $matches); + if (empty($matches)) { + throw new Error\Exception(__d('cake_dev', 'Unable to parse column type from "%s"', $column)); + } + + $col = strtolower($matches[1]); + $length = null; + if (isset($matches[2])) { + $length = (int)$matches[2]; + } + + if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) { + return [$col, $length]; + } + if (($col === 'tinyint' && $length === 1) || $col === 'boolean') { + return ['boolean', null]; + } + if (strpos($col, 'bigint') !== false || $col === 'bigint') { + return ['biginteger', $length]; + } + if (strpos($col, 'int') !== false) { + return ['integer', $length]; + } + if (strpos($col, 'char') !== false || $col === 'tinytext') { + return ['string', $length]; + } + if (strpos($col, 'text') !== false) { + return ['text', $length]; + } + if (strpos($col, 'blob') !== false || $col === 'binary') { + return ['binary', $length]; + } + if (strpos($col, 'float') !== false || strpos($col, 'double') !== false) { + return ['float', $length]; + } + if (strpos($col, 'decimal') !== false) { + return ['decimal', null]; + } + return 'text'; + } } diff --git a/lib/Cake/Test/TestCase/Model/Datasource/Database/Driver/MysqlTest.php b/lib/Cake/Test/TestCase/Model/Datasource/Database/Driver/MysqlTest.php index e1d3ac6c487..ca1bc0f2c96 100644 --- a/lib/Cake/Test/TestCase/Model/Datasource/Database/Driver/MysqlTest.php +++ b/lib/Cake/Test/TestCase/Model/Datasource/Database/Driver/MysqlTest.php @@ -27,13 +27,23 @@ */ class MysqlTest extends \Cake\TestSuite\TestCase { +/** + * setUp + * + * @return void + */ + public function setUp() { + parent::setUp(); + $config = Configure::read('Datasource.test'); + $this->skipIf(strpos($config['datasource'], 'Mysql') === false, 'Not using Mysql for test config'); + } /** * Test connecting to Mysql with default configuration * * @return void */ public function testConnectionConfigDefault() { - $driver = $this->getMock('Cake\Model\Datasource\Database\driver\Mysql', ['_connect']); + $driver = $this->getMock('Cake\Model\Datasource\Database\Driver\Mysql', ['_connect']); $expected = [ 'persistent' => true, 'host' => 'localhost', @@ -65,7 +75,7 @@ public function testConnectionConfigDefault() { * @return void */ public function testConnectionConfigCustom() { - $driver = $this->getMock('Cake\Model\Datasource\Database\driver\Mysql', ['_connect']); + $driver = $this->getMock('Cake\Model\Datasource\Database\Driver\Mysql', ['_connect']); $config = [ 'persistent' => false, 'host' => 'foo', @@ -93,17 +103,114 @@ public function testConnectionConfigCustom() { $driver->connect($config); } +/** + * Helper method for testing methods. + * + * @return void + */ protected function _createTables($connection) { $connection->execute('DROP TABLE IF EXISTS articles'); $connection->execute('DROP TABLE IF EXISTS authors'); - $table = 'CREATE TABLE authors(id int, name varchar(50))'; + $table = <<execute($table); - $table = 'CREATE TABLE articles(id int, title varchar(20), body varchar(50), author_id int)'; + $table = <<execute($table); } +/** + * Dataprovider for column testing + * + * @return array + */ + public static function columnProvider() { + return [ + [ + 'DATETIME', + ['datetime', null] + ], + [ + 'DATE', + ['date', null] + ], + [ + 'TIME', + ['time', null] + ], + [ + 'TINYINT(1)', + ['boolean', null] + ], + [ + 'TINYINT(2)', + ['integer', 2] + ], + [ + 'BIGINT', + ['biginteger', null] + ], + [ + 'VARCHAR(255)', + ['string', 255] + ], + [ + 'CHAR(25)', + ['string', 25] + ], + [ + 'TINYTEXT', + ['string', null] + ], + [ + 'BLOB', + ['binary', null] + ], + [ + 'MEDIUMBLOB', + ['binary', null] + ], + [ + 'FLOAT', + ['float', null] + ], + [ + 'DOUBLE', + ['float', null] + ], + [ + 'DECIMAL(11,2)', + ['decimal', null] + ], + ]; + } + +/** + * Test parsing MySQL column types. + * + * @dataProvider columnProvider + * @return void + */ + public function testColumnType($input, $expected) { + $driver = $this->getMock('Cake\Model\Datasource\Database\Driver\Mysql', ['_connect']); + $this->assertEquals($driver->columnType($input), $expected); + } + /** * Test listing tables with Mysql * @@ -126,5 +233,43 @@ public function testListTables() { * @return void */ public function testDescribeTable() { + $connection = new Connection(Configure::read('Datasource.test')); + $this->_createTables($connection); + + $result = $connection->describe('articles'); + $expected = [ + 'id' => [ + 'type' => 'biginteger', + 'null' => false, + 'default' => null, + 'length' => 8, + 'primary' => true + ], + 'title' => [ + 'type' => 'string', + 'null' => true, + 'default' => null, + 'length' => 20, + ], + 'body' => [ + 'type' => 'text', + 'null' => true, + 'default' => null, + 'length' => null, + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => false, + 'default' => null, + 'length' => 11, + ], + 'created' => [ + 'type' => 'datetime', + 'null' => true, + 'default' => null, + 'length' => null, + ], + ]; + $this->assertEquals($expected, $result); } }