Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/wp-includes/class-wpdb.php
Original file line number Diff line number Diff line change
Expand Up @@ -3914,9 +3914,13 @@ protected function get_table_from_query( $query ) {
* It is usually a pattern for matching a prefix so we just
* strip the trailing % and unescape the _ to get 'wp_123_'
* which drop-ins can use for routing these SQL statements.
*
* A LIKE operand built with wpdb::prepare() and wpdb::esc_like(),
* such as 'wp\\_123\\_%', has its backslashes escaped once more
* as a string literal, so that escaping is removed first.
*/
if ( preg_match( '/^\s*SHOW\s+(?:TABLE\s+STATUS|(?:FULL\s+)?TABLES)\s+(?:WHERE\s+Name\s+)?LIKE\s*("|\')((?:[\\\\0-9a-zA-Z$_.-]|[\xC2-\xDF][\x80-\xBF])+)%?\\1/is', $query, $maybe ) ) {
return str_replace( '\\_', '_', $maybe[2] );
return str_replace( '\\_', '_', str_replace( '\\\\', '\\', $maybe[2] ) );
}

// Big pattern for the rest of the table-related queries.
Expand Down
49 changes: 49 additions & 0 deletions tests/phpunit/tests/db.php
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,55 @@ public function test_get_escaped_table_from_show_query( $query, $table ) {
$this->assertSame( $table, self::$_wpdb->get_table_from_query( $query ) );
}

/**
* Data provider.
*
* @return array[]
*/
public function data_get_table_from_prepared_show_like_query() {
return array(
'SHOW TABLES, single quotes' => array( "SHOW TABLES LIKE 'wp\\\\_my\\\\_custom\\\\_table'", 'wp_my_custom_table' ),
'SHOW TABLES, double quotes' => array( 'SHOW TABLES LIKE "wp\\\\_my\\\\_custom\\\\_table"', 'wp_my_custom_table' ),
'SHOW TABLE STATUS, prefix wildcard' => array( "SHOW TABLE STATUS LIKE 'wp\\\\_123\\\\_%'", 'wp_123_' ),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This covers the trailing wildcard with a handwritten SQL string. The routing path builds the operand with esc_like( $prefix ) . '%' and prepare(), which temporarily replaces % with a placeholder token until the query filter removes it. Please add a case that prepares the wildcard query and runs it through that filter (or remove_placeholder_escape()) before asserting the extracted prefix. That will verify the escape and filter order used by database drop-ins.

'SHOW FULL TABLES, no underscore' => array( "SHOW FULL TABLES LIKE 'wptable'", 'wptable' ),
);
}

/**
* @ticket 57149
*
* @dataProvider data_get_table_from_prepared_show_like_query
*
* @param string $query Query.
* @param string $table Expected table name.
*/
public function test_get_table_from_prepared_show_like_query( $query, $table ) {
$this->assertSame( $table, self::$_wpdb->get_table_from_query( $query ) );
}

/**
* @ticket 57149
*/
public function test_get_table_from_query_built_with_prepare_and_esc_like() {
$query = self::$_wpdb->prepare( 'SHOW TABLES LIKE %s', self::$_wpdb->esc_like( 'wp_my_custom_table' ) );

$this->assertSame( 'wp_my_custom_table', self::$_wpdb->get_table_from_query( $query ) );
}

/**
* A prefix pattern prepared as database drop-ins see it: the wildcard placeholder
* is replaced again by the `query` filter before the table is looked up.
*
* @ticket 57149
*/
public function test_get_table_from_query_built_with_prepare_esc_like_and_wildcard() {
$query = self::$_wpdb->prepare( 'SHOW TABLE STATUS LIKE %s', self::$_wpdb->esc_like( 'wp_123_' ) . '%' );
$query = self::$_wpdb->remove_placeholder_escape( $query );

$this->assertSame( "SHOW TABLE STATUS LIKE 'wp\\\\_123\\\\_%'", $query, 'The prepared query is not in the expected form.' );
$this->assertSame( 'wp_123_', self::$_wpdb->get_table_from_query( $query ) );
}

/**
* @ticket 21212
*/
Expand Down
Loading