From b90c2adb7f65b610c84db80fa35b66cc036e1a66 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Mon, 15 Aug 2022 13:18:13 +0000 Subject: [PATCH] Database: Ignore display width for integer data types in `dbDelta()` on MySQL 8.0.17 or later. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MySQL 8.0.17 deprecated the display width attribute for integer data types: > As of MySQL 8.0.17, the `ZEROFILL` attribute is deprecated for numeric data types, as is the display width attribute for integer data types. You should expect support for `ZEROFILL` and display widths for integer data types to be removed in a future version of MySQL. Consider using an alternative means of producing the effect of these attributes. For example, applications can use the `LPAD()` function to zero-pad numbers up to the desired width, or they can store the formatted numbers in `CHAR` columns. In practice, this means that display width is removed for integer types when creating a table: * `BIGINT(20)` → `BIGINT` * `INT(11)` → `INT` * `MEDIUMINT(9)` → `MEDIUMINT` * `SMALLINT(6)` → `SMALLINT` * `TINYINT(4)` → `TINYINT` Note: This only applies specifically to MySQL 8.0.17 or later. In MariaDB, display width for integer types is still available and expected. This commit ensures that `dbDelta()`, which relies on the `DESCRIBE` SQL command to get the existing table structure and field types, when running on MySQL 8.0.17 or later, does not unnecessarily attempt to convert `BIGINT` fields back to `BIGINT(20)`, `INT` back to `INT(11)`, etc. When comparing the field type in the query with the existing field type, if display width is the only difference, it can be safely ignored to match MySQL behavior. The change is covered by existing `dbDelta()` unit tests: * A test for not altering `wp_get_db_schema()` queries on an existing install using MySQL 8.0.17+ now passes. * More than twenty tests which previously failed on PHP 8.0.x + MariaDB due to incorrect expectations, caused by MariaDB version reporting not being consistent between PHP versions, now pass. References: * [https://dev.mysql.com/doc/refman/8.0/en/numeric-type-attributes.html MySQL: Nymeric Type Attributes] * [https://mariadb.com/kb/en/data-types-numeric-data-types/ MariaDB: Numeric Data Types] Follow-up to [1575], [18899], [37525], [47183], [47184]. Props SergeyBiryukov, pbearne, leewillis77, JavierCasares, desrosj, costdev, johnbillion. Fixes #49364. See #51740. Built from https://develop.svn.wordpress.org/trunk@53897 git-svn-id: http://core.svn.wordpress.org/trunk@53456 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/includes/upgrade.php | 49 ++++++++++++++++++++++++++++++++++- wp-includes/version.php | 2 +- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/wp-admin/includes/upgrade.php b/wp-admin/includes/upgrade.php index dfe38d29e3..5030483769 100644 --- a/wp-admin/includes/upgrade.php +++ b/wp-admin/includes/upgrade.php @@ -2713,6 +2713,8 @@ function deslash( $content ) { * Useful for creating new tables and updating existing tables to a new structure. * * @since 1.5.0 + * @since 6.1.0 Ignores display width for integer data types on MySQL 8.0.17 or later, + * to match MySQL behavior. Note: This does not affect MariaDB. * * @global wpdb $wpdb WordPress database abstraction object. * @@ -2789,8 +2791,12 @@ function dbDelta( $queries = '', $execute = true ) { // phpcs:ignore WordPress.N $text_fields = array( 'tinytext', 'text', 'mediumtext', 'longtext' ); $blob_fields = array( 'tinyblob', 'blob', 'mediumblob', 'longblob' ); + $int_fields = array( 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint' ); + + $global_tables = $wpdb->tables( 'global' ); + $db_version = $wpdb->db_version(); + $db_server_info = $wpdb->db_server_info(); - $global_tables = $wpdb->tables( 'global' ); foreach ( $cqueries as $table => $qry ) { // Upgrade global tables only for the main site. Don't upgrade at all if conditions are not optimal. if ( in_array( $table, $global_tables, true ) && ! wp_should_upgrade_global_tables() ) { @@ -2948,6 +2954,19 @@ function dbDelta( $queries = '', $execute = true ) { // phpcs:ignore WordPress.N $tablefield_field_lowercased = strtolower( $tablefield->Field ); $tablefield_type_lowercased = strtolower( $tablefield->Type ); + $tablefield_type_without_parentheses = preg_replace( + '/' + . '(.+)' // Field type, e.g. `int`. + . '\(\d*\)' // Display width. + . '(.*)' // Optional attributes, e.g. `unsigned`. + . '/', + '$1$2', + $tablefield_type_lowercased + ); + + // Get the type without attributes, e.g. `int`. + $tablefield_type_base = strtok( $tablefield_type_without_parentheses, ' ' ); + // If the table field exists in the field array... if ( array_key_exists( $tablefield_field_lowercased, $cfields ) ) { @@ -2956,6 +2975,19 @@ function dbDelta( $queries = '', $execute = true ) { // phpcs:ignore WordPress.N $fieldtype = $matches[1]; $fieldtype_lowercased = strtolower( $fieldtype ); + $fieldtype_without_parentheses = preg_replace( + '/' + . '(.+)' // Field type, e.g. `int`. + . '\(\d*\)' // Display width. + . '(.*)' // Optional attributes, e.g. `unsigned`. + . '/', + '$1$2', + $fieldtype_lowercased + ); + + // Get the type without attributes, e.g. `int`. + $fieldtype_base = strtok( $fieldtype_without_parentheses, ' ' ); + // Is actual field type different from the field type in query? if ( $tablefield->Type != $fieldtype ) { $do_change = true; @@ -2971,6 +3003,21 @@ function dbDelta( $queries = '', $execute = true ) { // phpcs:ignore WordPress.N } } + if ( in_array( $fieldtype_base, $int_fields, true ) && in_array( $tablefield_type_base, $int_fields, true ) + && $fieldtype_without_parentheses === $tablefield_type_without_parentheses + ) { + /* + * MySQL 8.0.17 or later does not support display width for integer data types, + * so if display width is the only difference, it can be safely ignored. + * Note: This is specific to MySQL and does not affect MariaDB. + */ + if ( version_compare( $db_version, '8.0.17', '>=' ) + && ! str_contains( $db_server_info, 'MariaDB' ) + ) { + $do_change = false; + } + } + if ( $do_change ) { // Add a query to change the column type. $cqueries[] = "ALTER TABLE {$table} CHANGE COLUMN `{$tablefield->Field}` " . $cfields[ $tablefield_field_lowercased ]; diff --git a/wp-includes/version.php b/wp-includes/version.php index c40100342c..533ebf6e50 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.1-alpha-53896'; +$wp_version = '6.1-alpha-53897'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.