From 2a9209f2703b4e7108486394c03d6c872e5c5375 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Thu, 1 Jun 2023 15:05:07 -0400 Subject: [PATCH 1/3] Add testing for multikey tables --- features/search-replace.feature | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/features/search-replace.feature b/features/search-replace.feature index fb7baa53..745fcf38 100644 --- a/features/search-replace.feature +++ b/features/search-replace.feature @@ -1163,6 +1163,15 @@ 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;"` @@ -1170,13 +1179,13 @@ Feature: Do global search/replace 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` @@ -1205,6 +1214,15 @@ 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;"` @@ -1212,13 +1230,13 @@ Feature: Do global search/replace 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` From 498fc7e1d2ab09aa67d83a414fc38ad0d077778e Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Thu, 1 Jun 2023 23:09:40 -0400 Subject: [PATCH 2/3] Fix SQL syntax error in precise/regex replacement --- src/Search_Replace_Command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Search_Replace_Command.php b/src/Search_Replace_Command.php index a0a53e4a..79b2887f 100644 --- a/src/Search_Replace_Command.php +++ b/src/Search_Replace_Command.php @@ -612,7 +612,7 @@ static function ( $key ) { 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 = 'WHERE ' . implode( ' AND ', $where_key_conditions ); } if ( $this->verbose && 'table' === $this->format ) { From dddf61a6aae00f567d0c6c60cb6134d039e6c850 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Thu, 1 Jun 2023 15:06:04 -0400 Subject: [PATCH 3/3] Stop skipping rows within multikey tables --- src/Search_Replace_Command.php | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Search_Replace_Command.php b/src/Search_Replace_Command.php index 79b2887f..96234e58 100644 --- a/src/Search_Replace_Command.php +++ b/src/Search_Replace_Command.php @@ -604,14 +604,36 @@ 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_conditions[] = '( ' . implode( ' OR ', $next_key_conditions ) . ' )'; + $where_key = 'WHERE ' . implode( ' AND ', $where_key_conditions ); }