WordPress.org

Make WordPress Core

Ticket #21212: 21212-utf8mb4.8.diff

File 21212-utf8mb4.8.diff, 12.4 KB (added by pento, 5 years ago)
  • src/wp-admin/includes/schema.php

     
    4444        // Engage multisite if in the middle of turning it on from network.php.
    4545        $is_multisite = is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK );
    4646
     47        // Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
     48        // As of 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which
     49        // used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
     50        $max_index_length = 191;
     51
    4752        // Blog specific tables.
    4853        $blog_tables = "CREATE TABLE $wpdb->terms (
    4954 term_id bigint(20) unsigned NOT NULL auto_increment,
     
    5156 slug varchar(200) NOT NULL default '',
    5257 term_group bigint(10) NOT NULL default 0,
    5358 PRIMARY KEY  (term_id),
    54  KEY slug (slug),
    55  KEY name (name)
     59 KEY slug (slug($max_index_length)),
     60 KEY name (name($max_index_length))
    5661) $charset_collate;
    5762CREATE TABLE $wpdb->term_taxonomy (
    5863 term_taxonomy_id bigint(20) unsigned NOT NULL auto_increment,
     
    7984  meta_value longtext,
    8085  PRIMARY KEY  (meta_id),
    8186  KEY comment_id (comment_id),
    82   KEY meta_key (meta_key)
     87  KEY meta_key (meta_key($max_index_length))
    8388) $charset_collate;
    8489CREATE TABLE $wpdb->comments (
    8590  comment_ID bigint(20) unsigned NOT NULL auto_increment,
     
    136141  meta_value longtext,
    137142  PRIMARY KEY  (meta_id),
    138143  KEY post_id (post_id),
    139   KEY meta_key (meta_key)
     144  KEY meta_key (meta_key($max_index_length))
    140145) $charset_collate;
    141146CREATE TABLE $wpdb->posts (
    142147  ID bigint(20) unsigned NOT NULL auto_increment,
     
    163168  post_mime_type varchar(100) NOT NULL default '',
    164169  comment_count bigint(20) NOT NULL default '0',
    165170  PRIMARY KEY  (ID),
    166   KEY post_name (post_name),
     171  KEY post_name (post_name($max_index_length)),
    167172  KEY type_status_date (post_type,post_status,post_date,ID),
    168173  KEY post_parent (post_parent),
    169174  KEY post_author (post_author)
     
    213218  meta_value longtext,
    214219  PRIMARY KEY  (umeta_id),
    215220  KEY user_id (user_id),
    216   KEY meta_key (meta_key)
     221  KEY meta_key (meta_key($max_index_length))
    217222) $charset_collate;\n";
    218223
    219224        // Global tables
     
    261266  domain varchar(200) NOT NULL default '',
    262267  path varchar(100) NOT NULL default '',
    263268  PRIMARY KEY  (id),
    264   KEY domain (domain,path)
     269  KEY domain (domain(140),path(51))
    265270) $charset_collate;
    266271CREATE TABLE $wpdb->sitemeta (
    267272  meta_id bigint(20) NOT NULL auto_increment,
     
    269274  meta_key varchar(255) default NULL,
    270275  meta_value longtext,
    271276  PRIMARY KEY  (meta_id),
    272   KEY meta_key (meta_key),
     277  KEY meta_key (meta_key($max_index_length)),
    273278  KEY site_id (site_id)
    274279) $charset_collate;
    275280CREATE TABLE $wpdb->signups (
     
    288293  KEY activation_key (activation_key),
    289294  KEY user_email (user_email),
    290295  KEY user_login_email (user_login,user_email),
    291   KEY domain_path (domain,path)
     296  KEY domain_path (domain(140),path(51))
    292297) $charset_collate;";
    293298
    294299        switch ( $scope ) {
  • src/wp-admin/includes/upgrade.php

     
    519519        if ( $wp_current_db_version < 29630 )
    520520                upgrade_400();
    521521
     522        if ( $wp_current_db_version < 31349 )
     523                upgrade_420();
     524
    522525        maybe_disable_link_manager();
    523526
    524527        maybe_disable_automattic_widgets();
     
    14071410}
    14081411
    14091412/**
     1413 * Execute changes made in WordPress 4.2.0.
     1414 *
     1415 * @since 4.2.0
     1416 */
     1417function upgrade_420() {
     1418        global $wp_current_db_version, $wpdb;
     1419
     1420        if ( $wp_current_db_version < 31349 && $wpdb->charset === 'utf8mb4' ) {
     1421                if ( is_multisite() ) {
     1422                        $tables = $wpdb->tables( 'blog' );
     1423                } else {
     1424                        $tables = $wpdb->tables( 'all' );
     1425                }
     1426
     1427                foreach ( $tables as $table ) {
     1428                        $results = $wpdb->get_results( "SHOW FULL COLUMNS FROM `$table`" );
     1429                        if ( ! $results ) {
     1430                                continue;
     1431                        }
     1432
     1433                        $has_utf8 = false;
     1434                        foreach ( $results as $column ) {
     1435                                if ( $column->Collation ) {
     1436                                        if ( 'utf8' === $column->Collation ) {
     1437                                                $has_utf8 = true;
     1438                                        } elseif ( 'utf8mb4' !== $column->Collation ) {
     1439                                                // Don't upgrade tables that have non-utf8 columns.
     1440                                                continue 2;
     1441                                        }
     1442                                }
     1443                        }
     1444
     1445                        if ( ! $has_utf8 ) {
     1446                                // Don't bother upgrading tables that don't have utf8 columns.
     1447                                continue;
     1448                        }
     1449
     1450                        $wpdb->query( "ALTER TABLE $table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" );
     1451                }
     1452        }
     1453}
     1454
     1455/**
    14101456 * Executes network-level upgrade routines.
    14111457 *
    14121458 * @since 3.0.0
     
    15021548                        update_site_option( 'illegal_names', $illegal_names );
    15031549                }
    15041550        }
     1551
     1552        // 4.2
     1553        if ( $wp_current_db_version < 31349 && $wpdb->charset === 'utf8mb4' ) {
     1554                if ( ! ( defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) && DO_NOT_UPGRADE_GLOBAL_TABLES ) ) {
     1555                        $wpdb->query( "ALTER TABLE $wpdb->site DROP INDEX domain, ADD INDEX domain(domain(140),path(51))" );
     1556                        $wpdb->query( "ALTER TABLE $wpdb->sitemeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" );
     1557                        $wpdb->query( "ALTER TABLE $wpdb->signups DROP INDEX domain, ADD INDEX domain(domain(140),path(51))" );
     1558
     1559                        $tables = $wpdb->tables( 'global' );
     1560
     1561                        foreach ( $tables as $table ) {
     1562                                $results = $wpdb->get_results( "SHOW FULL COLUMNS FROM `$table`" );
     1563                                if ( ! $results ) {
     1564                                        continue;
     1565                                }
     1566
     1567                                $has_utf8 = false;
     1568                                foreach ( $results as $column ) {
     1569                                        if ( $column->Collation ) {
     1570                                                if ( 'utf8' === $column->Collation ) {
     1571                                                        $has_utf8 = true;
     1572                                                } elseif ( 'utf8mb4' !== $column->Collation ) {
     1573                                                        // Don't upgrade tables that have non-utf8 columns.
     1574                                                        continue 2;
     1575                                                }
     1576                                        }
     1577                                }
     1578
     1579                                if ( ! $has_utf8 ) {
     1580                                        // Don't bother upgrading tables that don't have utf8 columns.
     1581                                        continue;
     1582                                }
     1583
     1584                                $wpdb->query( "ALTER TABLE $table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" );
     1585                        }
     1586                }
     1587        }
    15051588}
    15061589
    15071590//
     
    22842367                // dbDelta() can recreate but can't drop the index.
    22852368                $wpdb->query( "ALTER TABLE $wpdb->terms DROP INDEX slug" );
    22862369        }
     2370
     2371        // Upgrade versions prior to 4.2.
     2372        if ( $wp_current_db_version < 31349 ) {
     2373                // So that we can change tables to utf8mb4, we need to shorten the index lengths to less than 767 bytes
     2374                $wpdb->query( "ALTER TABLE $wpdb->usermeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" );
     2375                $wpdb->query( "ALTER TABLE $wpdb->terms DROP INDEX slug, ADD INDEX slug(slug(191))" );
     2376                $wpdb->query( "ALTER TABLE $wpdb->terms DROP INDEX name, ADD INDEX name(name(191))" );
     2377                $wpdb->query( "ALTER TABLE $wpdb->commentmeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" );
     2378                $wpdb->query( "ALTER TABLE $wpdb->postmeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" );
     2379                $wpdb->query( "ALTER TABLE $wpdb->posts DROP INDEX post_name, ADD INDEX post_name(post_name(191))" );
     2380        }
    22872381}
    22882382
    22892383/**
  • src/wp-admin/setup-config.php

     
    280280                        case 'DB_HOST'     :
    281281                                $config_file[ $line_num ] = "define('" . $constant . "'," . $padding . "'" . addcslashes( constant( $constant ), "\\'" ) . "');\r\n";
    282282                                break;
     283                        case 'DB_CHARSET'  :
     284                                if ( 'utf8mb4' === $wpdb->charset || ( ! $wpdb->charset && $wpdb->has_cap( 'utf8mb4' ) ) ) {
     285                                        $config_file[ $line_num ] = "define('" . $constant . "'," . $padding . "'utf8mb4');\r\n";
     286                                }
     287                                break;
    283288                        case 'AUTH_KEY'         :
    284289                        case 'SECURE_AUTH_KEY'  :
    285290                        case 'LOGGED_IN_KEY'    :
  • src/wp-includes/version.php

     
    1111 *
    1212 * @global int $wp_db_version
    1313 */
    14 $wp_db_version = 30133;
     14$wp_db_version = 31349;
    1515
    1616/**
    1717 * Holds the TinyMCE version
  • src/wp-includes/wp-db.php

     
    624624                        }
    625625                }
    626626
    627                 $this->init_charset();
    628 
    629627                $this->dbuser = $dbuser;
    630628                $this->dbpassword = $dbpassword;
    631629                $this->dbname = $dbname;
     
    717715        public function init_charset() {
    718716                if ( function_exists('is_multisite') && is_multisite() ) {
    719717                        $this->charset = 'utf8';
    720                         if ( defined( 'DB_COLLATE' ) && DB_COLLATE )
     718                        if ( defined( 'DB_COLLATE' ) && DB_COLLATE ) {
    721719                                $this->collate = DB_COLLATE;
    722                         else
     720                        } else {
    723721                                $this->collate = 'utf8_general_ci';
     722                        }
    724723                } elseif ( defined( 'DB_COLLATE' ) ) {
    725724                        $this->collate = DB_COLLATE;
    726725                }
    727726
    728                 if ( defined( 'DB_CHARSET' ) )
     727                if ( defined( 'DB_CHARSET' ) ) {
    729728                        $this->charset = DB_CHARSET;
     729                }
     730
     731                if ( ( $this->use_mysqli && ! ( $this->dbh instanceof mysqli ) )
     732                  || ( empty( $this->dbh ) || ! ( $this->dbh instanceof mysqli ) ) ) {
     733                        return;
     734                }
     735
     736                if ( 'utf8' === $this->charset && $this->has_cap( 'utf8mb4' ) ) {
     737                        $this->charset = 'utf8mb4';
     738                }
     739
     740                if ( 'utf8mb4' === $this->charset && ( ! $this->collate || stripos( $this->collate, 'utf8_' ) === 0 ) ) {
     741                        $this->collate = 'utf8mb4_unicode_ci';
     742                }
    730743        }
    731744
    732745        /**
     
    14761489
    14771490                        return false;
    14781491                } elseif ( $this->dbh ) {
     1492                        if ( ! $this->has_connected ) {
     1493                                $this->init_charset();
     1494                        }
     1495
    14791496                        $this->has_connected = true;
     1497
    14801498                        $this->set_charset( $this->dbh );
     1499
    14811500                        $this->ready = true;
    14821501                        $this->set_sql_mode();
    14831502                        $this->select( $this->dbname, $this->dbh );
     
    22492268         * Retrieves the character set for the given column.
    22502269         *
    22512270         * @since 4.2.0
    2252          * @access protected
     2271         * @access public
    22532272         *
    22542273         * @param string $table  Table name.
    22552274         * @param string $column Column name.
     
    22562275         * @return mixed Column character set as a string. False if the column has no
    22572276         *               character set. {@see WP_Error} object if there was an error.
    22582277         */
    2259         protected function get_col_charset( $table, $column ) {
     2278        public function get_col_charset( $table, $column ) {
    22602279                $tablekey = strtolower( $table );
    22612280                $columnkey = strtolower( $column );
    22622281
     
    23562375                        'gb2312'  => 'EUC-CN',
    23572376                        'ujis'    => 'EUC-JP',
    23582377                        'utf32'   => 'UTF-32',
    2359                         'utf8mb4' => 'UTF-8',
    23602378                );
    23612379
    23622380                $supported_charsets = array();
     
    23912409                                }
    23922410                        }
    23932411
    2394                         // utf8(mb3) can be handled by regex, which is a bunch faster than a DB lookup.
    2395                         if ( 'utf8' === $charset || 'utf8mb3' === $charset ) {
     2412                        // utf8 can be handled by regex, which is a bunch faster than a DB lookup.
     2413                        if ( 'utf8' === $charset || 'utf8mb3' === $charset || 'utf8mb4' === $charset ) {
    23962414                                $regex = '/
    23972415                                        (
    23982416                                                (?: [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
     
    24002418                                                |   \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
    24012419                                                |   [\xE1-\xEC][\x80-\xBF]{2}
    24022420                                                |   \xED[\x80-\x9F][\x80-\xBF]
    2403                                                 |   [\xEE-\xEF][\x80-\xBF]{2}
    2404                                                 ){1,50}                          # ...one or more times
     2421                                                |   [\xEE-\xEF][\x80-\xBF]{2}';
     2422
     2423                                if ( 'utf8mb4' === $charset) {
     2424                                        $regex .= '
     2425                                                |    \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
     2426                                                |    [\xF1-\xF3][\x80-\xBF]{3}
     2427                                                |    \xF4[\x80-\x8F][\x80-\xBF]{2}
     2428                                        ';
     2429                                }
     2430
     2431                                $regex .= '){1,50}                          # ...one or more times
    24052432                                        )
    24062433                                        | .                                  # anything else
    24072434                                        /x';
  • tests/phpunit/tests/db/charset.php

     
    130130        }
    131131
    132132        /**
    133          * @ ticket 21212
     133         * @ticket 21212
    134134         */
    135135        function test_process_fields_failure() {
    136136                global $wpdb;
    137                 $data = array( 'post_content' => "H€llo\xf0\x9f\x98\x88World¢" );
     137                // \xf0\xff\xff\xff is invalid in utf8 and utf8mb4.
     138                $data = array( 'post_content' => "H€llo\xf0\xff\xff\xffWorld¢" );
    138139                $this->assertFalse( self::$_wpdb->process_fields( $wpdb->posts, $data, null ) );
    139140        }
    140141
     
    436437         */
    437438        function test_invalid_characters_in_query() {
    438439                global $wpdb;
    439                 $this->assertFalse( $wpdb->query( "INSERT INTO {$wpdb->posts} (post_content) VALUES ('foo\xf0\x9f\x98\x88bar')" ) );
     440                $this->assertFalse( $wpdb->query( "INSERT INTO {$wpdb->posts} (post_content) VALUES ('foo\xf0\xff\xff\xffbar')" ) );
    440441        }
    441442}