From 5c56c0fc02e54bf1ab73c172ce0e0577ccab57a8 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 2 Jun 2013 16:05:11 +0200 Subject: [PATCH 1/5] Better flexibility for abstract db types Feature was requested in yiisoft/yii#765 --- framework/yii/db/QueryBuilder.php | 6 ++ framework/yii/db/mysql/QueryBuilder.php | 2 +- tests/unit/framework/db/QueryBuilderTest.php | 109 +++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 tests/unit/framework/db/QueryBuilderTest.php diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index c0b4223..3e7f95a 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -464,6 +464,8 @@ class QueryBuilder extends \yii\base\Object * the first part will be converted, and the rest of the parts will be appended to the converted result. * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'. * + // TODO documentation + * * If a type cannot be found in [[typeMap]], it will be returned without any change. * @param string $type abstract column type * @return string physical column type. @@ -472,6 +474,10 @@ class QueryBuilder extends \yii\base\Object { if (isset($this->typeMap[$type])) { return $this->typeMap[$type]; + } elseif ((preg_match('/^(\w+)\((.+?)\)(.*)$/', $type, $matches))) { + if (isset($this->typeMap[$matches[1]])) { + return preg_replace('/\(.+\)/', '(' . $matches[2] . ')', $this->typeMap[$matches[1]]) . $matches[3]; + } } elseif (preg_match('/^(\w+)\s+/', $type, $matches)) { if (isset($this->typeMap[$matches[1]])) { return preg_replace('/^\w+/', $this->typeMap[$matches[1]], $type); diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index 70c6d64..4b35e24 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -29,7 +29,7 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_INTEGER => 'int(11)', Schema::TYPE_BIGINT => 'bigint(20)', Schema::TYPE_FLOAT => 'float', - Schema::TYPE_DECIMAL => 'decimal', + Schema::TYPE_DECIMAL => 'decimal(10,0)', Schema::TYPE_DATETIME => 'datetime', Schema::TYPE_TIMESTAMP => 'timestamp', Schema::TYPE_TIME => 'time', diff --git a/tests/unit/framework/db/QueryBuilderTest.php b/tests/unit/framework/db/QueryBuilderTest.php new file mode 100644 index 0000000..355f080 --- /dev/null +++ b/tests/unit/framework/db/QueryBuilderTest.php @@ -0,0 +1,109 @@ +driverName) + { + case 'mysql': + return new MysqlQueryBuilder($this->getConnection()); + case 'sqlite': + return new SqliteQueryBuilder($this->getConnection()); + case 'mssql': + return new MssqlQueryBuilder($this->getConnection()); + } + throw new \Exception('Test is not implemented for ' . $this->driverName); + } + + /** + * this is not used as a dataprovider for testGetColumnType to speed up the test + * when used as dataprovider every single line will cause a reconnect with the database which is not needed here + */ + public function columnTypes() + { + return array( + array(Schema::TYPE_PK, 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY'), + array(Schema::TYPE_PK . '(8)', 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY'), + array(Schema::TYPE_PK . ' CHECK (value > 5)', 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)'), + array(Schema::TYPE_PK . '(8) CHECK (value > 5)', 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)'), + array(Schema::TYPE_STRING, 'varchar(255)'), + array(Schema::TYPE_STRING . '(32)', 'varchar(32)'), + array(Schema::TYPE_STRING . ' CHECK (value LIKE "test%")', 'varchar(255) CHECK (value LIKE "test%")'), + array(Schema::TYPE_STRING . '(32) CHECK (value LIKE "test%")', 'varchar(32) CHECK (value LIKE "test%")'), + array(Schema::TYPE_STRING . ' NOT NULL', 'varchar(255) NOT NULL'), + array(Schema::TYPE_TEXT, 'text'), + array(Schema::TYPE_TEXT . '(255)', 'text'), + array(Schema::TYPE_TEXT . ' CHECK (value LIKE "test%")', 'text CHECK (value LIKE "test%")'), + array(Schema::TYPE_TEXT . '(255) CHECK (value LIKE "test%")', 'text CHECK (value LIKE "test%")'), + array(Schema::TYPE_TEXT . ' NOT NULL', 'text NOT NULL'), + array(Schema::TYPE_TEXT . '(255) NOT NULL', 'text NOT NULL'), + array(Schema::TYPE_SMALLINT, 'smallint(6)'), + array(Schema::TYPE_SMALLINT . '(8)', 'smallint(8)'), + array(Schema::TYPE_INTEGER, 'int(11)'), + array(Schema::TYPE_INTEGER . '(8)', 'int(8)'), + array(Schema::TYPE_INTEGER . ' CHECK (value > 5)', 'int(11) CHECK (value > 5)'), + array(Schema::TYPE_INTEGER . '(8) CHECK (value > 5)', 'int(8) CHECK (value > 5)'), + array(Schema::TYPE_INTEGER . ' NOT NULL', 'int(11) NOT NULL'), + array(Schema::TYPE_BIGINT, 'bigint(20)'), + array(Schema::TYPE_BIGINT . '(8)', 'bigint(8)'), + array(Schema::TYPE_BIGINT . ' CHECK (value > 5)', 'bigint(20) CHECK (value > 5)'), + array(Schema::TYPE_BIGINT . '(8) CHECK (value > 5)', 'bigint(8) CHECK (value > 5)'), + array(Schema::TYPE_BIGINT . ' NOT NULL', 'bigint(20) NOT NULL'), + array(Schema::TYPE_FLOAT, 'float'), + array(Schema::TYPE_FLOAT . '(16,5)', 'float'), + array(Schema::TYPE_FLOAT . ' CHECK (value > 5.6)', 'float CHECK (value > 5.6)'), + array(Schema::TYPE_FLOAT . '(16,5) CHECK (value > 5.6)', 'float CHECK (value > 5.6)'), + array(Schema::TYPE_FLOAT . ' NOT NULL', 'float NOT NULL'), + array(Schema::TYPE_DECIMAL, 'decimal(10,0)'), + array(Schema::TYPE_DECIMAL . '(12,4)', 'decimal(12,4)'), + array(Schema::TYPE_DECIMAL . ' CHECK (value > 5.6)', 'decimal(10,0) CHECK (value > 5.6)'), + array(Schema::TYPE_DECIMAL . '(12,4) CHECK (value > 5.6)', 'decimal(12,4) CHECK (value > 5.6)'), + array(Schema::TYPE_DECIMAL . ' NOT NULL', 'decimal(10,0) NOT NULL'), + array(Schema::TYPE_DATETIME, 'datetime'), + array(Schema::TYPE_DATETIME . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "datetime CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_DATETIME . ' NOT NULL', 'datetime NOT NULL'), + array(Schema::TYPE_TIMESTAMP, 'timestamp'), + array(Schema::TYPE_TIMESTAMP . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "timestamp CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_TIMESTAMP . ' NOT NULL', 'timestamp NOT NULL'), + array(Schema::TYPE_TIME, 'time'), + array(Schema::TYPE_TIME . " CHECK(value BETWEEN '12:00:00' AND '13:01:01')", "time CHECK(value BETWEEN '12:00:00' AND '13:01:01')"), + array(Schema::TYPE_TIME . ' NOT NULL', 'time NOT NULL'), + array(Schema::TYPE_DATE, 'date'), + array(Schema::TYPE_DATE . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "date CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_DATE . ' NOT NULL', 'date NOT NULL'), + array(Schema::TYPE_BINARY, 'blob'), + array(Schema::TYPE_BOOLEAN, 'tinyint(1)'), + array(Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1', 'tinyint(1) NOT NULL DEFAULT 1'), + array(Schema::TYPE_MONEY, 'decimal(19,4)'), + array(Schema::TYPE_MONEY . '(16,2)', 'decimal(16,2)'), + array(Schema::TYPE_MONEY . ' CHECK (value > 0.0)', 'decimal(19,4) CHECK (value > 0.0)'), + array(Schema::TYPE_MONEY . '(16,2) CHECK (value > 0.0)', 'decimal(16,2) CHECK (value > 0.0)'), + array(Schema::TYPE_MONEY . ' NOT NULL', 'decimal(19,4) NOT NULL'), + ); + } + + /** + * + */ + public function testGetColumnType() + { + $qb = $this->getQueryBuilder(); + foreach($this->columnTypes() as $item) { + list ($column, $expected) = $item; + $this->assertEquals($expected, $qb->getColumnType($column)); + } + } +} From c28b0183a5188adf1cbb574e4dbb33bb62817ca4 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 2 Jun 2013 22:12:37 +0200 Subject: [PATCH 2/5] whitespace --- tests/unit/framework/db/QueryBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/framework/db/QueryBuilderTest.php b/tests/unit/framework/db/QueryBuilderTest.php index 355f080..7dc4731 100644 --- a/tests/unit/framework/db/QueryBuilderTest.php +++ b/tests/unit/framework/db/QueryBuilderTest.php @@ -96,7 +96,7 @@ class QueryBuilderTest extends DatabaseTestCase } /** - * + * */ public function testGetColumnType() { From 7117a6752b6927038b62e2366d9034b282b3ced0 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 2 Jun 2013 22:58:42 +0200 Subject: [PATCH 3/5] Added phpdoc about precision constraints for abstract db types --- framework/yii/db/QueryBuilder.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index 3e7f95a..46457c6 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -464,7 +464,11 @@ class QueryBuilder extends \yii\base\Object * the first part will be converted, and the rest of the parts will be appended to the converted result. * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'. * - // TODO documentation + * For some of the abstract types you can also specify a length or precision constraint + * by prepending it in round brackets directly to the type. + * For example `string(32)` will be converted into "varchar(32)" on a MySQL database. + * If the underlying DBMS does not support these kind of constraints for a type it will + * be ignored. * * If a type cannot be found in [[typeMap]], it will be returned without any change. * @param string $type abstract column type From af68ee9d2745bf7b3fd27ea786c0d9add221e17b Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 3 Jun 2013 00:24:39 +0200 Subject: [PATCH 4/5] Sqlite unit test for abstract db types --- framework/yii/db/sqlite/QueryBuilder.php | 4 +- .../framework/db/sqlite/SqliteQueryBuilderTest.php | 74 ++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php diff --git a/framework/yii/db/sqlite/QueryBuilder.php b/framework/yii/db/sqlite/QueryBuilder.php index 72d48f4..52c101b 100644 --- a/framework/yii/db/sqlite/QueryBuilder.php +++ b/framework/yii/db/sqlite/QueryBuilder.php @@ -30,13 +30,13 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_INTEGER => 'integer', Schema::TYPE_BIGINT => 'bigint', Schema::TYPE_FLOAT => 'float', - Schema::TYPE_DECIMAL => 'decimal', + Schema::TYPE_DECIMAL => 'decimal(10,0)', Schema::TYPE_DATETIME => 'datetime', Schema::TYPE_TIMESTAMP => 'timestamp', Schema::TYPE_TIME => 'time', Schema::TYPE_DATE => 'date', Schema::TYPE_BINARY => 'blob', - Schema::TYPE_BOOLEAN => 'tinyint(1)', + Schema::TYPE_BOOLEAN => 'boolean', Schema::TYPE_MONEY => 'decimal(19,4)', ); diff --git a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php new file mode 100644 index 0000000..c36628f --- /dev/null +++ b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php @@ -0,0 +1,74 @@ + 5)', 'integer PRIMARY KEY AUTOINCREMENT NOT NULL CHECK (value > 5)'), + array(Schema::TYPE_PK . '(8) CHECK (value > 5)', 'integer PRIMARY KEY AUTOINCREMENT NOT NULL CHECK (value > 5)'), + array(Schema::TYPE_STRING, 'varchar(255)'), + array(Schema::TYPE_STRING . '(32)', 'varchar(32)'), + array(Schema::TYPE_STRING . ' CHECK (value LIKE "test%")', 'varchar(255) CHECK (value LIKE "test%")'), + array(Schema::TYPE_STRING . '(32) CHECK (value LIKE "test%")', 'varchar(32) CHECK (value LIKE "test%")'), + array(Schema::TYPE_STRING . ' NOT NULL', 'varchar(255) NOT NULL'), + array(Schema::TYPE_TEXT, 'text'), + array(Schema::TYPE_TEXT . '(255)', 'text'), + array(Schema::TYPE_TEXT . ' CHECK (value LIKE "test%")', 'text CHECK (value LIKE "test%")'), + array(Schema::TYPE_TEXT . '(255) CHECK (value LIKE "test%")', 'text CHECK (value LIKE "test%")'), + array(Schema::TYPE_TEXT . ' NOT NULL', 'text NOT NULL'), + array(Schema::TYPE_TEXT . '(255) NOT NULL', 'text NOT NULL'), + array(Schema::TYPE_SMALLINT, 'smallint'), + array(Schema::TYPE_SMALLINT . '(8)', 'smallint'), + array(Schema::TYPE_INTEGER, 'integer'), + array(Schema::TYPE_INTEGER . '(8)', 'integer'), + array(Schema::TYPE_INTEGER . ' CHECK (value > 5)', 'integer CHECK (value > 5)'), + array(Schema::TYPE_INTEGER . '(8) CHECK (value > 5)', 'integer CHECK (value > 5)'), + array(Schema::TYPE_INTEGER . ' NOT NULL', 'integer NOT NULL'), + array(Schema::TYPE_BIGINT, 'bigint'), + array(Schema::TYPE_BIGINT . '(8)', 'bigint'), + array(Schema::TYPE_BIGINT . ' CHECK (value > 5)', 'bigint CHECK (value > 5)'), + array(Schema::TYPE_BIGINT . '(8) CHECK (value > 5)', 'bigint CHECK (value > 5)'), + array(Schema::TYPE_BIGINT . ' NOT NULL', 'bigint NOT NULL'), + array(Schema::TYPE_FLOAT, 'float'), + array(Schema::TYPE_FLOAT . '(16,5)', 'float'), + array(Schema::TYPE_FLOAT . ' CHECK (value > 5.6)', 'float CHECK (value > 5.6)'), + array(Schema::TYPE_FLOAT . '(16,5) CHECK (value > 5.6)', 'float CHECK (value > 5.6)'), + array(Schema::TYPE_FLOAT . ' NOT NULL', 'float NOT NULL'), + array(Schema::TYPE_DECIMAL, 'decimal(10,0)'), + array(Schema::TYPE_DECIMAL . '(12,4)', 'decimal(12,4)'), + array(Schema::TYPE_DECIMAL . ' CHECK (value > 5.6)', 'decimal(10,0) CHECK (value > 5.6)'), + array(Schema::TYPE_DECIMAL . '(12,4) CHECK (value > 5.6)', 'decimal(12,4) CHECK (value > 5.6)'), + array(Schema::TYPE_DECIMAL . ' NOT NULL', 'decimal(10,0) NOT NULL'), + array(Schema::TYPE_DATETIME, 'datetime'), + array(Schema::TYPE_DATETIME . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "datetime CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_DATETIME . ' NOT NULL', 'datetime NOT NULL'), + array(Schema::TYPE_TIMESTAMP, 'timestamp'), + array(Schema::TYPE_TIMESTAMP . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "timestamp CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_TIMESTAMP . ' NOT NULL', 'timestamp NOT NULL'), + array(Schema::TYPE_TIME, 'time'), + array(Schema::TYPE_TIME . " CHECK(value BETWEEN '12:00:00' AND '13:01:01')", "time CHECK(value BETWEEN '12:00:00' AND '13:01:01')"), + array(Schema::TYPE_TIME . ' NOT NULL', 'time NOT NULL'), + array(Schema::TYPE_DATE, 'date'), + array(Schema::TYPE_DATE . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "date CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_DATE . ' NOT NULL', 'date NOT NULL'), + array(Schema::TYPE_BINARY, 'blob'), + array(Schema::TYPE_BOOLEAN, 'boolean'), + array(Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1', 'boolean NOT NULL DEFAULT 1'), + array(Schema::TYPE_MONEY, 'decimal(19,4)'), + array(Schema::TYPE_MONEY . '(16,2)', 'decimal(16,2)'), + array(Schema::TYPE_MONEY . ' CHECK (value > 0.0)', 'decimal(19,4) CHECK (value > 0.0)'), + array(Schema::TYPE_MONEY . '(16,2) CHECK (value > 0.0)', 'decimal(16,2) CHECK (value > 0.0)'), + array(Schema::TYPE_MONEY . ' NOT NULL', 'decimal(19,4) NOT NULL'), + ); + } +} \ No newline at end of file From 9b6df54c7df061e08cce580dc9cba4e7c2c80e4f Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 3 Jun 2013 00:28:32 +0200 Subject: [PATCH 5/5] Adjusted type map in MSSQL Querybuilder MSSQL seems to support limits for decimal type, so we add a default limit. --- framework/yii/db/mssql/QueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/db/mssql/QueryBuilder.php b/framework/yii/db/mssql/QueryBuilder.php index 45a7507..e7f8f80 100644 --- a/framework/yii/db/mssql/QueryBuilder.php +++ b/framework/yii/db/mssql/QueryBuilder.php @@ -28,7 +28,7 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_INTEGER => 'int(11)', Schema::TYPE_BIGINT => 'bigint(20)', Schema::TYPE_FLOAT => 'float', - Schema::TYPE_DECIMAL => 'decimal', + Schema::TYPE_DECIMAL => 'decimal(10,0)', Schema::TYPE_DATETIME => 'datetime', Schema::TYPE_TIMESTAMP => 'timestamp', Schema::TYPE_TIME => 'time',