From fd762a70aa3f8db8635534af56a81bf82821144c Mon Sep 17 00:00:00 2001 From: Bernhard Kau Date: Fri, 10 Nov 2023 19:08:28 +0100 Subject: [PATCH 1/5] Enable formatter for search command --- features/db-search.feature | 24 ++++++++++++++ src/DB_Command.php | 66 +++++++++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/features/db-search.feature b/features/db-search.feature index 8e929d00..923ab14b 100644 --- a/features/db-search.feature +++ b/features/db-search.feature @@ -1066,3 +1066,27 @@ Feature: Search through the database :aöXYXYX """ And STDERR should be empty + + Scenario: Search for a string and output the format as a table + Given a WP install + + When I run `wp db search example.com --format=csv` + Then STDOUT should contain: + """ + wp_options,option_value,option_id,14,mail.example.com + wp_options,option_value,option_id,15,login@example.com + """ + + When I try `wp db search example.com --format=ids` + Then STDERR should be: + """ + Error: The "ids" format can only be used for a single table. + """ + And STDOUT should be empty + And the return code should be 1 + + When I run `wp db search example.com wp_options --format=ids` + Then STDOUT should contain: + """ + 14 15 + """ diff --git a/src/DB_Command.php b/src/DB_Command.php index 484d259c..4d6471b7 100644 --- a/src/DB_Command.php +++ b/src/DB_Command.php @@ -1208,10 +1208,10 @@ public function prefix() { * : Output the 'table:column' line once before all matching row lines in the table column rather than before each matching row. * * [--one_line] - * : Place the 'table:column' output on the same line as the row id and match ('table:column:id:match'). Overrides --table_column_once. + * : Deprecated: use `--format` instead. Place the 'table:column' output on the same line as the row id and match ('table:column:id:match'). Overrides --table_column_once. * * [--matches_only] - * : Only output the string matches (including context). No 'table:column's or row ids are outputted. + * : Deprecated: use `--format` instead. Only output the string matches (including context). No 'table:column's or row ids are outputted. * * [--stats] * : Output stats on the number of matches found, time taken, tables/columns/rows searched, tables skipped. @@ -1225,6 +1225,12 @@ public function prefix() { * [--match_color=] * : Percent color code to use for the match (unless both before and after context are 0, when no color code is used). For a list of available percent color codes, see below. Default '%3%k' (black on a mustard background). * + * [--fields=] + * : Get a specific subset of the fields. + * + * [--format=] + * : Render output in a particular format. + * * The percent color codes available are: * * | Code | Color @@ -1293,6 +1299,21 @@ public function prefix() { * # SQL search and delete records from database table 'wp_options' where 'option_name' match 'foo' * wp db query "DELETE from wp_options where option_id in ($(wp db query "SELECT GROUP_CONCAT(option_id SEPARATOR ',') from wp_options where option_name like '%foo%';" --silent --skip-column-names))" * + * # Search for a string and print the result as a table + * $ wp db search https://localhost:8889 --format=table + * +------------+--------------+-----------+----+-----------------------------+ + * | table | column | key | ID | match | + * +------------+--------------+-----------+----+-----------------------------+ + * | wp_options | option_value | option_id | 1 | https://localhost:8889 | + * | wp_options | option_value | option_id | 2 | https://localhost:8889 | + * | wp_posts | guid | ID | 1 | https://localhost:8889/?p=1 | + * | wp_users | user_url | ID | 1 | https://localhost:8889 | + * +------------+--------------+-----------+----+-----------------------------+ + * + * # Search for a string and get only the IDs (only works for a single table) + * $ wp db search https://localhost:8889 wp_options --format=ids + * 1 2 + * * @when after_wp_load */ public function search( $args, $assoc_args ) { @@ -1332,6 +1353,8 @@ public function search( $args, $assoc_args ) { $one_line = Utils\get_flag_value( $assoc_args, 'one_line', false ); $matches_only = Utils\get_flag_value( $assoc_args, 'matches_only', false ); $stats = Utils\get_flag_value( $assoc_args, 'stats', false ); + $fields = Utils\get_flag_value( $assoc_args, 'fields' ); + $format = Utils\get_flag_value( $assoc_args, 'format' ); $column_count = 0; $row_count = 0; @@ -1366,6 +1389,8 @@ public function search( $args, $assoc_args ) { $tables = Utils\wp_get_table_names( $args, $assoc_args ); + $search_results = []; + $start_search_time = microtime( true ); foreach ( $tables as $table ) { @@ -1409,7 +1434,7 @@ public function search( $args, $assoc_args ) { foreach ( $results as $result ) { $col_val = $result->$column; if ( preg_match_all( $search_regex, $col_val, $matches, PREG_OFFSET_CAPTURE ) ) { - if ( ! $matches_only && ( ! $table_column_once || ! $outputted_table_column_once ) && ! $one_line ) { + if ( ! $format && ! $matches_only && ( ! $table_column_once || ! $outputted_table_column_once ) && ! $one_line ) { WP_CLI::log( $table_column_val ); $outputted_table_column_once = true; } @@ -1457,13 +1482,46 @@ public function search( $args, $assoc_args ) { $match_count += $match_cnt; $col_val = implode( ' [...] ', $bits ); - WP_CLI::log( $matches_only ? $col_val : ( $one_line ? "{$table_column_val}:{$pk_val}{$col_val}" : "{$pk_val}{$col_val}" ) ); + if ( $format ) { + $search_results[] = [ + 'table' => $table, + 'column' => $column, + 'key' => $primary_key, + 'ID' => $result->$primary_key, + // Remove the colors for the format output. + 'match' => str_replace( [ $colors['match'][0], $colors['match'][1] ], [ '', '' ], $col_val ), + ]; + } else { + WP_CLI::log( $matches_only ? $col_val : ( $one_line ? "{$table_column_val}:{$pk_val}{$col_val}" : "{$pk_val}{$col_val}" ) ); + } } } } } } + if ( $format ) { + $formatter_args = [ + 'format' => $format, + ]; + $formatter_fields = [ 'table', 'column', 'key', 'ID', 'match' ]; + + if ( $fields ) { + $fields = explode( ',', $assoc_args['fields'] ); + $formatter_fields = array_values( array_intersect( $formatter_fields, $fields ) ); + } + + if ( in_array( $format, [ 'ids', 'count' ], true ) ) { + if ( count( $tables ) > 1 ) { + WP_CLI::error( 'The "ids" format can only be used for a single table.' ); + } + $search_results = array_column( $search_results, 'ID' ); + } + + $formatter = new Formatter( $formatter_args, $formatter_fields ); + $formatter->display_items( $search_results ); + } + if ( $stats ) { $table_count = count( $tables ); $skipped_count = count( $skipped ); From 1c9b2e862d578d37a6505c4f0b4aee2029f78c15 Mon Sep 17 00:00:00 2001 From: Bernhard Kau Date: Fri, 26 Apr 2024 16:19:01 +0200 Subject: [PATCH 2/5] Use `value` instead of `ID` for the primary key column --- src/DB_Command.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/DB_Command.php b/src/DB_Command.php index 4d6471b7..e174e216 100644 --- a/src/DB_Command.php +++ b/src/DB_Command.php @@ -1301,14 +1301,14 @@ public function prefix() { * * # Search for a string and print the result as a table * $ wp db search https://localhost:8889 --format=table - * +------------+--------------+-----------+----+-----------------------------+ - * | table | column | key | ID | match | - * +------------+--------------+-----------+----+-----------------------------+ - * | wp_options | option_value | option_id | 1 | https://localhost:8889 | - * | wp_options | option_value | option_id | 2 | https://localhost:8889 | - * | wp_posts | guid | ID | 1 | https://localhost:8889/?p=1 | - * | wp_users | user_url | ID | 1 | https://localhost:8889 | - * +------------+--------------+-----------+----+-----------------------------+ + * +------------+--------------+-----------+-------+-----------------------------+ + * | table | column | key | value | match | + * +------------+--------------+-----------+-------+-----------------------------+ + * | wp_options | option_value | option_id | 1 | https://localhost:8889 | + * | wp_options | option_value | option_id | 2 | https://localhost:8889 | + * | wp_posts | guid | ID | 1 | https://localhost:8889/?p=1 | + * | wp_users | user_url | ID | 1 | https://localhost:8889 | + * +------------+--------------+-----------+-------+-----------------------------+ * * # Search for a string and get only the IDs (only works for a single table) * $ wp db search https://localhost:8889 wp_options --format=ids @@ -1487,7 +1487,7 @@ public function search( $args, $assoc_args ) { 'table' => $table, 'column' => $column, 'key' => $primary_key, - 'ID' => $result->$primary_key, + 'value' => $result->$primary_key, // Remove the colors for the format output. 'match' => str_replace( [ $colors['match'][0], $colors['match'][1] ], [ '', '' ], $col_val ), ]; @@ -1504,7 +1504,7 @@ public function search( $args, $assoc_args ) { $formatter_args = [ 'format' => $format, ]; - $formatter_fields = [ 'table', 'column', 'key', 'ID', 'match' ]; + $formatter_fields = [ 'table', 'column', 'key', 'value', 'match' ]; if ( $fields ) { $fields = explode( ',', $assoc_args['fields'] ); @@ -1515,7 +1515,7 @@ public function search( $args, $assoc_args ) { if ( count( $tables ) > 1 ) { WP_CLI::error( 'The "ids" format can only be used for a single table.' ); } - $search_results = array_column( $search_results, 'ID' ); + $search_results = array_column( $search_results, 'value' ); } $formatter = new Formatter( $formatter_args, $formatter_fields ); From 531b897b9e8740f104a094cda1b5f00a7b0d7e98 Mon Sep 17 00:00:00 2001 From: Bernhard Kau Date: Fri, 26 Apr 2024 17:24:40 +0200 Subject: [PATCH 3/5] Rename and reorder the columns --- features/db-search.feature | 4 ++-- src/DB_Command.php | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/features/db-search.feature b/features/db-search.feature index 923ab14b..5295a688 100644 --- a/features/db-search.feature +++ b/features/db-search.feature @@ -1073,8 +1073,8 @@ Feature: Search through the database When I run `wp db search example.com --format=csv` Then STDOUT should contain: """ - wp_options,option_value,option_id,14,mail.example.com - wp_options,option_value,option_id,15,login@example.com + wp_options,option_value,mail.example.com,option_id,14 + wp_options,option_value,login@example.com,option_id,15 """ When I try `wp db search example.com --format=ids` diff --git a/src/DB_Command.php b/src/DB_Command.php index e174e216..3c79ca28 100644 --- a/src/DB_Command.php +++ b/src/DB_Command.php @@ -1484,12 +1484,16 @@ public function search( $args, $assoc_args ) { if ( $format ) { $search_results[] = [ - 'table' => $table, - 'column' => $column, - 'key' => $primary_key, - 'value' => $result->$primary_key, + 'table' => $table, + 'column' => $column, // Remove the colors for the format output. - 'match' => str_replace( [ $colors['match'][0], $colors['match'][1] ], [ '', '' ], $col_val ), + 'match' => str_replace( + [ $colors['match'][0], $colors['match'][1] ], + [ '','' ], + $col_val + ), + 'primary_key_name' => $primary_key, + 'primary_key_value' => $result->$primary_key, ]; } else { WP_CLI::log( $matches_only ? $col_val : ( $one_line ? "{$table_column_val}:{$pk_val}{$col_val}" : "{$pk_val}{$col_val}" ) ); @@ -1504,7 +1508,7 @@ public function search( $args, $assoc_args ) { $formatter_args = [ 'format' => $format, ]; - $formatter_fields = [ 'table', 'column', 'key', 'value', 'match' ]; + $formatter_fields = [ 'table', 'column', 'match', 'primary_key_name', 'primary_key_value' ]; if ( $fields ) { $fields = explode( ',', $assoc_args['fields'] ); @@ -1515,7 +1519,7 @@ public function search( $args, $assoc_args ) { if ( count( $tables ) > 1 ) { WP_CLI::error( 'The "ids" format can only be used for a single table.' ); } - $search_results = array_column( $search_results, 'value' ); + $search_results = array_column( $search_results, 'primary_key_value' ); } $formatter = new Formatter( $formatter_args, $formatter_fields ); From 1e72bb1e254e31cfba9a21683d24c9daef08d004 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Fri, 26 Apr 2024 19:47:03 -0700 Subject: [PATCH 4/5] Update docs --- README.md | 23 ++++++++++++++++++++++- src/DB_Command.php | 22 +++++++++++----------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a0a88377..24552a90 100644 --- a/README.md +++ b/README.md @@ -527,7 +527,7 @@ defined in the SQL. Finds a string in the database. ~~~ -wp db search [...] [--network] [--all-tables-with-prefix] [--all-tables] [--before_context=] [--after_context=] [--regex] [--regex-flags=] [--regex-delimiter=] [--table_column_once] [--one_line] [--matches_only] [--stats] [--table_column_color=] [--id_color=] [--match_color=] +wp db search [...] [--network] [--all-tables-with-prefix] [--all-tables] [--before_context=] [--after_context=] [--regex] [--regex-flags=] [--regex-delimiter=] [--table_column_once] [--one_line] [--matches_only] [--stats] [--table_column_color=] [--id_color=] [--match_color=] [--fields=] [--format=] ~~~ Searches through all of the text columns in a selection of database tables for a given string, Outputs colorized references to the string. @@ -593,6 +593,12 @@ Defaults to searching through all tables registered to $wpdb. On multisite, this [--match_color=] Percent color code to use for the match (unless both before and after context are 0, when no color code is used). For a list of available percent color codes, see below. Default '%3%k' (black on a mustard background). + [--fields=] + Get a specific subset of the fields. + + [--format=] + Render output in a particular format. + The percent color codes available are: | Code | Color @@ -661,6 +667,21 @@ They can be concatenated. For instance, the default match color of black on a mu # SQL search and delete records from database table 'wp_options' where 'option_name' match 'foo' wp db query "DELETE from wp_options where option_id in ($(wp db query "SELECT GROUP_CONCAT(option_id SEPARATOR ',') from wp_options where option_name like '%foo%';" --silent --skip-column-names))" + # Search for a string and print the result as a table + $ wp db search https://localhost:8889 --format=table --fields=table,column,match + +------------+--------------+-----------------------------+ + | table | column | match | + +------------+--------------+-----------------------------+ + | wp_options | option_value | https://localhost:8889 | + | wp_options | option_value | https://localhost:8889 | + | wp_posts | guid | https://localhost:8889/?p=1 | + | wp_users | user_url | https://localhost:8889 | + +------------+--------------+-----------------------------+ + + # Search for a string and get only the IDs (only works for a single table) + $ wp db search https://localhost:8889 wp_options --format=ids + 1 2 + ### wp db tables diff --git a/src/DB_Command.php b/src/DB_Command.php index 3c79ca28..308fe357 100644 --- a/src/DB_Command.php +++ b/src/DB_Command.php @@ -1208,10 +1208,10 @@ public function prefix() { * : Output the 'table:column' line once before all matching row lines in the table column rather than before each matching row. * * [--one_line] - * : Deprecated: use `--format` instead. Place the 'table:column' output on the same line as the row id and match ('table:column:id:match'). Overrides --table_column_once. + * : Place the 'table:column' output on the same line as the row id and match ('table:column:id:match'). Overrides --table_column_once. * * [--matches_only] - * : Deprecated: use `--format` instead. Only output the string matches (including context). No 'table:column's or row ids are outputted. + * : Only output the string matches (including context). No 'table:column's or row ids are outputted. * * [--stats] * : Output stats on the number of matches found, time taken, tables/columns/rows searched, tables skipped. @@ -1300,15 +1300,15 @@ public function prefix() { * wp db query "DELETE from wp_options where option_id in ($(wp db query "SELECT GROUP_CONCAT(option_id SEPARATOR ',') from wp_options where option_name like '%foo%';" --silent --skip-column-names))" * * # Search for a string and print the result as a table - * $ wp db search https://localhost:8889 --format=table - * +------------+--------------+-----------+-------+-----------------------------+ - * | table | column | key | value | match | - * +------------+--------------+-----------+-------+-----------------------------+ - * | wp_options | option_value | option_id | 1 | https://localhost:8889 | - * | wp_options | option_value | option_id | 2 | https://localhost:8889 | - * | wp_posts | guid | ID | 1 | https://localhost:8889/?p=1 | - * | wp_users | user_url | ID | 1 | https://localhost:8889 | - * +------------+--------------+-----------+-------+-----------------------------+ + * $ wp db search https://localhost:8889 --format=table --fields=table,column,match + * +------------+--------------+-----------------------------+ + * | table | column | match | + * +------------+--------------+-----------------------------+ + * | wp_options | option_value | https://localhost:8889 | + * | wp_options | option_value | https://localhost:8889 | + * | wp_posts | guid | https://localhost:8889/?p=1 | + * | wp_users | user_url | https://localhost:8889 | + * +------------+--------------+-----------------------------+ * * # Search for a string and get only the IDs (only works for a single table) * $ wp db search https://localhost:8889 wp_options --format=ids From 8211354f3ebf0f724df3f057ca48873a442cb9c6 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Fri, 26 Apr 2024 20:00:54 -0700 Subject: [PATCH 5/5] Less brittle test --- features/db-search.feature | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/features/db-search.feature b/features/db-search.feature index 5295a688..1f5e8f5b 100644 --- a/features/db-search.feature +++ b/features/db-search.feature @@ -1070,11 +1070,10 @@ Feature: Search through the database Scenario: Search for a string and output the format as a table Given a WP install - When I run `wp db search example.com --format=csv` + When I run `wp db search mail.example.com --format=csv` Then STDOUT should contain: """ - wp_options,option_value,mail.example.com,option_id,14 - wp_options,option_value,login@example.com,option_id,15 + wp_options,option_value,mail.example.com,option_id """ When I try `wp db search example.com --format=ids` @@ -1085,8 +1084,5 @@ Feature: Search through the database And STDOUT should be empty And the return code should be 1 - When I run `wp db search example.com wp_options --format=ids` - Then STDOUT should contain: - """ - 14 15 - """ + When I run `wp db search mail.example.com wp_options --format=ids` + Then STDOUT should not be empty