diff --git a/src/Driver/Compiler.php b/src/Driver/Compiler.php index 35179944..b6fafbf6 100644 --- a/src/Driver/Compiler.php +++ b/src/Driver/Compiler.php @@ -208,7 +208,7 @@ protected function joins(QueryParameters $params, Quoter $q, array $joins): stri $statement = ''; foreach ($joins as $join) { $statement .= sprintf( - "\n%s JOIN %s", + \str_contains($join['type'], 'JOIN') ? "\n%s %s" : "\n%s JOIN %s", $join['type'], $this->nameWithAlias($params, $q, $join['outer'], $join['alias'], true) ); diff --git a/src/Driver/CompilerCache.php b/src/Driver/CompilerCache.php index 5b02b559..d55aea3c 100644 --- a/src/Driver/CompilerCache.php +++ b/src/Driver/CompilerCache.php @@ -171,7 +171,7 @@ protected function hashSelectQuery(QueryParameters $params, array $tokens): stri $hash .= $this->hashColumns($params, $tokens['columns']); foreach ($tokens['join'] as $join) { - $hash .= 'j' . $join['alias'] . $join['type']; + $hash .= 'j' . $join['alias'] . \str_replace(['JOIN', ' '], '', $join['type']); if ($join['outer'] instanceof SelectQuery) { $hash .= $join['outer']->getPrefix() === null ? '' : 'p_' . $join['outer']->getPrefix(); diff --git a/tests/Database/Functional/Driver/Common/Query/SelectWithJoinQueryTest.php b/tests/Database/Functional/Driver/Common/Query/SelectWithJoinQueryTest.php index 69c2c885..6b9b0ac2 100644 --- a/tests/Database/Functional/Driver/Common/Query/SelectWithJoinQueryTest.php +++ b/tests/Database/Functional/Driver/Common/Query/SelectWithJoinQueryTest.php @@ -368,6 +368,28 @@ public function testInnerJoin3(): void ); } + public function testJoinWithFullTypeName(): void + { + $select = $this->database->select() + ->from('temperature as t') + ->where('t.date', '2022-01-05') + ->join( + type: 'LEFT JOIN LATERAL', + outer: $this->database->select() + ->from('humidity') + ->where('h.date', '<=', 't.date'), + alias: 'h', + on: new Fragment('true') + ); + + $this->assertSameQuery( + 'SELECT * FROM {temperature} AS {t} + LEFT JOIN LATERAL (SELECT * FROM {humidity} WHERE {h}.{date} <= ?) AS {h} + ON true WHERE {t}.{date} = ?', + $select + ); + } + //Join with WHERE public function testJoinOnWhereParameterOrder(): void diff --git a/tests/Database/Functional/Driver/Postgres/Query/SelectWithJoinQueryTest.php b/tests/Database/Functional/Driver/Postgres/Query/SelectWithJoinQueryTest.php index e7373ee3..08af3792 100644 --- a/tests/Database/Functional/Driver/Postgres/Query/SelectWithJoinQueryTest.php +++ b/tests/Database/Functional/Driver/Postgres/Query/SelectWithJoinQueryTest.php @@ -5,6 +5,7 @@ namespace Cycle\Database\Tests\Functional\Driver\Postgres\Query; // phpcs:ignore +use Cycle\Database\Injection\Fragment; use Cycle\Database\Tests\Functional\Driver\Common\Query\SelectWithJoinQueryTest as CommonClass; /** @@ -14,4 +15,33 @@ class SelectWithJoinQueryTest extends CommonClass { public const DRIVER = 'postgres'; + + public function testCacheLeftJoinLateral(): void + { + $compiler = $this->database->select()->getDriver()->getQueryCompiler(); + + $ref = new \ReflectionProperty($compiler, 'cache'); + $ref->setAccessible(true); + $ref->setValue($compiler, []); + + $select = $this->database->select() + ->from('temperature as t') + ->where('t.date', '2022-01-05') + ->join( + type: 'LEFT JOIN LATERAL', + outer: $this->database->select() + ->from('humidity') + ->where('h.date', '<=', 't.date'), + alias: 'h', + on: new Fragment('true') + ); + + $select->sqlStatement(); + + // Verify that the join name has a correct format in the cache + $this->assertArrayHasKey( + 's__temperature as t*,jhLEFTLATERALp_s__humidity*,wANDh.date<=?_1_1onANDtruewANDt.date=?_1_1', + $ref->getValue($compiler) + ); + } } diff --git a/tests/Database/Functional/Driver/SQLite/Query/SelectWithJoinQueryTest.php b/tests/Database/Functional/Driver/SQLite/Query/SelectWithJoinQueryTest.php index 5eff02ba..304f1c53 100644 --- a/tests/Database/Functional/Driver/SQLite/Query/SelectWithJoinQueryTest.php +++ b/tests/Database/Functional/Driver/SQLite/Query/SelectWithJoinQueryTest.php @@ -14,4 +14,31 @@ class SelectWithJoinQueryTest extends CommonClass { public const DRIVER = 'sqlite'; + + public function testCacheJoinWithFullTypeName(): void + { + $compiler = $this->database->select()->getDriver()->getQueryCompiler(); + + $ref = new \ReflectionProperty($compiler, 'cache'); + $ref->setAccessible(true); + $ref->setValue($compiler, []); + + $select = $this->database->select() + ->from(['users']) + ->join('INNER', 'photos')->on('photos.user_id', 'users.id'); + + $sql1 = $select->sqlStatement(); + $cache1 = $ref->getValue($compiler); + + $select = $this->database->select() + ->from(['users']) + ->join('INNER JOIN', 'photos')->on('photos.user_id', 'users.id'); + + $sql2 = $select->sqlStatement(); + $cache2 = $ref->getValue($compiler); + + $this->assertCount(1, $ref->getValue($compiler)); + $this->assertSame($sql1, $sql2); + $this->assertSame($cache1, $cache2); + } }