Make WordPress Core


Ignore:
Timestamp:
05/27/2016 11:56:20 AM (10 years ago)
Author:
ocean90
Message:

Database: Normalize index definitions in dbDelta().

dbDelta() compares the index definitions against the result of SHOW INDEX FROM $table_name. This requires a specific format so indices are not unnecessarily re-created. This format wasn't ensured, until now.

  • Parse the raw index definition to extract the type, name and columns so a normalized definition can be built (#20263, #34873).
  • Standardize on uppercase types (#34871) and on 'KEY'. 'INDEX' is only a synonym for 'KEY'.
  • Escape index names with backticks (#20263).
  • Normalize columns: Ignore ASC and DESC definitions (#34959), remove whitespaces (#34869) and escape column names with backticks (#20263).
  • Add backticks to all index change queries (#20263).

Props ocean90, pento, kurtpayne.
Fixes #20263, #34869, #34871, #34873, #34959.

File:
1 edited

Legend:

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

    r37574 r37583  
    21852185
    21862186                // For every field line specified in the query.
    2187                 foreach ($flds as $fld) {
     2187                foreach ( $flds as $fld ) {
     2188                        $fld = trim( $fld, " \t\n\r\0\x0B," ); // Default trim characters, plus ','.
    21882189
    21892190                        // Extract the field name.
    2190                         preg_match("|^([^ ]*)|", trim($fld), $fvals);
     2191                        preg_match( '|^([^ ]*)|', $fld, $fvals );
    21912192                        $fieldname = trim( $fvals[1], '`' );
    21922193                        $fieldname_lowercased = strtolower( $fieldname );
     
    22032204                                case 'spatial':
    22042205                                        $validfield = false;
    2205                                         $indices[] = trim(trim($fld), ", \n");
     2206
     2207                                        /*
     2208                                         * Normalize the index definition.
     2209                                         *
     2210                                         * This is done so the definition can be compared against the result of a
     2211                                         * `SHOW INDEX FROM $table_name` query which returns the current table
     2212                                         * index information.
     2213                                         */
     2214
     2215                                        // Extract type, name and columns from the definition.
     2216                                        preg_match(
     2217                                                  '/^'
     2218                                                .   '(?P<index_type>'             // 1) Type of the index.
     2219                                                .       'PRIMARY\s+KEY|(?:UNIQUE|FULLTEXT|SPATIAL)\s+(?:KEY|INDEX)|KEY|INDEX'
     2220                                                .   ')'
     2221                                                .   '\s+'                         // Followed by at least one white space character.
     2222                                                .   '(?:'                         // Name of the index. Optional if type is PRIMARY KEY.
     2223                                                .       '`?'                      // Name can be escaped with a backtick.
     2224                                                .           '(?P<index_name>'     // 2) Name of the index.
     2225                                                .               '(?:[0-9a-zA-Z$_-]|[\xC2-\xDF][\x80-\xBF])+'
     2226                                                .           ')'
     2227                                                .       '`?'                      // Name can be escaped with a backtick.
     2228                                                .       '\s+'                     // Followed by at least one white space character.
     2229                                                .   ')*'
     2230                                                .   '\('                          // Opening bracket for the columns.
     2231                                                .       '(?P<index_columns>'
     2232                                                .           '.+?'                 // 3) Column names, index prefixes, and orders.
     2233                                                .       ')'
     2234                                                .   '\)'                          // Closing bracket for the columns.
     2235                                                . '$/im',
     2236                                                $fld,
     2237                                                $index_matches
     2238                                        );
     2239
     2240                                        // Uppercase the index type and normalize space characters.
     2241                                        $index_type = strtoupper( preg_replace( '/\s+/', ' ', trim( $index_matches['index_type'] ) ) );
     2242
     2243                                        // 'INDEX' is a synonym for 'KEY', standardize on 'KEY'.
     2244                                        $index_type = str_replace( 'INDEX', 'KEY', $index_type );
     2245
     2246                                        // Escape the index name with backticks. An index for a primary key has no name.
     2247                                        $index_name = ( 'PRIMARY KEY' === $index_type ) ? '' : '`' . $index_matches['index_name'] . '`';
     2248
     2249                                        // Parse the columns. Multiple columns are separated by a comma.
     2250                                        $index_columns = array_map( 'trim', explode( ',', $index_matches['index_columns'] ) );
     2251
     2252                                        // Normalize columns.
     2253                                        foreach ( $index_columns as &$index_column ) {
     2254                                                // Extract column name and number of indexed characters (sub_part).
     2255                                                preg_match(
     2256                                                          '/'
     2257                                                        .   '`?'                      // Name can be escaped with a backtick.
     2258                                                        .       '(?P<column_name>'    // 1) Name of the column.
     2259                                                        .           '(?:[0-9a-zA-Z$_-]|[\xC2-\xDF][\x80-\xBF])+'
     2260                                                        .       ')'
     2261                                                        .   '`?'                      // Name can be escaped with a backtick.
     2262                                                        .   '(?:'                     // Optional sub part.
     2263                                                        .       '\s*'                 // Optional white space character between name and opening bracket.
     2264                                                        .       '\('                  // Opening bracket for the sub part.
     2265                                                        .           '\s*'             // Optional white space character after opening bracket.
     2266                                                        .           '(?P<sub_part>'
     2267                                                        .               '\d+'         // 2) Number of indexed characters.
     2268                                                        .           ')'
     2269                                                        .           '\s*'             // Optional white space character before closing bracket.
     2270                                                        .        '\)'                 // Closing bracket for the sub part.
     2271                                                        .   ')?'
     2272                                                        . '/',
     2273                                                        $index_column,
     2274                                                        $index_column_matches
     2275                                                );
     2276
     2277                                                // Escape the column name with backticks.
     2278                                                $index_column = '`' . $index_column_matches['column_name'] . '`';
     2279
     2280                                                // Append the optional sup part with the number of indexed characters.
     2281                                                if ( isset( $index_column_matches['sub_part'] ) ) {
     2282                                                        $index_column .= '(' . $index_column_matches['sub_part'] . ')';
     2283                                                }
     2284                                        }
     2285
     2286                                        // Build the normalized index definition and add it to the list of indices.
     2287                                        $indices[] = "{$index_type} {$index_name} (" . implode( ',', $index_columns ) . ")";
     2288
     2289                                        // Destroy no longer needed variables.
     2290                                        unset( $index_column, $index_column_matches, $index_matches, $index_type, $index_name, $index_columns );
     2291
    22062292                                        break;
    22072293                        }
    2208                         $fld = trim( $fld );
    22092294
    22102295                        // If it's a valid field, add it to the field array.
    22112296                        if ( $validfield ) {
    2212                                 $cfields[ $fieldname_lowercased ] = trim( $fld, ", \n" );
     2297                                $cfields[ $fieldname_lowercased ] = $fld;
    22132298                        }
    22142299                }
     
    22442329                                        if ( $do_change ) {
    22452330                                                // Add a query to change the column type.
    2246                                                 $cqueries[] = "ALTER TABLE {$table} CHANGE COLUMN {$tablefield->Field} " . $cfields[ $tablefield_field_lowercased ];
     2331                                                $cqueries[] = "ALTER TABLE {$table} CHANGE COLUMN `{$tablefield->Field}` " . $cfields[ $tablefield_field_lowercased ];
    22472332                                                $for_update[$table.'.'.$tablefield->Field] = "Changed type of {$table}.{$tablefield->Field} from {$tablefield->Type} to {$fieldtype}";
    22482333                                        }
     
    22542339                                        if ($tablefield->Default != $default_value) {
    22552340                                                // Add a query to change the column's default value
    2256                                                 $cqueries[] = "ALTER TABLE {$table} ALTER COLUMN {$tablefield->Field} SET DEFAULT '{$default_value}'";
     2341                                                $cqueries[] = "ALTER TABLE {$table} ALTER COLUMN `{$tablefield->Field}` SET DEFAULT '{$default_value}'";
    22572342                                                $for_update[$table.'.'.$tablefield->Field] = "Changed default value of {$table}.{$tablefield->Field} from {$tablefield->Default} to {$default_value}";
    22582343                                        }
     
    23072392                                }
    23082393                                $index_string .= 'KEY ';
    2309                                 if ($index_name != 'PRIMARY') {
    2310                                         $index_string .= $index_name;
     2394                                if ( 'PRIMARY' !== $index_name  ) {
     2395                                        $index_string .= '`' . $index_name . '`';
    23112396                                }
    23122397                                $index_columns = '';
     
    23142399                                // For each column in the index.
    23152400                                foreach ($index_data['columns'] as $column_data) {
    2316                                         if ($index_columns != '') $index_columns .= ',';
     2401                                        if ( $index_columns != '' ) {
     2402                                                $index_columns .= ',';
     2403                                        }
    23172404
    23182405                                        // Add the field to the column list string.
    2319                                         $index_columns .= $column_data['fieldname'];
     2406                                        $index_columns .= '`' . $column_data['fieldname'] . '`';
    23202407                                        if ($column_data['subpart'] != '') {
    23212408                                                $index_columns .= '('.$column_data['subpart'].')';
Note: See TracChangeset for help on using the changeset viewer.