From f09f067698e8364ad8afd389080807093328691c Mon Sep 17 00:00:00 2001 From: Tim Martin Date: Wed, 31 Mar 2021 09:49:11 -0400 Subject: [PATCH] [8.x] Support useCurrentOnUpdate for MySQL datetime column types (#36817) * Support useCurrentOnUpdate for MySQL datetime column types This commit introduces support `useCurrentOnUpdate` for MySQL datetime column types. MySQL has supported using `ON UPDATE CURRENT_TIMESTAMP` for `DATETIME` columns since `v5.6.5`. Laravel currently previously only supported `useCurrentOnUpdate` for `TIMESTAMP` columns in MySQL. We set the fractional seconds precision to be consistent throughout the column initialization (for the column type, the default, and the on update). MySQL requires that the precision be identical throughout. Note that this implicitly fixed a (likely rare) bug when users tried to run something like `->dateTime('foo', 2)->useCurrent()`. Previously this would result in MySQL error code 1067 for invalid default values. It will not set the default to have the same precision as the column definition. The actual code was borrowed heavily from `typeTimeStamp`. Since they are distinct column types, it felt incorrect to DRY it up despite their similarities. Relevant MySQL documentation: [MySQL 5.6](https://dev.mysql.com/doc/refman/5.6/en/timestamp-initialization.html) [MySQL 5.7](https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html) [MySQL 8.0](https://dev.mysql.com/doc/refman/8.0/en/timestamp-initialization.html) * Use single quotes instead of double quotes when possible --- .../Database/Schema/Grammars/MySqlGrammar.php | 6 +++- .../DatabaseMySqlSchemaGrammarTest.php | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index c1a2c5586b54..dd53b9fe9bed 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -691,7 +691,11 @@ protected function typeDateTime(Fluent $column) { $columnType = $column->precision ? "datetime($column->precision)" : 'datetime'; - return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType; + $current = $column->precision ? "CURRENT_TIMESTAMP($column->precision)" : 'CURRENT_TIMESTAMP'; + + $columnType = $column->useCurrent ? "$columnType default $current" : $columnType; + + return $column->useCurrentOnUpdate ? "$columnType on update $current" : $columnType; } /** diff --git a/tests/Database/DatabaseMySqlSchemaGrammarTest.php b/tests/Database/DatabaseMySqlSchemaGrammarTest.php index 4c8813130677..05e92a085435 100755 --- a/tests/Database/DatabaseMySqlSchemaGrammarTest.php +++ b/tests/Database/DatabaseMySqlSchemaGrammarTest.php @@ -811,6 +811,42 @@ public function testAddingDateTime() $this->assertSame('alter table `users` add `foo` datetime(1) not null', $statements[0]); } + public function testAddingDateTimeWithDefaultCurrent() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo')->useCurrent(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime default CURRENT_TIMESTAMP not null', $statements[0]); + } + + public function testAddingDateTimeWithOnUpdateCurrent() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo')->useCurrentOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime on update CURRENT_TIMESTAMP not null', $statements[0]); + } + + public function testAddingDateTimeWithDefaultCurrentAndOnUpdateCurrent() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo')->useCurrent()->useCurrentOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP not null', $statements[0]); + } + + public function testAddingDateTimeWithDefaultCurrentOnUpdateCurrentAndPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo', 3)->useCurrent()->useCurrentOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime(3) default CURRENT_TIMESTAMP(3) on update CURRENT_TIMESTAMP(3) not null', $statements[0]); + } + public function testAddingDateTimeTz() { $blueprint = new Blueprint('users');