From 930c8efd11a4237949b7c5ac4e11ea90a839bcd3 Mon Sep 17 00:00:00 2001 From: Javakky Date: Thu, 23 Jan 2025 21:38:25 +0900 Subject: [PATCH] feat: Skip FORCE INDEX for JOIN --- src/Parser/FromParser.php | 18 ++++++++++++++++++ tests/EndToEndTest.php | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/Parser/FromParser.php b/src/Parser/FromParser.php index b8361ea0..6cc8a393 100644 --- a/src/Parser/FromParser.php +++ b/src/Parser/FromParser.php @@ -295,7 +295,25 @@ private function buildJoin(string $left_table, Token $token) } } + /* + * Unlike other clauses (e.g., FROM), the buildJoin advances the pointer to the specified keyword (e.g., FORCE). + * Therefore, the pointer needs to be adjusted. + * For instance, in "FROM a FORCE INDEX ...", processing for other clauses ends just before the identifier (a), + * but for the JOIN clause, the pointer advances to "FORCE". + * To address this issue, we adjusted the pointer before and after calling SQLParser::skipIndexHints(), + * and modified the code to advance the pointer to the closing parenthesis ')' if necessary. + */ + $this->pointer--; $this->pointer = SQLParser::skipIndexHints($this->pointer, $this->tokens); + $this->pointer++; + if ($this->tokens[$this->pointer]->type === TokenType::SEPARATOR + && $this->tokens[$this->pointer]->value === ")") { + $this->pointer++; + } + $next = $this->tokens[$this->pointer] ?? null; + if ($next === null) { + return $table; + } if ($table['join_type'] === JoinType::NATURAL || $table['join_type'] === JoinType::CROSS) { return $table; diff --git a/tests/EndToEndTest.php b/tests/EndToEndTest.php index 45192f99..d2cd95a3 100644 --- a/tests/EndToEndTest.php +++ b/tests/EndToEndTest.php @@ -269,6 +269,27 @@ public function testLeftJoinWithCount() ); } + public function testLeftJoinSkipIndex() + { + $pdo = self::getConnectionToFullDB(false); + + $query = $pdo->prepare( + "SELECT `id` + FROM `video_game_characters` FORCE INDEX (`PRIMARY`) + LEFT JOIN `character_tags` FORCE INDEX (`PRIMARY`) + ON `character_tags`.`character_id` = `video_game_characters`.`id` + LIMIT 1" + ); + $query->execute(); + + $this->assertSame( + [ + ['id' => 1] + ], + $query->fetchAll(\PDO::FETCH_ASSOC) + ); + } + public function testMaxValueAliasedToColumnName() { $pdo = self::getConnectionToFullDB(false);