Skip to content

Commit 1f9a007

Browse files
committed
Add Query Builder test for lateral join methods
1 parent 4d0a99b commit 1f9a007

File tree

5 files changed

+126
-33
lines changed

5 files changed

+126
-33
lines changed

src/Illuminate/Database/Query/Grammars/Grammar.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,30 +176,30 @@ protected function compileFrom(Builder $query, $table)
176176
protected function compileJoins(Builder $query, $joins)
177177
{
178178
return collect($joins)->map(function ($join) use ($query) {
179-
if ($join->lateral) {
180-
return $this->joinLateral($query, $join);
181-
}
182-
183179
$table = $this->wrapTable($join->table);
184180

185181
$nestedJoins = is_null($join->joins) ? '' : ' '.$this->compileJoins($query, $join->joins);
186182

187183
$tableAndNestedJoins = is_null($join->joins) ? $table : '('.$table.$nestedJoins.')';
188184

185+
if ($join->lateral) {
186+
return $this->joinLateral($join, $tableAndNestedJoins);
187+
}
188+
189189
return trim("{$join->type} join {$tableAndNestedJoins} {$this->compileWheres($join)}");
190190
})->implode(' ');
191191
}
192192

193193
/**
194194
* Compile a "lateral join" clause.
195195
*
196-
* @param \Illuminate\Database\Query\Builder $query
197196
* @param \Illuminate\Database\Query\JoinClause $join
197+
* @param string $expression
198198
* @return string
199199
*
200200
* @throws \RuntimeException
201201
*/
202-
public function joinLateral(Builder $query, JoinClause $join)
202+
public function joinLateral(JoinClause $join, string $expression)
203203
{
204204
throw new RuntimeException('This database engine does not support lateral joins.');
205205
}

src/Illuminate/Database/Query/Grammars/MySqlGrammar.php

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,19 +237,13 @@ protected function compileUpdateColumns(Builder $query, array $values)
237237
/**
238238
* Compile a "lateral join" clause.
239239
*
240-
* @param \Illuminate\Database\Query\Builder $query
241240
* @param \Illuminate\Database\Query\JoinClause $join
241+
* @param string $expression
242242
* @return string
243243
*/
244-
public function joinLateral(Builder $query, JoinClause $join)
244+
public function joinLateral(JoinClause $join, string $expression)
245245
{
246-
$table = $this->wrapTable($join->table);
247-
248-
$nestedJoins = is_null($join->joins) ? '' : ' '.$this->compileJoins($query, $join->joins);
249-
250-
$tableAndNestedJoins = is_null($join->joins) ? $table : '('.$table.$nestedJoins.')';
251-
252-
return trim("{$join->type} join lateral {$tableAndNestedJoins} on true");
246+
return trim("{$join->type} join lateral {$expression} on true");
253247
}
254248

255249
/**

src/Illuminate/Database/Query/Grammars/PostgresGrammar.php

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -389,19 +389,13 @@ protected function compileUpdateColumns(Builder $query, array $values)
389389
/**
390390
* Compile a "lateral join" clause.
391391
*
392-
* @param \Illuminate\Database\Query\Builder $query
393392
* @param \Illuminate\Database\Query\JoinClause $join
393+
* @param string $expression
394394
* @return string
395395
*/
396-
public function joinLateral(Builder $query, JoinClause $join)
396+
public function joinLateral(JoinClause $join, string $expression)
397397
{
398-
$table = $this->wrapTable($join->table);
399-
400-
$nestedJoins = is_null($join->joins) ? '' : ' '.$this->compileJoins($query, $join->joins);
401-
402-
$tableAndNestedJoins = is_null($join->joins) ? $table : '('.$table.$nestedJoins.')';
403-
404-
return trim("{$join->type} join lateral {$tableAndNestedJoins} on true");
398+
return trim("{$join->type} join lateral {$expression} on true");
405399
}
406400

407401
/**

src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -390,21 +390,15 @@ protected function compileUpdateWithJoins(Builder $query, $table, $columns, $whe
390390
/**
391391
* Compile a "lateral join" clause.
392392
*
393-
* @param \Illuminate\Database\Query\Builder $query
394393
* @param \Illuminate\Database\Query\JoinClause $join
394+
* @param string $expression
395395
* @return string
396396
*/
397-
public function joinLateral(Builder $query, JoinClause $join)
397+
public function joinLateral(JoinClause $join, string $expression)
398398
{
399-
$table = $this->wrapTable($join->table);
400-
401-
$nestedJoins = is_null($join->joins) ? '' : ' '.$this->compileJoins($query, $join->joins);
402-
403-
$tableAndNestedJoins = is_null($join->joins) ? $table : '('.$table.$nestedJoins.')';
404-
405399
$type = $join->type == 'left' ? 'outer' : 'cross';
406400

407-
return trim("{$type} apply {$tableAndNestedJoins}");
401+
return trim("{$type} apply {$expression}");
408402
}
409403

410404
/**

tests/Database/DatabaseQueryBuilderTest.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2526,6 +2526,117 @@ public function testRightJoinSub()
25262526
$builder->from('users')->rightJoinSub(['foo'], 'sub', 'users.id', '=', 'sub.id');
25272527
}
25282528

2529+
public function testJoinLateral()
2530+
{
2531+
$builder = $this->getMySqlBuilder();
2532+
$builder->getConnection()->shouldReceive('getDatabaseName');
2533+
$builder->from('users')->joinLateral('select * from `contacts` where `contracts`.`user_id` = `users`.`id`', 'sub');
2534+
$this->assertSame('select * from `users` inner join lateral (select * from `contacts` where `contracts`.`user_id` = `users`.`id`) as `sub` on true', $builder->toSql());
2535+
2536+
$builder = $this->getMySqlBuilder();
2537+
$builder->getConnection()->shouldReceive('getDatabaseName');
2538+
$builder->from('users')->joinLateral(function ($q) {
2539+
$q->from('contacts')->whereColumn('contracts.user_id', 'users.id');
2540+
}, 'sub');
2541+
$this->assertSame('select * from `users` inner join lateral (select * from `contacts` where `contracts`.`user_id` = `users`.`id`) as `sub` on true', $builder->toSql());
2542+
2543+
$builder = $this->getMySqlBuilder();
2544+
$builder->getConnection()->shouldReceive('getDatabaseName');
2545+
$sub = $this->getMySqlBuilder();
2546+
$sub->getConnection()->shouldReceive('getDatabaseName');
2547+
$eloquentBuilder = new EloquentBuilder($sub->from('contacts')->whereColumn('contracts.user_id', 'users.id'));
2548+
$builder->from('users')->joinLateral($eloquentBuilder, 'sub');
2549+
$this->assertSame('select * from `users` inner join lateral (select * from `contacts` where `contracts`.`user_id` = `users`.`id`) as `sub` on true', $builder->toSql());
2550+
2551+
$sub1 = $this->getMySqlBuilder();
2552+
$sub1->getConnection()->shouldReceive('getDatabaseName');
2553+
$sub1 = $sub1->from('contacts')->whereColumn('contracts.user_id', 'users.id')->where('name', 'foo');
2554+
2555+
$sub2 = $this->getMySqlBuilder();
2556+
$sub2->getConnection()->shouldReceive('getDatabaseName');
2557+
$sub2 = $sub2->from('contacts')->whereColumn('contracts.user_id', 'users.id')->where('name', 'bar');
2558+
2559+
$builder = $this->getMySqlBuilder();
2560+
$builder->getConnection()->shouldReceive('getDatabaseName');
2561+
$builder->from('users')->joinLateral($sub1, 'sub1')->joinLateral($sub2, 'sub2');
2562+
2563+
$expected = 'select * from `users` ';
2564+
$expected .= 'inner join lateral (select * from `contacts` where `contracts`.`user_id` = `users`.`id` and `name` = ?) as `sub1` on true ';
2565+
$expected .= 'inner join lateral (select * from `contacts` where `contracts`.`user_id` = `users`.`id` and `name` = ?) as `sub2` on true';
2566+
2567+
$this->assertEquals($expected, $builder->toSql());
2568+
$this->assertEquals(['foo', 'bar'], $builder->getRawBindings()['join']);
2569+
2570+
$this->expectException(InvalidArgumentException::class);
2571+
$builder = $this->getMySqlBuilder();
2572+
$builder->from('users')->joinLateral(['foo'], 'sub');
2573+
}
2574+
2575+
public function testJoinLateralSQLite()
2576+
{
2577+
$this->expectException(RuntimeException::class);
2578+
$builder = $this->getSQLiteBuilder();
2579+
$builder->getConnection()->shouldReceive('getDatabaseName');
2580+
$builder->from('users')->joinLateral(function ($q) {
2581+
$q->from('contacts')->whereColumn('contracts.user_id', 'users.id');
2582+
}, 'sub')->toSql();
2583+
}
2584+
2585+
public function testJoinLateralPostgres()
2586+
{
2587+
$builder = $this->getPostgresBuilder();
2588+
$builder->getConnection()->shouldReceive('getDatabaseName');
2589+
$builder->from('users')->joinLateral(function ($q) {
2590+
$q->from('contacts')->whereColumn('contracts.user_id', 'users.id');
2591+
}, 'sub');
2592+
$this->assertSame('select * from "users" inner join lateral (select * from "contacts" where "contracts"."user_id" = "users"."id") as "sub" on true', $builder->toSql());
2593+
}
2594+
2595+
public function testJoinLateralSqlServer()
2596+
{
2597+
$builder = $this->getSqlServerBuilder();
2598+
$builder->getConnection()->shouldReceive('getDatabaseName');
2599+
$builder->from('users')->joinLateral(function ($q) {
2600+
$q->from('contacts')->whereColumn('contracts.user_id', 'users.id');
2601+
}, 'sub');
2602+
$this->assertSame('select * from [users] cross apply (select * from [contacts] where [contracts].[user_id] = [users].[id]) as [sub]', $builder->toSql());
2603+
}
2604+
2605+
public function testJoinLateralWithPrefix()
2606+
{
2607+
$builder = $this->getMySqlBuilder();
2608+
$builder->getConnection()->shouldReceive('getDatabaseName');
2609+
$builder->getGrammar()->setTablePrefix('prefix_');
2610+
$builder->from('users')->joinLateral('select * from `contacts` where `contracts`.`user_id` = `users`.`id`', 'sub');
2611+
$this->assertSame('select * from `prefix_users` inner join lateral (select * from `contacts` where `contracts`.`user_id` = `users`.`id`) as `prefix_sub` on true', $builder->toSql());
2612+
}
2613+
2614+
public function testLeftJoinLateral()
2615+
{
2616+
$builder = $this->getMySqlBuilder();
2617+
$builder->getConnection()->shouldReceive('getDatabaseName');
2618+
2619+
$sub = $this->getMySqlBuilder();
2620+
$sub->getConnection()->shouldReceive('getDatabaseName');
2621+
2622+
$builder->from('users')->leftJoinLateral($sub->from('contacts')->whereColumn('contracts.user_id', 'users.id'), 'sub');
2623+
$this->assertSame('select * from `users` left join lateral (select * from `contacts` where `contracts`.`user_id` = `users`.`id`) as `sub` on true', $builder->toSql());
2624+
2625+
$this->expectException(InvalidArgumentException::class);
2626+
$builder = $this->getBuilder();
2627+
$builder->from('users')->leftJoinLateral(['foo'], 'sub');
2628+
}
2629+
2630+
public function testLeftJoinLateralSqlServer()
2631+
{
2632+
$builder = $this->getSqlServerBuilder();
2633+
$builder->getConnection()->shouldReceive('getDatabaseName');
2634+
$builder->from('users')->leftJoinLateral(function ($q) {
2635+
$q->from('contacts')->whereColumn('contracts.user_id', 'users.id');
2636+
}, 'sub');
2637+
$this->assertSame('select * from [users] outer apply (select * from [contacts] where [contracts].[user_id] = [users].[id]) as [sub]', $builder->toSql());
2638+
}
2639+
25292640
public function testRawExpressionsInSelect()
25302641
{
25312642
$builder = $this->getBuilder();

0 commit comments

Comments
 (0)