diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index f955f97571..d5577302f2 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.16 under development ------------------------ +- Enh #14367: In `yii\db\mysql\QueryBuilder` added support fractional seconds for time types for MySQL >= 5.6.4 (konstantin-vl) - Bug #16766: `yii\filters\ContentNegotiator` was not setting `Vary` header to inform cache recipients (koteq, cebe, samdark) - Bug #11960: Fixed `checked` option ignore in `yii\helpers\BaseHtml::checkbox()` (misantron) - Bug #14759: Fixed `yii\web\JsonResponseFormatter` output for `null` data (misantron) diff --git a/framework/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php index 491be25aae..2394d45ad4 100644 --- a/framework/db/mysql/QueryBuilder.php +++ b/framework/db/mysql/QueryBuilder.php @@ -39,9 +39,6 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_FLOAT => 'float', Schema::TYPE_DOUBLE => 'double', 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)', @@ -49,6 +46,15 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_JSON => 'json' ]; + /** + * {@inheritdoc} + */ + public function init() + { + parent::init(); + + $this->typeMap = array_merge($this->typeMap, $this->defaultTimeTypeMap()); + } /** * {@inheritdoc} @@ -362,4 +368,41 @@ class QueryBuilder extends \yii\db\QueryBuilder return null; } + /** + * Checks the ability to use fractional seconds. + * + * @return bool + * @see https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html + */ + private function supportsFractionalSeconds() + { + $version = $this->db->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION); + return version_compare($version, '5.6.4', '>='); + } + + /** + * Returns the map for default time type. + * If the version of MySQL is lower than 5.6.4, then the types will be without fractional seconds, + * otherwise with fractional seconds. + * + * @return array + */ + private function defaultTimeTypeMap() + { + $map = [ + Schema::TYPE_DATETIME => 'datetime', + Schema::TYPE_TIMESTAMP => 'timestamp', + Schema::TYPE_TIME => 'time', + ]; + + if ($this->supportsFractionalSeconds()) { + $map = [ + Schema::TYPE_DATETIME => 'datetime(0)', + Schema::TYPE_TIMESTAMP => 'timestamp(0)', + Schema::TYPE_TIME => 'time(0)', + ]; + } + + return $map; + } } diff --git a/tests/framework/db/QueryBuilderTest.php b/tests/framework/db/QueryBuilderTest.php index 7776068c9f..154fd51caa 100644 --- a/tests/framework/db/QueryBuilderTest.php +++ b/tests/framework/db/QueryBuilderTest.php @@ -280,7 +280,6 @@ abstract class QueryBuilderTest extends DatabaseTestCase Schema::TYPE_DATETIME . ' NOT NULL', $this->dateTime()->notNull(), [ - 'mysql' => 'datetime NOT NULL', 'postgres' => 'timestamp(0) NOT NULL', 'sqlite' => 'datetime NOT NULL', 'oci' => 'TIMESTAMP NOT NULL', @@ -292,7 +291,6 @@ abstract class QueryBuilderTest extends DatabaseTestCase Schema::TYPE_DATETIME, $this->dateTime(), [ - 'mysql' => 'datetime', 'postgres' => 'timestamp(0)', 'sqlite' => 'datetime', 'oci' => 'TIMESTAMP', @@ -871,7 +869,6 @@ abstract class QueryBuilderTest extends DatabaseTestCase Schema::TYPE_TIME . ' NOT NULL', $this->time()->notNull(), [ - 'mysql' => 'time NOT NULL', 'postgres' => 'time(0) NOT NULL', 'sqlite' => 'time NOT NULL', 'oci' => 'TIMESTAMP NOT NULL', @@ -883,7 +880,6 @@ abstract class QueryBuilderTest extends DatabaseTestCase Schema::TYPE_TIME, $this->time(), [ - 'mysql' => 'time', 'postgres' => 'time(0)', 'sqlite' => 'time', 'oci' => 'TIMESTAMP', @@ -906,7 +902,6 @@ abstract class QueryBuilderTest extends DatabaseTestCase Schema::TYPE_TIMESTAMP . ' NOT NULL', $this->timestamp()->notNull(), [ - 'mysql' => 'timestamp NOT NULL', 'postgres' => 'timestamp(0) NOT NULL', 'sqlite' => 'timestamp NOT NULL', 'oci' => 'TIMESTAMP NOT NULL', @@ -934,7 +929,6 @@ abstract class QueryBuilderTest extends DatabaseTestCase Schema::TYPE_TIMESTAMP . ' NULL DEFAULT NULL', $this->timestamp()->defaultValue(null), [ - 'mysql' => 'timestamp NULL DEFAULT NULL', 'postgres' => 'timestamp(0) NULL DEFAULT NULL', 'sqlite' => 'timestamp NULL DEFAULT NULL', 'sqlsrv' => 'timestamp NULL DEFAULT NULL', diff --git a/tests/framework/db/mysql/QueryBuilderTest.php b/tests/framework/db/mysql/QueryBuilderTest.php index f45e6e53fe..a47fab9fd3 100644 --- a/tests/framework/db/mysql/QueryBuilderTest.php +++ b/tests/framework/db/mysql/QueryBuilderTest.php @@ -85,6 +85,64 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest ]; } + return array_merge(parent::columnTypes(), $this->columnTimeTypes(), $columns); + } + + public function columnTimeTypes() + { + $columns = [ + [ + Schema::TYPE_DATETIME . ' NOT NULL', + $this->dateTime()->notNull(), + 'datetime NOT NULL', + ], + [ + Schema::TYPE_DATETIME, + $this->dateTime(), + 'datetime', + ], + [ + Schema::TYPE_TIME . ' NOT NULL', + $this->time()->notNull(), + 'time NOT NULL', + ], + [ + Schema::TYPE_TIME, + $this->time(), + 'time', + ], + [ + Schema::TYPE_TIMESTAMP . ' NOT NULL', + $this->timestamp()->notNull(), + 'timestamp NOT NULL', + ], + [ + Schema::TYPE_TIMESTAMP . ' NULL DEFAULT NULL', + $this->timestamp()->defaultValue(null), + 'timestamp NULL DEFAULT NULL', + ], + ]; + + /** + * @link https://github.com/yiisoft/yii2/issues/14367 + */ + $mysqlVersion = $this->getDb()->getSlavePdo()->getAttribute(\PDO::ATTR_SERVER_VERSION); + $supportsFractionalSeconds = version_compare($mysqlVersion,'5.6.4', '>='); + if ($supportsFractionalSeconds) { + $expectedValues = [ + 'datetime(0) NOT NULL', + 'datetime(0)', + 'time(0) NOT NULL', + 'time(0)', + 'timestamp(0) NOT NULL', + 'timestamp(0) NULL DEFAULT NULL', + ]; + + foreach ($expectedValues as $index => $expected) { + $columns[$index][2] = $expected; + } + } + /** * @link https://github.com/yiisoft/yii2/issues/14834 */ @@ -99,11 +157,11 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest $columns[] = [ Schema::TYPE_TIMESTAMP, $this->timestamp(), - 'timestamp', + $supportsFractionalSeconds ? 'timestamp(0)' : 'timestamp', ]; } - return array_merge(parent::columnTypes(), $columns); + return $columns; } public function primaryKeysProvider()