Skip to content

Commit 654f271

Browse files
author
ocean90
committed
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. git-svn-id: https://develop.svn.wordpress.org/trunk@37583 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 3f662bd commit 654f271

File tree

2 files changed

+364
-12
lines changed

2 files changed

+364
-12
lines changed

src/wp-admin/includes/upgrade.php

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2184,10 +2184,11 @@ function dbDelta( $queries = '', $execute = true ) {
21842184
$flds = explode("\n", $qryline);
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 );
21932194

@@ -2202,14 +2203,98 @@ function dbDelta( $queries = '', $execute = true ) {
22022203
case 'key':
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
}
22152300

@@ -2243,7 +2328,7 @@ function dbDelta( $queries = '', $execute = true ) {
22432328

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
}
22492334
}
@@ -2253,7 +2338,7 @@ function dbDelta( $queries = '', $execute = true ) {
22532338
$default_value = $matches[1];
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
}
22592344
}
@@ -2306,17 +2391,19 @@ function dbDelta( $queries = '', $execute = true ) {
23062391
$index_string .= 'SPATIAL ';
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 = '';
23132398

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'].')';
23222409
}

0 commit comments

Comments
 (0)