Make WordPress Core

Changeset 53897


Ignore:
Timestamp:
08/15/2022 01:16:22 PM (19 months ago)
Author:
SergeyBiryukov
Message:

Database: Ignore display width for integer data types in dbDelta() on MySQL 8.0.17 or later.

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:

Follow-up to [1575], [18899], [37525], [47183], [47184].

Props SergeyBiryukov, pbearne, leewillis77, JavierCasares, desrosj, costdev, johnbillion.
Fixes #49364. See #51740.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/upgrade.php

    r53896 r53897  
    27142714 *
    27152715 * @since 1.5.0
     2716 * @since 6.1.0 Ignores display width for integer data types on MySQL 8.0.17 or later,
     2717 *              to match MySQL behavior. Note: This does not affect MariaDB.
    27162718 *
    27172719 * @global wpdb $wpdb WordPress database abstraction object.
     
    27902792    $text_fields = array( 'tinytext', 'text', 'mediumtext', 'longtext' );
    27912793    $blob_fields = array( 'tinyblob', 'blob', 'mediumblob', 'longblob' );
    2792 
    2793     $global_tables = $wpdb->tables( 'global' );
     2794    $int_fields  = array( 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint' );
     2795
     2796    $global_tables  = $wpdb->tables( 'global' );
     2797    $db_version     = $wpdb->db_version();
     2798    $db_server_info = $wpdb->db_server_info();
     2799
    27942800    foreach ( $cqueries as $table => $qry ) {
    27952801        // Upgrade global tables only for the main site. Don't upgrade at all if conditions are not optimal.
     
    29492955            $tablefield_type_lowercased  = strtolower( $tablefield->Type );
    29502956
     2957            $tablefield_type_without_parentheses = preg_replace(
     2958                '/'
     2959                . '(.+)'       // Field type, e.g. `int`.
     2960                . '\(\d*\)'    // Display width.
     2961                . '(.*)'       // Optional attributes, e.g. `unsigned`.
     2962                . '/',
     2963                '$1$2',
     2964                $tablefield_type_lowercased
     2965            );
     2966
     2967            // Get the type without attributes, e.g. `int`.
     2968            $tablefield_type_base = strtok( $tablefield_type_without_parentheses, ' ' );
     2969
    29512970            // If the table field exists in the field array...
    29522971            if ( array_key_exists( $tablefield_field_lowercased, $cfields ) ) {
     
    29562975                $fieldtype            = $matches[1];
    29572976                $fieldtype_lowercased = strtolower( $fieldtype );
     2977
     2978                $fieldtype_without_parentheses = preg_replace(
     2979                    '/'
     2980                    . '(.+)'       // Field type, e.g. `int`.
     2981                    . '\(\d*\)'    // Display width.
     2982                    . '(.*)'       // Optional attributes, e.g. `unsigned`.
     2983                    . '/',
     2984                    '$1$2',
     2985                    $fieldtype_lowercased
     2986                );
     2987
     2988                // Get the type without attributes, e.g. `int`.
     2989                $fieldtype_base = strtok( $fieldtype_without_parentheses, ' ' );
    29582990
    29592991                // Is actual field type different from the field type in query?
     
    29683000                    if ( in_array( $fieldtype_lowercased, $blob_fields, true ) && in_array( $tablefield_type_lowercased, $blob_fields, true ) ) {
    29693001                        if ( array_search( $fieldtype_lowercased, $blob_fields, true ) < array_search( $tablefield_type_lowercased, $blob_fields, true ) ) {
     3002                            $do_change = false;
     3003                        }
     3004                    }
     3005
     3006                    if ( in_array( $fieldtype_base, $int_fields, true ) && in_array( $tablefield_type_base, $int_fields, true )
     3007                        && $fieldtype_without_parentheses === $tablefield_type_without_parentheses
     3008                    ) {
     3009                        /*
     3010                         * MySQL 8.0.17 or later does not support display width for integer data types,
     3011                         * so if display width is the only difference, it can be safely ignored.
     3012                         * Note: This is specific to MySQL and does not affect MariaDB.
     3013                         */
     3014                        if ( version_compare( $db_version, '8.0.17', '>=' )
     3015                            && ! str_contains( $db_server_info, 'MariaDB' )
     3016                        ) {
    29703017                            $do_change = false;
    29713018                        }
  • trunk/tests/phpunit/tests/dbdelta.php

    r52010 r53897  
    2323
    2424    /**
    25      * Display width for BIGINT data type.
     25     * The database server version.
    2626     *
    27      * Prior to MySQL 8.0.17, default width of 20 digits was used: BIGINT(20).
    28      * Since MySQL 8.0.17, display width for integer data types is no longer supported.
    29      */
    30     protected $bigint_display_width = '';
     27     * @var string
     28     */
     29    private static $db_version;
     30
     31    /**
     32     * Full database server information.
     33     *
     34     * @var string
     35     */
     36    private static $db_server_info;
    3137
    3238    /**
     
    3541    public static function set_up_before_class() {
    3642
     43        global $wpdb;
     44
    3745        parent::set_up_before_class();
    3846
    3947        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
     48
     49        self::$db_version     = $wpdb->db_version();
     50        self::$db_server_info = $wpdb->db_server_info();
    4051    }
    4152
     
    4758        global $wpdb;
    4859
    49         $db_version = $wpdb->db_version();
    50 
    51         if ( version_compare( $db_version, '5.7', '<' ) ) {
     60        if ( version_compare( self::$db_version, '5.7', '<' ) ) {
    5261            // Prior to MySQL 5.7, InnoDB did not support FULLTEXT indexes, so MyISAM is used instead.
    5362            $this->db_engine = 'ENGINE=MyISAM';
    54         }
    55 
    56         if ( version_compare( $db_version, '8.0.17', '<' ) ) {
    57             // Prior to MySQL 8.0.17, default width of 20 digits was used: BIGINT(20).
    58             $this->bigint_display_width = '(20)';
    5963        }
    6064
     
    6468                CREATE TABLE {$wpdb->prefix}dbdelta_test (" .
    6569                    // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    66                     "id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     70                    'id bigint(20) NOT NULL AUTO_INCREMENT,
    6771                    column_1 varchar(255) NOT NULL,
    6872                    column_2 text,
     
    7175                    KEY key_1 (column_1(%d)),
    7276                    KEY compound_key (id,column_1(%d)),
    73                     FULLTEXT KEY fulltext_key (column_1)" .
     77                    FULLTEXT KEY fulltext_key (column_1)' .
    7478                    // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    7579                ") {$this->db_engine}
     
    110114        $updates = dbDelta(
    111115            "CREATE TABLE {$wpdb->prefix}dbdelta_create_test (
    112                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     116                id bigint(20) NOT NULL AUTO_INCREMENT,
    113117                column_1 varchar(255) NOT NULL,
    114118                PRIMARY KEY  (id)
     
    145149            "
    146150            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    147                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     151                id bigint(20) NOT NULL AUTO_INCREMENT,
    148152                column_1 varchar(255) NOT NULL,
    149153                PRIMARY KEY  (id),
     
    164168        global $wpdb;
    165169
    166         // id: bigint => int(11)
     170        // id: bigint(20) => int(11)
    167171        $updates = dbDelta(
    168172            "
     
    176180            "
    177181        );
     182
     183        $bigint_display_width = '(20)';
     184
     185        /*
     186         * MySQL 8.0.17 or later does not support display width for integer data types,
     187         * so if display width is the only difference, it can be safely ignored.
     188         * Note: This is specific to MySQL and does not affect MariaDB.
     189         */
     190        if ( version_compare( self::$db_version, '8.0.17', '>=' )
     191            && ! str_contains( self::$db_server_info, 'MariaDB' )
     192        ) {
     193            $bigint_display_width = '';
     194        }
    178195
    179196        $this->assertSame(
    180197            array(
    181198                "{$wpdb->prefix}dbdelta_test.id"
    182                     => "Changed type of {$wpdb->prefix}dbdelta_test.id from bigint{$this->bigint_display_width} to int(11)",
     199                    => "Changed type of {$wpdb->prefix}dbdelta_test.id from bigint{$bigint_display_width} to int(11)",
    183200            ),
    184201            $updates
     
    196213            "
    197214            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    198                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     215                id bigint(20) NOT NULL AUTO_INCREMENT,
    199216                column_1 varchar(255) NOT NULL,
    200217                extra_col longtext,
     
    231248            "
    232249            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    233                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     250                id bigint(20) NOT NULL AUTO_INCREMENT,
    234251                PRIMARY KEY  (id),
    235252                KEY key_1 (column_1($this->max_index_length)),
     
    255272            "
    256273            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    257                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     274                id bigint(20) NOT NULL AUTO_INCREMENT,
    258275                column_1 varchar(255) NOT NULL,
    259276                extra_col longtext,
     
    307324            "
    308325            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    309                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     326                id bigint(20) NOT NULL AUTO_INCREMENT,
    310327                column_1 varchar(255) NOT NULL,
    311328                PRIMARY KEY  (id),
     
    452469            "
    453470            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    454                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     471                id bigint(20) NOT NULL AUTO_INCREMENT,
    455472                column_1 varchar(255) NOT NULL,
    456473                column_2 tinytext,
     
    477494            "
    478495            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    479                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     496                id bigint(20) NOT NULL AUTO_INCREMENT,
    480497                column_1 varchar(255) NOT NULL,
    481498                column_2 text,
     
    502519            "
    503520            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    504                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     521                id bigint(20) NOT NULL AUTO_INCREMENT,
    505522                column_1 varchar(255) NOT NULL,
    506523                column_2 bigtext,
     
    533550            "
    534551            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    535                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     552                id bigint(20) NOT NULL AUTO_INCREMENT,
    536553                column_1 varchar(255) NOT NULL,
    537554                column_2 text,
     
    563580        $schema = "
    564581            CREATE TABLE {$wpdb->prefix}dbdelta_test2 (
    565                 `id` bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     582                `id` bigint(20) NOT NULL AUTO_INCREMENT,
    566583                `column_1` varchar(255) NOT NULL,
    567584                PRIMARY KEY  (id),
     
    586603        global $wpdb;
    587604
    588         $db_version = $wpdb->db_version();
    589 
    590         if ( version_compare( $db_version, '5.4', '<' ) ) {
     605        if ( version_compare( self::$db_version, '5.4', '<' ) ) {
    591606            $this->markTestSkipped( 'Spatial indices require MySQL 5.4 and above.' );
    592607        }
    593608
    594         $geomcollection_name = 'geomcollection';
    595 
    596         if ( version_compare( $db_version, '8.0.11', '<' ) ) {
    597             // Prior to MySQL 8.0.11, GeometryCollection data type name was used.
    598             $geomcollection_name = 'geometrycollection';
     609        $geometrycollection_name = 'geometrycollection';
     610
     611        if ( version_compare( self::$db_version, '8.0.11', '>=' )
     612            && ! str_contains( self::$db_server_info, 'MariaDB' )
     613        ) {
     614            /*
     615             * MySQL 8.0.11 or later uses GeomCollection data type name
     616             * as the preferred synonym for GeometryCollection.
     617             * Note: This is specific to MySQL and does not affect MariaDB.
     618             */
     619            $geometrycollection_name = 'geomcollection';
    599620        }
    600621
     
    602623            "
    603624            CREATE TABLE {$wpdb->prefix}spatial_index_test (
    604                 non_spatial bigint{$this->bigint_display_width} unsigned NOT NULL,
    605                 spatial_value {$geomcollection_name} NOT NULL,
     625                non_spatial bigint(20) unsigned NOT NULL,
     626                spatial_value {$geometrycollection_name} NOT NULL,
    606627                KEY non_spatial (non_spatial),
    607628                SPATIAL KEY spatial_key (spatial_value)
     
    619640            "
    620641            CREATE TABLE {$wpdb->prefix}spatial_index_test (
    621                 non_spatial bigint{$this->bigint_display_width} unsigned NOT NULL,
    622                 spatial_value {$geomcollection_name} NOT NULL,
    623                 spatial_value2 {$geomcollection_name} NOT NULL,
     642                non_spatial bigint(20) unsigned NOT NULL,
     643                spatial_value {$geometrycollection_name} NOT NULL,
     644                spatial_value2 {$geometrycollection_name} NOT NULL,
    624645                KEY non_spatial (non_spatial),
    625646                SPATIAL KEY spatial_key (spatial_value)
     
    649670        $schema = "
    650671            CREATE TABLE {$wpdb->prefix}dbdelta_test2 (
    651                 `id` bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     672                `id` bigint(20) NOT NULL AUTO_INCREMENT,
    652673                `references` varchar(255) NOT NULL,
    653674                PRIMARY KEY  (`id`),
     
    679700            "
    680701            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    681                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     702                id bigint(20) NOT NULL AUTO_INCREMENT,
    682703                column_1 varchar(255) NOT NULL,
    683704                column_2 text,
     
    709730     * @ticket 20263
    710731     */
    711     public function test_wp_get_db_schema_does_no_alter_queries_on_existing_install() {
     732    public function test_wp_get_db_schema_does_not_alter_queries_on_existing_install() {
    712733        $updates = dbDelta( wp_get_db_schema() );
    713734
     
    723744        $schema = "
    724745            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    725                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     746                id bigint(20) NOT NULL AUTO_INCREMENT,
    726747                column_1 varchar(255) NOT NULL,
    727748                column_2 text,
     
    762783            "
    763784            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    764                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     785                id bigint(20) NOT NULL AUTO_INCREMENT,
    765786                column_1 varchar(255) NOT NULL,
    766787                column_2 text,
     
    785806        $schema = "
    786807            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    787                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     808                id bigint(20) NOT NULL AUTO_INCREMENT,
    788809                column_1 varchar(255) NOT NULL,
    789810                column_2 text,
     
    820841            "
    821842            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    822                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     843                id bigint(20) NOT NULL AUTO_INCREMENT,
    823844                column_1 varchar(255) NOT NULL,
    824845                column_2 text,
     
    844865            "
    845866            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    846                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     867                id bigint(20) NOT NULL AUTO_INCREMENT,
    847868                column_1 varchar(255) NOT NULL,
    848869                column_2 text,
     
    868889            "
    869890            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    870                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     891                id bigint(20) NOT NULL AUTO_INCREMENT,
    871892                column_1 varchar(255) NOT NULL,
    872893                column_2 text,
     
    892913            "
    893914            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    894                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     915                id bigint(20) NOT NULL AUTO_INCREMENT,
    895916                column_1 varchar(255) NOT NULL,
    896917                column_2 text,
     
    916937            "
    917938            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    918                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     939                id bigint(20) NOT NULL AUTO_INCREMENT,
    919940                column_1 varchar(255) NOT NULL,
    920941                column_2 text,
     
    941962            "
    942963            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    943                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     964                id bigint(20) NOT NULL AUTO_INCREMENT,
    944965                column_1 varchar(255) NOT NULL,
    945966                column_2 text,
     
    966987            "
    967988            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    968                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     989                id bigint(20) NOT NULL AUTO_INCREMENT,
    969990                column_1 varchar(255) NOT NULL,
    970991                column_2 text,
     
    9891010            "
    9901011            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    991                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     1012                id bigint(20) NOT NULL AUTO_INCREMENT,
    9921013                column_1 varchar(255) NOT NULL,
    9931014                column_2 text,
     
    10071028            "
    10081029            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    1009                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     1030                id bigint(20) NOT NULL AUTO_INCREMENT,
    10101031                column_1 varchar(255) NOT NULL,
    10111032                column_2 text,
     
    10251046            "
    10261047            CREATE TABLE {$wpdb->prefix}dbdelta_test (
    1027                 id bigint{$this->bigint_display_width} NOT NULL AUTO_INCREMENT,
     1048                id bigint(20) NOT NULL AUTO_INCREMENT,
    10281049                column_1 varchar(255) NOT NULL,
    10291050                column_2 text,
Note: See TracChangeset for help on using the changeset viewer.