Make WordPress Core


Ignore:
Timestamp:
08/15/2022 01:16:22 PM (2 years 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.

File:
1 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                        }
Note: See TracChangeset for help on using the changeset viewer.