Skip to content

Commit 4113c78

Browse files
committed
Merge remote-tracking branch 'upstream/master' into context_bug
2 parents d748262 + 1501796 commit 4113c78

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

features/db-search.feature

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,13 @@ Feature: Search through the database
708708
"""
709709
Then the return code should be 1
710710

711+
When I try `wp db search 'unfindable' --regex --regex-delimiter='1'`
712+
Then STDERR should be:
713+
"""
714+
Error: The regex '1unfindable1' fails.
715+
"""
716+
Then the return code should be 1
717+
711718
When I run `wp db search '[0-9é]+?https:' --regex --regex-flags=u --before_context=0 --after_context=0`
712719
Then STDOUT should contain:
713720
"""
@@ -909,6 +916,36 @@ Feature: Search through the database
909916
Warning: Unrecognized percent color code '%x' for 'match_color'.
910917
"""
911918

919+
Scenario: Search should cater for field/table names that use reserved words or unusual characters
920+
Given a WP install
921+
And a esc_sql_ident.sql file:
922+
"""
923+
CREATE TABLE `TABLE` (`KEY` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `VALUES` TEXT, `back``tick` TEXT, `single'double"quote` TEXT, PRIMARY KEY (`KEY`) );
924+
INSERT INTO `TABLE` (`VALUES`, `back``tick`, `single'double"quote`) VALUES ('v"v`v\'v\\v_v1', 'v"v`v\'v\\v_v1', 'v"v`v\'v\\v_v1' );
925+
INSERT INTO `TABLE` (`VALUES`, `back``tick`, `single'double"quote`) VALUES ('v"v`v\'v\\v_v2', 'v"v`v\'v\\v_v2', 'v"v`v\'v\\v_v2' );
926+
"""
927+
928+
When I run `wp db query "SOURCE esc_sql_ident.sql;"`
929+
Then STDERR should be empty
930+
931+
When I run `wp db search 'v_v' TABLE`
932+
Then STDOUT should be:
933+
"""
934+
TABLE:VALUES
935+
1:v"v`v'v\v_v1
936+
TABLE:VALUES
937+
2:v"v`v'v\v_v2
938+
TABLE:back`tick
939+
1:v"v`v'v\v_v1
940+
TABLE:back`tick
941+
2:v"v`v'v\v_v2
942+
TABLE:single'double"quote
943+
1:v"v`v'v\v_v1
944+
TABLE:single'double"quote
945+
2:v"v`v'v\v_v2
946+
"""
947+
And STDERR should be empty
948+
912949
Scenario: Search with matches within context
913950
Given a WP install
914951
And I run `wp option update matches_in_context '1234_XYXYX_2345678_XYXYX_2345678901_XYXYX_2345'`

src/DB_Command.php

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -875,20 +875,22 @@ public function search( $args, $assoc_args ) {
875875
}
876876
continue;
877877
}
878+
$table_sql = self::esc_sql_ident( $table );
878879
$column_count += count( $text_columns );
879880
if ( ! $primary_keys ) {
880881
WP_CLI::warning( "No primary key for table '$table'. No row ids will be outputted." );
881882
$primary_key = $primary_key_sql = '';
882883
} else {
883884
$primary_key = array_shift( $primary_keys );
884-
$primary_key_sql = $primary_key . ', ';
885+
$primary_key_sql = self::esc_sql_ident( $primary_key ) . ', ';
885886
}
886887

887888
foreach ( $text_columns as $column ) {
889+
$column_sql = self::esc_sql_ident( $column );
888890
if ( $regex ) {
889-
$results = $wpdb->get_results( "SELECT {$primary_key_sql}{$column} FROM {$table}" );
891+
$results = $wpdb->get_results( "SELECT {$primary_key_sql}{$column_sql} FROM {$table_sql}" );
890892
} else {
891-
$results = $wpdb->get_results( $wpdb->prepare( "SELECT {$primary_key_sql}{$column} FROM {$table} WHERE {$column} LIKE %s;", $esc_like_search ) );
893+
$results = $wpdb->get_results( $wpdb->prepare( "SELECT {$primary_key_sql}{$column_sql} FROM {$table_sql} WHERE {$column_sql} LIKE %s;", $esc_like_search ) );
892894
}
893895
if ( $results ) {
894896
$row_count += count( $results );
@@ -953,12 +955,12 @@ public function search( $args, $assoc_args ) {
953955

954956
private static function get_create_query() {
955957

956-
$create_query = sprintf( 'CREATE DATABASE `%s`', DB_NAME );
958+
$create_query = sprintf( 'CREATE DATABASE %s', self::esc_sql_ident( DB_NAME ) );
957959
if ( defined( 'DB_CHARSET' ) && constant( 'DB_CHARSET' ) ) {
958-
$create_query .= sprintf( ' DEFAULT CHARSET `%s`', constant( 'DB_CHARSET' ) );
960+
$create_query .= sprintf( ' DEFAULT CHARSET %s', self::esc_sql_ident( DB_CHARSET ) );
959961
}
960962
if ( defined( 'DB_COLLATE' ) && constant( 'DB_COLLATE' ) ) {
961-
$create_query .= sprintf( ' DEFAULT COLLATE `%s`', constant( 'DB_COLLATE' ) );
963+
$create_query .= sprintf( ' DEFAULT COLLATE %s', self::esc_sql_ident( DB_COLLATE ) );
962964
}
963965
return $create_query;
964966
}
@@ -992,10 +994,11 @@ private static function run( $cmd, $assoc_args = array(), $descriptors = null )
992994
private static function get_columns( $table ) {
993995
global $wpdb;
994996

997+
$table_sql = self::esc_sql_ident( $table );
995998
$primary_keys = $text_columns = $all_columns = array();
996999
$suppress_errors = $wpdb->suppress_errors();
997-
if ( ( $results = $wpdb->get_results( "DESCRIBE $table" ) ) ) {
998-
foreach ( $wpdb->get_results( "DESCRIBE $table" ) as $col ) {
1000+
if ( ( $results = $wpdb->get_results( "DESCRIBE $table_sql" ) ) ) {
1001+
foreach ( $results as $col ) {
9991002
if ( 'PRI' === $col->Key ) {
10001003
$primary_keys[] = $col->Field;
10011004
}
@@ -1045,6 +1048,24 @@ private static function esc_like( $old ) {
10451048
return $old;
10461049
}
10471050

1051+
/**
1052+
* Escapes (backticks) MySQL identifiers (aka schema object names) - i.e. column names, table names, and database/index/alias/view etc names.
1053+
* See https://dev.mysql.com/doc/refman/5.5/en/identifiers.html
1054+
*
1055+
* @param string|array $idents A single identifier or an array of identifiers.
1056+
* @return string|array An escaped string if given a string, or an array of escaped strings if given an array of strings.
1057+
*/
1058+
private static function esc_sql_ident( $idents ) {
1059+
$backtick = function ( $v ) {
1060+
// Escape any backticks in the identifier by doubling.
1061+
return '`' . str_replace( '`', '``', $v ) . '`';
1062+
};
1063+
if ( is_string( $idents ) ) {
1064+
return $backtick( $idents );
1065+
}
1066+
return array_map( $backtick, $idents );
1067+
}
1068+
10481069
/**
10491070
* Gets the color codes from the options if any, and returns the passed in array colorized with 2 elements per entry, a color code (or '') and a reset (or '').
10501071
*

0 commit comments

Comments
 (0)