Skip to content

Commit 1d5c77f

Browse files
Fix search-replace for tables with composite primary keys (#183)
* Add testing for multikey tables * Fix SQL syntax error in precise/regex replacement * Stop skipping rows within multikey tables
1 parent ce16c80 commit 1d5c77f

File tree

2 files changed

+49
-9
lines changed

2 files changed

+49
-9
lines changed

features/search-replace.feature

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,20 +1163,29 @@ Feature: Do global search/replace
11631163
index=`expr $index + 1`
11641164
done
11651165
echo "('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc');" >> test_db.sql
1166+
echo "CREATE TABLE \`wp_123_test_multikey\` (\`key1\` INT(5) UNSIGNED NOT NULL AUTO_INCREMENT, \`key2\` INT(5) UNSIGNED NOT NULL, \`key3\` INT(5) UNSIGNED NOT NULL, \`text\` TEXT, PRIMARY KEY (\`key1\`,\`key2\`,\`key3\`) );" >> test_db.sql
1167+
echo "INSERT INTO \`wp_123_test_multikey\` (\`key2\`,\`key3\`,\`text\`) VALUES" >> test_db.sql
1168+
index=1
1169+
while [[ $index -le 204 ]];
1170+
do
1171+
echo "(0,0,'abc'),(1,1,'abc'),(2,2,'abc'),(3,3,'abc'),(4,4,'abc'),(5,0,'abc'),(6,1,'abc'),(7,2,'abc'),(8,3,'abc'),(9,4,'abc')," >> test_db.sql
1172+
index=`expr $index + 1`
1173+
done
1174+
echo "(0,0,'abc'),(1,1,'abc'),(2,2,'abc'),(3,3,'abc'),(4,4,'abc'),(5,0,'abc'),(6,1,'abc'),(7,2,'abc'),(8,3,'abc'),(9,4,'abc');" >> test_db.sql
11661175
"""
11671176
And I run `bash create_sql_file.sh`
11681177
And I run `wp db query "SOURCE test_db.sql;"`
11691178

11701179
When I run `wp search-replace --dry-run 'abc' 'def' --all-tables-with-prefix --skip-columns=guid,domain --precise`
11711180
Then STDOUT should contain:
11721181
"""
1173-
Success: 2000 replacements to be made.
1182+
Success: 4050 replacements to be made.
11741183
"""
11751184

11761185
When I run `wp search-replace 'abc' 'def' --all-tables-with-prefix --skip-columns=guid,domain --precise`
11771186
Then STDOUT should contain:
11781187
"""
1179-
Success: Made 2000 replacements.
1188+
Success: Made 4050 replacements.
11801189
"""
11811190

11821191
When I run `wp search-replace --dry-run 'abc' 'def' --all-tables-with-prefix --skip-columns=guid,domain --precise`
@@ -1205,20 +1214,29 @@ Feature: Do global search/replace
12051214
index=`expr $index + 1`
12061215
done
12071216
echo "('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc');" >> test_db.sql
1217+
echo "CREATE TABLE \`wp_123_test_multikey\` (\`key1\` INT(5) UNSIGNED NOT NULL AUTO_INCREMENT, \`key2\` INT(5) UNSIGNED NOT NULL, \`key3\` INT(5) UNSIGNED NOT NULL, \`text\` TEXT, PRIMARY KEY (\`key1\`,\`key2\`,\`key3\`) );" >> test_db.sql
1218+
echo "INSERT INTO \`wp_123_test_multikey\` (\`key2\`,\`key3\`,\`text\`) VALUES" >> test_db.sql
1219+
index=1
1220+
while [[ $index -le 204 ]];
1221+
do
1222+
echo "(0,0,'abc'),(1,1,'abc'),(2,2,'abc'),(3,3,'abc'),(4,4,'abc'),(5,0,'abc'),(6,1,'abc'),(7,2,'abc'),(8,3,'abc'),(9,4,'abc')," >> test_db.sql
1223+
index=`expr $index + 1`
1224+
done
1225+
echo "(0,0,'abc'),(1,1,'abc'),(2,2,'abc'),(3,3,'abc'),(4,4,'abc'),(5,0,'abc'),(6,1,'abc'),(7,2,'abc'),(8,3,'abc'),(9,4,'abc');" >> test_db.sql
12081226
"""
12091227
And I run `bash create_sql_file.sh`
12101228
And I run `wp db query "SOURCE test_db.sql;"`
12111229

12121230
When I run `wp search-replace --dry-run 'abc' 'def' --all-tables-with-prefix --skip-columns=guid,domain --regex`
12131231
Then STDOUT should contain:
12141232
"""
1215-
Success: 2000 replacements to be made.
1233+
Success: 4050 replacements to be made.
12161234
"""
12171235

12181236
When I run `wp search-replace 'abc' 'def' --all-tables-with-prefix --skip-columns=guid,domain --regex`
12191237
Then STDOUT should contain:
12201238
"""
1221-
Success: Made 2000 replacements.
1239+
Success: Made 4050 replacements.
12221240
"""
12231241

12241242
When I run `wp search-replace --dry-run 'abc' 'def' --all-tables-with-prefix --skip-columns=guid,domain --regex`

src/Search_Replace_Command.php

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -604,15 +604,37 @@ static function ( $key ) {
604604
// Because we are ordering by primary keys from least to greatest,
605605
// we can exclude previous chunks from consideration by adding greater-than conditions
606606
// to insist the next chunk's keys must be greater than the last of this chunk's keys.
607-
$last_keys = end( $rows );
607+
$last_row = end( $rows );
608+
$next_key_conditions = array();
609+
610+
// NOTE: For a composite key (X, Y, Z), selecting the next rows requires the following conditions:
611+
// ( X = lastX AND Y = lastY AND Z > lastZ ) OR
612+
// ( X = lastX AND Y > lastY ) OR
613+
// ( X > lastX )
614+
for ( $last_key_index = count( $primary_keys ) - 1; $last_key_index >= 0; $last_key_index-- ) {
615+
$next_key_subconditions = array();
616+
617+
for ( $i = 0; $i <= $last_key_index; $i++ ) {
618+
$k = $primary_keys[ $i ];
619+
$v = $last_row->{ $k };
620+
621+
if ( $i < $last_key_index ) {
622+
$next_key_subconditions[] = self::esc_sql_ident( $k ) . ' = ' . self::esc_sql_value( $v );
623+
} else {
624+
$next_key_subconditions[] = self::esc_sql_ident( $k ) . ' > ' . self::esc_sql_value( $v );
625+
}
626+
}
627+
628+
$next_key_conditions[] = '( ' . implode( ' AND ', $next_key_subconditions ) . ' )';
629+
}
630+
608631
$where_key_conditions = array();
609632
if ( $base_key_condition ) {
610633
$where_key_conditions[] = $base_key_condition;
611634
}
612-
foreach ( (array) $last_keys as $k => $v ) {
613-
$where_key_conditions[] = self::esc_sql_ident( $k ) . ' > ' . self::esc_sql_value( $v );
614-
}
615-
$where_key = 'WHERE ' . implode( 'AND', $where_key_conditions );
635+
$where_key_conditions[] = '( ' . implode( ' OR ', $next_key_conditions ) . ' )';
636+
637+
$where_key = 'WHERE ' . implode( ' AND ', $where_key_conditions );
616638
}
617639

618640
if ( $this->verbose && 'table' === $this->format ) {

0 commit comments

Comments
 (0)