diff --git a/docs/en/00_Getting_Started/00_Server_Requirements.md b/docs/en/00_Getting_Started/00_Server_Requirements.md index cb6bbc4a4..9b4e95b34 100644 --- a/docs/en/00_Getting_Started/00_Server_Requirements.md +++ b/docs/en/00_Getting_Started/00_Server_Requirements.md @@ -56,6 +56,17 @@ setting. It is generally recommended to leave this setting as-is because it resu some advanced cases, the sql_mode can be configured on the database connection via the configuration API ( see `MySQLDatabase::$sql_mode` for more details.) This setting is only available in Silverstripe CMS 4.7 and later. + ### MySQL/MariaDB Int width in schema + +MySQL 8.0.17 stopped reporting the width attribute for integers while MariaDB did not change its behaviour. +This results in constant rebuilding of the schema when MySQLSchemaManager expects a field to look like e.g. +`INT(8)` and MySQL server reports it simply as `INT`. MySQLSchemaManager has been updated to detect the MySQL +server implementation and act accordingly. In cases when auto-detection fails, you can force the desired behaviour like this: +```yml +SilverStripe\ORM\Connect\MySQLSchemaManager: + schema_use_int_width: true # or false when INT widths should be ignored +``` + ## Webserver Configuration ### Overview diff --git a/src/ORM/Connect/MySQLSchemaManager.php b/src/ORM/Connect/MySQLSchemaManager.php index 701eae4c7..fd02b38ca 100644 --- a/src/ORM/Connect/MySQLSchemaManager.php +++ b/src/ORM/Connect/MySQLSchemaManager.php @@ -241,6 +241,23 @@ class MySQLSchemaManager extends DBSchemaManager protected static $_cache_collation_info = []; + private function shouldUseIntegerWidth() + { + // MySQL 8.0.17 stopped reporting the width attribute for integers + // https://github.com/silverstripe/silverstripe-framework/issues/9453 + // Note: MariaDB did not change its behaviour + $forceWidth = Config::inst()->get(self::class, 'schema_use_int_width'); + if ($forceWidth !== null) { + return $forceWidth; + } + $v = $this->database->getVersion(); + if (false !== strpos($v, 'MariaDB')) { + // MariaDB is included in the version string: https://mariadb.com/kb/en/version/ + return true; + } + return version_compare($v, '8.0.17', '<'); + } + public function fieldList($table) { $fields = $this->query("SHOW FULL FIELDS IN \"$table\""); @@ -405,7 +422,8 @@ class MySQLSchemaManager extends DBSchemaManager //'default'=>$this->default); //DB::requireField($this->tableName, $this->name, "tinyint(1) unsigned not null default //'{$this->defaultVal}'"); - return 'tinyint(1) unsigned not null' . $this->defaultClause($values); + $width = $this->shouldUseIntegerWidth() ? '(1)' : ''; + return 'tinyint' . $width . ' unsigned not null' . $this->defaultClause($values); } /** @@ -518,7 +536,8 @@ class MySQLSchemaManager extends DBSchemaManager //For reference, this is what typically gets passed to this function: //$parts=Array('datatype'=>'int', 'precision'=>11, 'null'=>'not null', 'default'=>(int)$this->default); //DB::requireField($this->tableName, $this->name, "int(11) not null default '{$this->defaultVal}'"); - return "int(11) not null" . $this->defaultClause($values); + $width = $this->shouldUseIntegerWidth() ? '(11)' : ''; + return 'int' . $width . ' not null' . $this->defaultClause($values); } /** @@ -534,8 +553,8 @@ class MySQLSchemaManager extends DBSchemaManager // 'arrayValue'=>$this->arrayValue); //$values=Array('type'=>'bigint', 'parts'=>$parts); //DB::requireField($this->tableName, $this->name, $values); - - return 'bigint(20) not null' . $this->defaultClause($values); + $width = $this->shouldUseIntegerWidth() ? '(20)' : ''; + return 'bigint' . $width . ' not null' . $this->defaultClause($values); } /** @@ -616,7 +635,8 @@ class MySQLSchemaManager extends DBSchemaManager public function IdColumn($asDbValue = false, $hasAutoIncPK = true) { - return 'int(11) not null auto_increment'; + $width = $this->shouldUseIntegerWidth() ? '(11)' : ''; + return 'int' . $width . ' not null auto_increment'; } /** diff --git a/tests/php/ORM/MySQLSchemaManagerTest.php b/tests/php/ORM/MySQLSchemaManagerTest.php new file mode 100644 index 000000000..a9a256e48 --- /dev/null +++ b/tests/php/ORM/MySQLSchemaManagerTest.php @@ -0,0 +1,209 @@ +set('schema_use_int_width', null); + + $mysqlDBdummy = new MySQLDBDummy('8.0.16-standard'); + $mgr = new MySQLSchemaManager(); + $mgr->setDatabase($mysqlDBdummy); + + $this->assertEquals( + 'tinyint(1) unsigned not null', + $mgr->boolean([]), + 'mysql 8.0.16 boolean has width' + ); + + $this->assertEquals( + 'int(11) not null', + $mgr->int([]), + 'mysql 8.0.16 int has width' + ); + + $this->assertEquals( + 'bigint(20) not null', + $mgr->bigint([]), + 'mysql 8.0.16 bigint has width' + ); + + $this->assertEquals( + 'int(11) not null auto_increment', + $mgr->IdColumn([]), + 'mysql 8.0.16 IdColumn has width' + ); + } + + public function testMYSQL_8_0_17() + { + Config::forClass(MySQLSchemaManager::class)->set('schema_use_int_width', null); + + $mysqlDBdummy = new MySQLDBDummy('8.0.17'); + $mgr = new MySQLSchemaManager(); + $mgr->setDatabase($mysqlDBdummy); + + $this->assertEquals( + 'tinyint unsigned not null', + $mgr->boolean([]), + 'mysql 8.0.17 boolean has no width' + ); + + $this->assertEquals( + 'int not null', + $mgr->int([]), + 'mysql 8.0.17 int has no width' + ); + + $this->assertEquals( + 'bigint not null', + $mgr->bigint([]), + 'mysql 8.0.17 bigint has no width' + ); + + $this->assertEquals( + 'int not null auto_increment', + $mgr->IdColumn([]), + 'mysql 8.0.17 IdColumn has no width' + ); + } + + public function testMariaDB() + { + Config::forClass(MySQLSchemaManager::class)->set('schema_use_int_width', null); + + $mariaDBdummy = new MySQLDBDummy('10.4.7-MariaDB'); + $mgr = new MySQLSchemaManager(); + $mgr->setDatabase($mariaDBdummy); + + $this->assertEquals( + 'tinyint(1) unsigned not null', + $mgr->boolean([]), + 'mariadb boolean has width' + ); + + $this->assertEquals( + 'int(11) not null', + $mgr->int([]), + 'mariadb int has width' + ); + + $this->assertEquals( + 'bigint(20) not null', + $mgr->bigint([]), + 'mariadb bigint has width' + ); + + $this->assertEquals( + 'int(11) not null auto_increment', + $mgr->IdColumn([]), + 'mariadb IdColumn has width' + ); + } + + public function testMySQLForcedON() + { + Config::forClass(MySQLSchemaManager::class)->set('schema_use_int_width', true); + + $mysqlDBdummy = new MySQLDBDummy('8.0.17-standard'); + $mgr = new MySQLSchemaManager(); + $mgr->setDatabase($mysqlDBdummy); + + $this->assertEquals( + 'tinyint(1) unsigned not null', + $mgr->boolean([]), + 'mysql 8.0.17 boolean forced on has width' + ); + + $this->assertEquals( + 'int(11) not null', + $mgr->int([]), + 'mysql 8.0.17 int forced on has width' + ); + + $this->assertEquals( + 'bigint(20) not null', + $mgr->bigint([]), + 'mysql 8.0.17 bigint forced on has width' + ); + + $this->assertEquals( + 'int(11) not null auto_increment', + $mgr->IdColumn([]), + 'mysql 8.0.17 IdColumn forced on has width' + ); + } + + public function testMySQLForcedOFF() + { + Config::forClass(MySQLSchemaManager::class)->set('schema_use_int_width', false); + + $mysqlDBdummy = new MySQLDBDummy('8.0.16-standard'); + $mgr = new MySQLSchemaManager(); + $mgr->setDatabase($mysqlDBdummy); + + $this->assertEquals( + 'tinyint unsigned not null', + $mgr->boolean([]), + 'mysql 8.0.16 boolean forced off has no width' + ); + + $this->assertEquals( + 'int not null', + $mgr->int([]), + 'mysql 8.0.16 int forced off has no width' + ); + + $this->assertEquals( + 'bigint not null', + $mgr->bigint([]), + 'mysql 8.0.16 bigint forced off has no width' + ); + + $this->assertEquals( + 'int not null auto_increment', + $mgr->IdColumn([]), + 'mysql 8.0.16 IdColumn forced off has no width' + ); + } + + public function testMariaDBForcedOFF() + { + Config::forClass(MySQLSchemaManager::class)->set('schema_use_int_width', false); + + $mysqlDBdummy = new MySQLDBDummy('10.0.1-MariaDB'); + $mgr = new MySQLSchemaManager(); + $mgr->setDatabase($mysqlDBdummy); + + $this->assertEquals( + 'tinyint unsigned not null', + $mgr->boolean([]), + 'mariadb boolean forced off has no width' + ); + + $this->assertEquals( + 'int not null', + $mgr->int([]), + 'mariadb int forced off has no width' + ); + + $this->assertEquals( + 'bigint not null', + $mgr->bigint([]), + 'mariadb bigint forced off has no width' + ); + + $this->assertEquals( + 'int not null auto_increment', + $mgr->IdColumn([]), + 'mariadb IdColumn forced off has no width' + ); + } +} diff --git a/tests/php/ORM/MySQLSchemaManagerTest/MySQLDBDummy.php b/tests/php/ORM/MySQLSchemaManagerTest/MySQLDBDummy.php new file mode 100644 index 000000000..b6b03a47c --- /dev/null +++ b/tests/php/ORM/MySQLSchemaManagerTest/MySQLDBDummy.php @@ -0,0 +1,21 @@ +dbVersion = $version; + } + + public function getVersion() + { + return $this->dbVersion; + } +}