Skip to content

Commit

Permalink
Fix search-replace for tables with composite primary keys (#183)
Browse files Browse the repository at this point in the history
* Add testing for multikey tables

* Fix SQL syntax error in precise/regex replacement

* Stop skipping rows within multikey tables
  • Loading branch information
brandonpayton authored Jun 2, 2023
1 parent ce16c80 commit 1d5c77f
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 9 deletions.
26 changes: 22 additions & 4 deletions features/search-replace.feature
Original file line number Diff line number Diff line change
Expand Up @@ -1163,20 +1163,29 @@ Feature: Do global search/replace
index=`expr $index + 1`
done
echo "('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc');" >> test_db.sql
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
echo "INSERT INTO \`wp_123_test_multikey\` (\`key2\`,\`key3\`,\`text\`) VALUES" >> test_db.sql
index=1
while [[ $index -le 204 ]];
do
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
index=`expr $index + 1`
done
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
"""
And I run `bash create_sql_file.sh`
And I run `wp db query "SOURCE test_db.sql;"`

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

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

When I run `wp search-replace --dry-run 'abc' 'def' --all-tables-with-prefix --skip-columns=guid,domain --precise`
Expand Down Expand Up @@ -1205,20 +1214,29 @@ Feature: Do global search/replace
index=`expr $index + 1`
done
echo "('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc'),('abc');" >> test_db.sql
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
echo "INSERT INTO \`wp_123_test_multikey\` (\`key2\`,\`key3\`,\`text\`) VALUES" >> test_db.sql
index=1
while [[ $index -le 204 ]];
do
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
index=`expr $index + 1`
done
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
"""
And I run `bash create_sql_file.sh`
And I run `wp db query "SOURCE test_db.sql;"`

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

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

When I run `wp search-replace --dry-run 'abc' 'def' --all-tables-with-prefix --skip-columns=guid,domain --regex`
Expand Down
32 changes: 27 additions & 5 deletions src/Search_Replace_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -604,15 +604,37 @@ static function ( $key ) {
// Because we are ordering by primary keys from least to greatest,
// we can exclude previous chunks from consideration by adding greater-than conditions
// to insist the next chunk's keys must be greater than the last of this chunk's keys.
$last_keys = end( $rows );
$last_row = end( $rows );
$next_key_conditions = array();

// NOTE: For a composite key (X, Y, Z), selecting the next rows requires the following conditions:
// ( X = lastX AND Y = lastY AND Z > lastZ ) OR
// ( X = lastX AND Y > lastY ) OR
// ( X > lastX )
for ( $last_key_index = count( $primary_keys ) - 1; $last_key_index >= 0; $last_key_index-- ) {
$next_key_subconditions = array();

for ( $i = 0; $i <= $last_key_index; $i++ ) {
$k = $primary_keys[ $i ];
$v = $last_row->{ $k };

if ( $i < $last_key_index ) {
$next_key_subconditions[] = self::esc_sql_ident( $k ) . ' = ' . self::esc_sql_value( $v );
} else {
$next_key_subconditions[] = self::esc_sql_ident( $k ) . ' > ' . self::esc_sql_value( $v );
}
}

$next_key_conditions[] = '( ' . implode( ' AND ', $next_key_subconditions ) . ' )';
}

$where_key_conditions = array();
if ( $base_key_condition ) {
$where_key_conditions[] = $base_key_condition;
}
foreach ( (array) $last_keys as $k => $v ) {
$where_key_conditions[] = self::esc_sql_ident( $k ) . ' > ' . self::esc_sql_value( $v );
}
$where_key = 'WHERE ' . implode( 'AND', $where_key_conditions );
$where_key_conditions[] = '( ' . implode( ' OR ', $next_key_conditions ) . ' )';

$where_key = 'WHERE ' . implode( ' AND ', $where_key_conditions );
}

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

0 comments on commit 1d5c77f

Please sign in to comment.