diff --git a/tests/WP_SQLite_Translator_Tests.php b/tests/WP_SQLite_Translator_Tests.php index cfebc685..900dfa1f 100644 --- a/tests/WP_SQLite_Translator_Tests.php +++ b/tests/WP_SQLite_Translator_Tests.php @@ -146,6 +146,22 @@ public function testSelectFromDual() { $this->assertEquals( 1, $result[0]->output ); } + public function testSelectIndexHintForce() { + $this->assertQuery( "INSERT INTO _options (option_name) VALUES ('first');" ); + $result = $this->assertQuery( + 'SELECT 1 as output FROM _options FORCE INDEX (PRIMARY, post_parent) WHERE 1=1' + ); + $this->assertEquals( 1, $result[0]->output ); + } + + public function testSelectIndexHintUseGroup() { + $this->assertQuery( "INSERT INTO _options (option_name) VALUES ('first');" ); + $result = $this->assertQuery( + 'SELECT 1 as output FROM _options USE KEY FOR GROUP BY (PRIMARY, post_parent) WHERE 1=1' + ); + $this->assertEquals( 1, $result[0]->output ); + } + public function testLeftFunction1Char() { $result = $this->assertQuery( 'SELECT LEFT("abc", 1) as output' diff --git a/wp-includes/sqlite/class-wp-sqlite-translator.php b/wp-includes/sqlite/class-wp-sqlite-translator.php index 34ad86b7..88c9e432 100644 --- a/wp-includes/sqlite/class-wp-sqlite-translator.php +++ b/wp-includes/sqlite/class-wp-sqlite-translator.php @@ -1424,6 +1424,10 @@ private function execute_select() { continue; } + if ( $this->skip_index_hint() ) { + continue; + } + $this->rewriter->consume(); } $this->rewriter->consume_all(); @@ -1476,6 +1480,71 @@ private function execute_select() { } } + /** + * Ignores the FORCE INDEX clause + * + * + * USE {INDEX|KEY} + * [FOR {JOIN|ORDER BY|GROUP BY}] ([index_list]) + * | {IGNORE|FORCE} {INDEX|KEY} + * [FOR {JOIN|ORDER BY|GROUP BY}] (index_list) + * @see https://dev.mysql.com/doc/refman/8.3/en/index-hints.html + * @return bool + */ + private function skip_index_hint() { + $force = $this->rewriter->peek(); + if ( ! $force || ! $force->matches( + WP_SQLite_Token::TYPE_KEYWORD, + WP_SQLite_Token::FLAG_KEYWORD_RESERVED, + array( 'USE', 'FORCE', 'IGNORE' ) + ) ) { + return false; + } + + $index = $this->rewriter->peek_nth( 2 ); + if ( ! $index || ! $index->matches( + WP_SQLite_Token::TYPE_KEYWORD, + WP_SQLite_Token::FLAG_KEYWORD_RESERVED, + array( 'INDEX', 'KEY' ) + ) ) { + return false; + } + + $this->rewriter->skip(); // USE, FORCE, IGNORE + $this->rewriter->skip(); // INDEX, KEY + + $maybe_for = $this->rewriter->peek(); + if ( $maybe_for && $maybe_for->matches( + WP_SQLite_Token::TYPE_KEYWORD, + WP_SQLite_Token::FLAG_KEYWORD_RESERVED, + array( 'FOR' ) + ) ) { + $this->rewriter->skip(); // FOR + + $token = $this->rewriter->peek(); + if ( $token && $token->matches( + WP_SQLite_Token::TYPE_KEYWORD, + WP_SQLite_Token::FLAG_KEYWORD_RESERVED, + array( 'JOIN', 'ORDER', 'GROUP' ) + ) ) { + $this->rewriter->skip(); // JOIN, ORDER, GROUP + if ( 'BY' === strtoupper( $this->rewriter->peek()->value ) ) { + $this->rewriter->skip(); // BY + } + } + } + + // Skip everything until the closing parenthesis. + $this->rewriter->skip( + array( + 'type' => WP_SQLite_Token::TYPE_OPERATOR, + 'value' => ')', + ) + ); + + return true; + } + /** * Executes a TRUNCATE statement. */