Skip to content

Support subselects #103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 27, 2024
Merged
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
20 changes: 17 additions & 3 deletions src/Doctrine/MySql/UseIndexHintHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace ShipMonk\Doctrine\MySql;

use Doctrine\ORM\Query;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\ORM\Query\Parser;
use LogicException;
use ShipMonk\Doctrine\Walker\HintHandler;
use ShipMonk\Doctrine\Walker\SqlNode;
Expand All @@ -25,7 +27,7 @@ class UseIndexHintHandler extends HintHandler
*/
public function getNodes(): array
{
return [SqlNode::FromClause];
return [SqlNode::FromClause, SqlNode::SubselectFromClause];
}

public function processNode(SqlNode $sqlNode, string $sql): string
Expand All @@ -34,6 +36,18 @@ public function processNode(SqlNode $sqlNode, string $sql): string
$sqlWalker = $this->getDoctrineSqlWalker();
$query = $sqlWalker->getQuery();
$platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();
$dql = $query->getDQL();

if ($dql === null) {
throw new LogicException('Empty DQL query encountered');
}

$queryCopy = new Query($this->getDoctrineSqlWalker()->getEntityManager());
$queryCopy->setDQL($dql);
$parser = new Parser($queryCopy);

/** @var string $wholeSql */
$wholeSql = $parser->parse()->getSqlExecutor()->getSqlStatements();

if (!is_a($platform, 'Doctrine\DBAL\Platforms\MySqlPlatform')) { // bypass platform MySqlPlatform => MySQLPlatform rename in dbal
throw new LogicException("Only MySQL platform is supported, {$platform->getName()} given");
Expand Down Expand Up @@ -66,12 +80,12 @@ public function processNode(SqlNode $sqlNode, string $sql): string
: '\S+'; // doctrine always adds some alias
$tableWithAliasRegex = "{$delimiter}{$tableName}\s+{$tableAlias}{$delimiter}i";

if (preg_match($tableWithAliasRegex, $sql) === 0) {
if (preg_match($tableWithAliasRegex, $wholeSql) === 0) {
$aliasInfo = $hint->getDqlAlias() !== null ? " with DQL alias {$hint->getDqlAlias()}" : '';
throw new LogicException("Invalid hint for index {$hint->getIndexName()}, table {$tableName}{$aliasInfo} is not present in the query.");
}

if ($hint->getDqlAlias() === null && preg_match_all($tableWithAliasRegex, $sql) !== 1) {
if ($hint->getDqlAlias() === null && preg_match_all($tableWithAliasRegex, $wholeSql) !== 1) {
throw new LogicException("Invalid hint for index {$hint->getIndexName()}, table {$tableName} is present multiple times in the query, please specify DQL alias to apply index on a proper place.");
}

Expand Down
9 changes: 9 additions & 0 deletions tests/Doctrine/MySql/UseIndexSqlWalkerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public function testWalker(string $dql, callable $configureQueryCallback, ?strin
public static function walksProvider(): iterable
{
$userSelectDql = sprintf('SELECT u FROM %s u', User::class);
$userSubselectDql = sprintf('SELECT u FROM %s u WHERE u.id = (SELECT u2.id FROM %s u2 WHERE u2.id = 1)', User::class, User::class);

yield 'FROM - use single index' => [
$userSelectDql,
Expand Down Expand Up @@ -181,6 +182,14 @@ static function (Query $query): void {
. ' INNER JOIN account a2_ ON u0_.id = a2_.manager_id',
];

yield 'FROM in subselect' => [
$userSubselectDql,
static function (Query $query): void {
$query->setHint(UseIndexHintHandler::class, [IndexHint::use('IDX_FOO', User::TABLE_NAME, 'u2')]);
},
'SELECT u0_.id AS id_0, u0_.account_id AS account_id_1 FROM user u0_ WHERE u0_.id = (SELECT u1_.id FROM user u1_ USE INDEX (IDX_FOO) WHERE u1_.id = 1)',
];

yield 'no hint' => [
$userSelectDql,
static function (Query $query): void {
Expand Down
Loading