Skip to content

Commit f430d57

Browse files
authored
Support Lateral Join to Hyperf\Database\Query\Builder (#6774)
1 parent 2501a5e commit f430d57

File tree

5 files changed

+105
-39
lines changed

5 files changed

+105
-39
lines changed

src/Query/Grammars/PostgresGrammar.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Hyperf\Collection\Arr;
1616
use Hyperf\Database\Query\Builder;
1717
use Hyperf\Database\Query\Grammars\Grammar;
18+
use Hyperf\Database\Query\JoinLateralClause;
1819
use Hyperf\Stringable\Str;
1920

2021
use function Hyperf\Collection\collect;
@@ -153,6 +154,14 @@ public function compileTruncate(Builder $query): array
153154
return ['truncate ' . $this->wrapTable($query->from) . ' restart identity cascade' => []];
154155
}
155156

157+
/**
158+
* Compile a "lateral join" clause.
159+
*/
160+
public function compileJoinLateral(JoinLateralClause $join, string $expression): string
161+
{
162+
return trim("{$join->type} join lateral {$expression} on true");
163+
}
164+
156165
/**
157166
* Substitute the given bindings into the given raw SQL query.
158167
*

tests/Cases/DatabasePostgresBuilderTest.php

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@
2222
use Hyperf\Database\Schema\Blueprint;
2323
use Hyperf\Database\Schema\Schema;
2424
use Hyperf\DbConnection\Db;
25+
use Hyperf\Stringable\Str;
2526
use HyperfTest\Database\PgSQL\Stubs\ContainerStub;
26-
use HyperfTest\Database\PgSQL\Stubs\SwooleVersionStub;
2727
use Mockery as m;
2828
use PHPUnit\Framework\Attributes\CoversNothing;
29+
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
2930
use PHPUnit\Framework\TestCase;
3031

3132
/**
@@ -42,9 +43,9 @@ protected function tearDown(): void
4243
m::close();
4344
}
4445

46+
#[RequiresPhpExtension('swoole', '< 6.0')]
4547
public function testCreateDatabase()
4648
{
47-
SwooleVersionStub::skipV6();
4849
$grammar = new PostgresGrammar();
4950

5051
$connection = m::mock(Connection::class);
@@ -58,9 +59,9 @@ public function testCreateDatabase()
5859
$this->assertEquals(true, $builder->createDatabase('my_temporary_database'));
5960
}
6061

62+
#[RequiresPhpExtension('swoole', '< 6.0')]
6163
public function testDropDatabaseIfExists()
6264
{
63-
SwooleVersionStub::skipV6();
6465
$grammar = new PostgresGrammar();
6566

6667
$connection = m::mock(Connection::class);
@@ -73,9 +74,9 @@ public function testDropDatabaseIfExists()
7374
$this->assertEquals(true, $builder->dropDatabaseIfExists('my_database_a'));
7475
}
7576

77+
#[RequiresPhpExtension('swoole', '< 6.0')]
7678
public function testWhereFullText()
7779
{
78-
SwooleVersionStub::skipV6();
7980
$builder = $this->getPostgresBuilderWithProcessor();
8081
$builder->select('*')->from('users')->whereFullText('body', 'Hello World');
8182
$this->assertSame('select * from "users" where (to_tsvector(\'english\', "body")) @@ plainto_tsquery(\'english\', ?)', $builder->toSql());
@@ -112,9 +113,95 @@ public function testWhereFullText()
112113
$this->assertEquals(['Car Plane'], $builder->getBindings());
113114
}
114115

116+
#[RequiresPhpExtension('swoole', '< 6.0')]
117+
public function testJoinLateralPostgres()
118+
{
119+
$builder = $this->getPostgresBuilderWithProcessor();
120+
$builder->getConnection()->shouldReceive('getDatabaseName');
121+
$builder->from('users')->joinLateral(function ($q) {
122+
$q->from('contacts')->whereColumn('contracts.user_id', 'users.id');
123+
}, 'sub');
124+
$this->assertSame('select * from "users" inner join lateral (select * from "contacts" where "contracts"."user_id" = "users"."id") as "sub" on true', $builder->toSql());
125+
}
126+
127+
#[RequiresPhpExtension('swoole', '< 6.0')]
128+
public function testJoinLateralTest(): void
129+
{
130+
$container = ContainerStub::getContainer();
131+
$container->shouldReceive('get')->with(Db::class)->andReturn(new Db($container));
132+
Schema::dropIfExists('join_posts');
133+
Schema::dropIfExists('join_users');
134+
Schema::create('join_users', static function (Blueprint $table) {
135+
$table->id('id');
136+
$table->string('name');
137+
});
138+
139+
Schema::create('join_posts', static function (Blueprint $table) {
140+
$table->id('id');
141+
$table->string('title');
142+
$table->integer('rating');
143+
$table->unsignedBigInteger('user_id');
144+
});
145+
Db::table('join_users')->insert([
146+
['name' => Str::random()],
147+
['name' => Str::random()],
148+
]);
149+
150+
Db::table('join_posts')->insert([
151+
['title' => Str::random(), 'rating' => 1, 'user_id' => 1],
152+
['title' => Str::random(), 'rating' => 3, 'user_id' => 1],
153+
['title' => Str::random(), 'rating' => 7, 'user_id' => 1],
154+
]);
155+
$subquery = Db::table('join_posts')
156+
->select('title as best_post_title', 'rating as best_post_rating')
157+
->whereColumn('user_id', 'join_users.id')
158+
->orderBy('rating', 'desc')
159+
->limit(2);
160+
161+
$userWithPosts = Db::table('join_users')
162+
->where('id', 1)
163+
->joinLateral($subquery, 'best_post')
164+
->get();
165+
166+
$this->assertCount(2, $userWithPosts);
167+
$this->assertEquals(7, $userWithPosts[0]['best_post_rating']);
168+
$this->assertEquals(3, $userWithPosts[1]['best_post_rating']);
169+
170+
$userWithoutPosts = Db::table('join_users')
171+
->where('id', 2)
172+
->joinLateral($subquery, 'best_post')
173+
->get();
174+
175+
$this->assertCount(0, $userWithoutPosts);
176+
177+
$subquery = Db::table('join_posts')
178+
->select('title as best_post_title', 'rating as best_post_rating')
179+
->whereColumn('user_id', 'join_users.id')
180+
->orderBy('rating', 'desc')
181+
->limit(2);
182+
183+
$userWithPosts = Db::table('join_users')
184+
->where('id', 1)
185+
->leftJoinLateral($subquery, 'best_post')
186+
->get();
187+
188+
$this->assertCount(2, $userWithPosts);
189+
$this->assertEquals(7, $userWithPosts[0]['best_post_rating']);
190+
$this->assertEquals(3, $userWithPosts[1]['best_post_rating']);
191+
192+
$userWithoutPosts = Db::table('join_users')
193+
->where('id', 2)
194+
->leftJoinLateral($subquery, 'best_post')
195+
->get();
196+
197+
$this->assertCount(1, $userWithoutPosts);
198+
$this->assertNull($userWithoutPosts[0]['best_post_title']);
199+
$this->assertNull($userWithoutPosts[0]['best_post_rating']);
200+
}
201+
202+
#[RequiresPhpExtension('swoole', '< 6.0')]
115203
public function testWhereFullTextForReal()
116204
{
117-
SwooleVersionStub::skipV6();
118205
$container = ContainerStub::getContainer();
119206
$container->shouldReceive('get')->with(Db::class)->andReturn(new Db($container));
120207

tests/Cases/PostgreSqlSwooleExtConnectionTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
use Hyperf\Database\Schema\Schema;
2424
use Hyperf\Support\Filesystem\Filesystem;
2525
use HyperfTest\Database\PgSQL\Stubs\ContainerStub;
26-
use HyperfTest\Database\PgSQL\Stubs\SwooleVersionStub;
2726
use Mockery;
2827
use PHPUnit\Framework\Attributes\CoversNothing;
28+
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
2929
use PHPUnit\Framework\TestCase;
3030
use Symfony\Component\Console\Style\OutputStyle;
3131

@@ -34,13 +34,13 @@
3434
* @coversNothing
3535
*/
3636
#[CoversNothing]
37+
#[RequiresPhpExtension('swoole', '< 6.0')]
3738
class PostgreSqlSwooleExtConnectionTest extends TestCase
3839
{
3940
protected Migrator $migrator;
4041

4142
public function setUp(): void
4243
{
43-
SwooleVersionStub::skipV6();
4444
$resolver = ContainerStub::getContainer()->get(ConnectionResolverInterface::class);
4545

4646
$this->migrator = new Migrator(

tests/DBAL/ConnectionTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
use Hyperf\Database\PgSQL\DBAL\Connection;
1616
use Hyperf\Database\PgSQL\DBAL\Result;
1717
use Hyperf\Database\PgSQL\DBAL\Statement;
18-
use HyperfTest\Database\PgSQL\Stubs\SwooleVersionStub;
1918
use PHPUnit\Framework\Attributes\CoversNothing;
19+
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
2020
use PHPUnit\Framework\TestCase;
2121
use Swoole\Coroutine\PostgreSQL;
2222

@@ -25,13 +25,13 @@
2525
* @coversNothing
2626
*/
2727
#[CoversNothing]
28+
#[RequiresPhpExtension('swoole', '< 6.0')]
2829
class ConnectionTest extends TestCase
2930
{
3031
protected Connection $connection;
3132

3233
public function setUp(): void
3334
{
34-
SwooleVersionStub::skipV6();
3535
$pgsql = new PostgreSQL();
3636
$connected = $pgsql->connect('host=127.0.0.1 port=5432 dbname=postgres user=postgres password=postgres');
3737
if (! $connected) {

tests/Stubs/SwooleVersionStub.php

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)