From 776542c8c467bad6d1f4d4170f080fc7eae94157 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 22:33:23 +0000 Subject: [PATCH 1/4] Initial plan From e88242ce654fd581b836fde0282332d93aecfad3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 22:38:41 +0000 Subject: [PATCH 2/4] Support table-qualified column names in --skip-columns and --include-columns Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/search-replace.feature | 11 +++++++++++ src/Search_Replace_Command.php | 16 +++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/features/search-replace.feature b/features/search-replace.feature index d6feee8a..99736a7f 100644 --- a/features/search-replace.feature +++ b/features/search-replace.feature @@ -41,6 +41,17 @@ Feature: Do global search/replace | Table | Column | Replacements | Type | | wp_posts | post_content | 0 | SQL | + When I run `wp search-replace foo bar --skip-columns=wp_posts.guid` + Then STDOUT should not contain: + """ + guid + """ + + When I run `wp search-replace foo bar --include-columns=wp_posts.post_content` + Then STDOUT should be a table containing rows: + | Table | Column | Replacements | Type | + | wp_posts | post_content | 0 | SQL | + @require-mysql Scenario: Multisite search/replace Given a WP multisite install diff --git a/src/Search_Replace_Command.php b/src/Search_Replace_Command.php index bbad1bdf..e3479ebc 100644 --- a/src/Search_Replace_Command.php +++ b/src/Search_Replace_Command.php @@ -184,11 +184,13 @@ class Search_Replace_Command extends WP_CLI_Command { * * [--skip-columns=] * : Do not perform the replacement on specific columns. Use commas to - * specify multiple columns. + * specify multiple columns. Table-qualified column names ("table.column") + * are supported to apply the skip to a specific table only. * * [--include-columns=] * : Perform the replacement on specific columns. Use commas to - * specify multiple columns. + * specify multiple columns. Table-qualified column names ("table.column") + * are supported to apply the inclusion to a specific table only. * * [--precise] * : Force the use of PHP (instead of SQL) for all columns. By default, the command @@ -510,11 +512,11 @@ public function __invoke( $args, $assoc_args ) { } foreach ( $columns as $col ) { - if ( ! empty( $this->include_columns ) && ! in_array( $col, $this->include_columns, true ) ) { + if ( ! empty( $this->include_columns ) && ! in_array( $col, $this->include_columns, true ) && ! in_array( $table . '.' . $col, $this->include_columns, true ) ) { continue; } - if ( in_array( $col, $this->skip_columns, true ) ) { + if ( in_array( $col, $this->skip_columns, true ) || in_array( $table . '.' . $col, $this->skip_columns, true ) ) { continue; } @@ -613,7 +615,11 @@ private function php_export_table( $table, $old, $new ) { $row_fields = array(); foreach ( $all_columns as $col ) { $value = $row->$col; - if ( $value && ! in_array( $col, $primary_keys, true ) && ! in_array( $col, $this->skip_columns, true ) ) { + if ( ! empty( $this->include_columns ) && ! in_array( $col, $this->include_columns, true ) && ! in_array( $table . '.' . $col, $this->include_columns, true ) ) { + $row_fields[ $col ] = $value; + continue; + } + if ( $value && ! in_array( $col, $primary_keys, true ) && ! in_array( $col, $this->skip_columns, true ) && ! in_array( $table . '.' . $col, $this->skip_columns, true ) ) { $new_value = $replacer->run( $value ); if ( $new_value !== $value ) { ++$col_counts[ $col ]; From 0191dc84dd8d0b77c88f87648e2d8f6b09408594 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 16 Mar 2026 09:51:03 +0100 Subject: [PATCH 3/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/Search_Replace_Command.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Search_Replace_Command.php b/src/Search_Replace_Command.php index e3479ebc..e1068e3f 100644 --- a/src/Search_Replace_Command.php +++ b/src/Search_Replace_Command.php @@ -603,8 +603,28 @@ private function php_export_table( $table, $old, $new ) { 'chunk_size' => $chunk_size, ); + // Determine which columns are actually eligible for search/replace and reporting. + $report_columns = array(); + foreach ( $all_columns as $col ) { + // Respect --include-columns if provided. + if ( ! empty( $this->include_columns ) + && ! in_array( $col, $this->include_columns, true ) + && ! in_array( $table . '.' . $col, $this->include_columns, true ) ) { + continue; + } + + // Skip primary key and explicitly skipped columns. + if ( in_array( $col, $primary_keys, true ) + || in_array( $col, $this->skip_columns, true ) + || in_array( $table . '.' . $col, $this->skip_columns, true ) ) { + continue; + } + + $report_columns[] = $col; + } + $replacer = new SearchReplacer( $old, $new, $this->recurse_objects, $this->regex, $this->regex_flags, $this->regex_delimiter, false, $this->regex_limit ); - $col_counts = array_fill_keys( $all_columns, 0 ); + $col_counts = array_fill_keys( $report_columns, 0 ); if ( $this->verbose && 'table' === $this->format ) { $this->start_time = microtime( true ); WP_CLI::log( sprintf( 'Checking: %s', $table ) ); From ada8f3169f4c9a4814217dbb8a52f40a03029e65 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 10:13:55 +0000 Subject: [PATCH 4/4] Fix CI failure: restore primary-key rows in export report Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/Search_Replace_Command.php | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/Search_Replace_Command.php b/src/Search_Replace_Command.php index e1068e3f..f08c6d5d 100644 --- a/src/Search_Replace_Command.php +++ b/src/Search_Replace_Command.php @@ -603,28 +603,8 @@ private function php_export_table( $table, $old, $new ) { 'chunk_size' => $chunk_size, ); - // Determine which columns are actually eligible for search/replace and reporting. - $report_columns = array(); - foreach ( $all_columns as $col ) { - // Respect --include-columns if provided. - if ( ! empty( $this->include_columns ) - && ! in_array( $col, $this->include_columns, true ) - && ! in_array( $table . '.' . $col, $this->include_columns, true ) ) { - continue; - } - - // Skip primary key and explicitly skipped columns. - if ( in_array( $col, $primary_keys, true ) - || in_array( $col, $this->skip_columns, true ) - || in_array( $table . '.' . $col, $this->skip_columns, true ) ) { - continue; - } - - $report_columns[] = $col; - } - $replacer = new SearchReplacer( $old, $new, $this->recurse_objects, $this->regex, $this->regex_flags, $this->regex_delimiter, false, $this->regex_limit ); - $col_counts = array_fill_keys( $report_columns, 0 ); + $col_counts = array_fill_keys( $all_columns, 0 ); if ( $this->verbose && 'table' === $this->format ) { $this->start_time = microtime( true ); WP_CLI::log( sprintf( 'Checking: %s', $table ) ); @@ -635,11 +615,12 @@ private function php_export_table( $table, $old, $new ) { $row_fields = array(); foreach ( $all_columns as $col ) { $value = $row->$col; - if ( ! empty( $this->include_columns ) && ! in_array( $col, $this->include_columns, true ) && ! in_array( $table . '.' . $col, $this->include_columns, true ) ) { - $row_fields[ $col ] = $value; - continue; - } - if ( $value && ! in_array( $col, $primary_keys, true ) && ! in_array( $col, $this->skip_columns, true ) && ! in_array( $table . '.' . $col, $this->skip_columns, true ) ) { + if ( $value + && ! in_array( $col, $primary_keys, true ) + && ! in_array( $col, $this->skip_columns, true ) + && ! in_array( $table . '.' . $col, $this->skip_columns, true ) + && ( empty( $this->include_columns ) || in_array( $col, $this->include_columns, true ) || in_array( $table . '.' . $col, $this->include_columns, true ) ) + ) { $new_value = $replacer->run( $value ); if ( $new_value !== $value ) { ++$col_counts[ $col ];