Skip to content

Commit 132e7ea

Browse files
committed
Add lateral join support to Query Builder
1 parent 7ff37c6 commit 132e7ea

File tree

6 files changed

+123
-3
lines changed

6 files changed

+123
-3
lines changed

src/Illuminate/Database/Query/Builder.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,39 @@ public function joinSub($query, $as, $first, $operator = null, $second = null, $
586586
return $this->join(new Expression($expression), $first, $operator, $second, $type, $where);
587587
}
588588

589+
/**
590+
* Add a lateral join clause to the query.
591+
*
592+
* @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query
593+
* @param string $as
594+
* @param string $type
595+
* @return $this
596+
*/
597+
public function joinLateral($query, $as, $type = 'inner')
598+
{
599+
[$query, $bindings] = $this->createSub($query);
600+
601+
$expression = '('.$query.') as '.$this->grammar->wrapTable($as);
602+
603+
$this->addBinding($bindings, 'join');
604+
605+
$this->joins[] = $this->newJoinClause($this, $type, new Expression($expression), true);
606+
607+
return $this;
608+
}
609+
610+
/**
611+
* Add a lateral left join to the query.
612+
*
613+
* @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query
614+
* @param string $as
615+
* @return $this
616+
*/
617+
public function leftJoinLateral($query, $as)
618+
{
619+
return $this->joinLateral($query, $as, 'left');
620+
}
621+
589622
/**
590623
* Add a left join to the query.
591624
*
@@ -718,11 +751,12 @@ public function crossJoinSub($query, $as)
718751
* @param \Illuminate\Database\Query\Builder $parentQuery
719752
* @param string $type
720753
* @param string $table
754+
* @param bool $lateral
721755
* @return \Illuminate\Database\Query\JoinClause
722756
*/
723-
protected function newJoinClause(self $parentQuery, $type, $table)
757+
protected function newJoinClause(self $parentQuery, $type, $table, $lateral = false)
724758
{
725-
return new JoinClause($parentQuery, $type, $table);
759+
return new JoinClause($parentQuery, $type, $table, $lateral);
726760
}
727761

728762
/**

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ 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+
179183
$table = $this->wrapTable($join->table);
180184

181185
$nestedJoins = is_null($join->joins) ? '' : ' '.$this->compileJoins($query, $join->joins);
@@ -186,6 +190,20 @@ protected function compileJoins(Builder $query, $joins)
186190
})->implode(' ');
187191
}
188192

193+
/**
194+
* Compile a "lateral join" clause.
195+
*
196+
* @param \Illuminate\Database\Query\Builder $query
197+
* @param \Illuminate\Database\Query\JoinClause $join
198+
* @return string
199+
*
200+
* @throws \RuntimeException
201+
*/
202+
public function joinLateral(Builder $query, JoinClause $join)
203+
{
204+
throw new RuntimeException('This database engine does not support lateral joins.');
205+
}
206+
189207
/**
190208
* Compile the "where" portions of the query.
191209
*

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Illuminate\Database\Query\Grammars;
44

55
use Illuminate\Database\Query\Builder;
6+
use Illuminate\Database\Query\JoinClause;
67
use Illuminate\Support\Str;
78

89
class MySqlGrammar extends Grammar
@@ -233,6 +234,24 @@ protected function compileUpdateColumns(Builder $query, array $values)
233234
})->implode(', ');
234235
}
235236

237+
/**
238+
* Compile a "lateral join" clause.
239+
*
240+
* @param \Illuminate\Database\Query\Builder $query
241+
* @param \Illuminate\Database\Query\JoinClause $join
242+
* @return string
243+
*/
244+
public function joinLateral(Builder $query, JoinClause $join)
245+
{
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");
253+
}
254+
236255
/**
237256
* Compile an "upsert" statement into SQL.
238257
*

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Illuminate\Database\Query\Grammars;
44

55
use Illuminate\Database\Query\Builder;
6+
use Illuminate\Database\Query\JoinClause;
67
use Illuminate\Support\Arr;
78
use Illuminate\Support\Str;
89

@@ -385,6 +386,24 @@ protected function compileUpdateColumns(Builder $query, array $values)
385386
})->implode(', ');
386387
}
387388

389+
/**
390+
* Compile a "lateral join" clause.
391+
*
392+
* @param \Illuminate\Database\Query\Builder $query
393+
* @param \Illuminate\Database\Query\JoinClause $join
394+
* @return string
395+
*/
396+
public function joinLateral(Builder $query, JoinClause $join)
397+
{
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");
405+
}
406+
388407
/**
389408
* Compile an "upsert" statement into SQL.
390409
*

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Illuminate\Database\Query\Grammars;
44

55
use Illuminate\Database\Query\Builder;
6+
use Illuminate\Database\Query\JoinClause;
67
use Illuminate\Support\Arr;
78
use Illuminate\Support\Str;
89

@@ -386,6 +387,26 @@ protected function compileUpdateWithJoins(Builder $query, $table, $columns, $whe
386387
return "update {$alias} set {$columns} from {$table} {$joins} {$where}";
387388
}
388389

390+
/**
391+
* Compile a "lateral join" clause.
392+
*
393+
* @param \Illuminate\Database\Query\Builder $query
394+
* @param \Illuminate\Database\Query\JoinClause $join
395+
* @return string
396+
*/
397+
public function joinLateral(Builder $query, JoinClause $join)
398+
{
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+
405+
$type = $join->type == 'left' ? 'outer' : 'cross';
406+
407+
return trim("{$type} apply {$tableAndNestedJoins}");
408+
}
409+
389410
/**
390411
* Compile an "upsert" statement into SQL.
391412
*

src/Illuminate/Database/Query/JoinClause.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ class JoinClause extends Builder
2020
*/
2121
public $table;
2222

23+
/**
24+
* Indicates if the join is a "lateral" join.
25+
*
26+
* @var string
27+
*/
28+
public $lateral;
29+
2330
/**
2431
* The connection of the parent query builder.
2532
*
@@ -54,12 +61,14 @@ class JoinClause extends Builder
5461
* @param \Illuminate\Database\Query\Builder $parentQuery
5562
* @param string $type
5663
* @param string $table
64+
* @param bool $lateral
5765
* @return void
5866
*/
59-
public function __construct(Builder $parentQuery, $type, $table)
67+
public function __construct(Builder $parentQuery, $type, $table, $lateral = false)
6068
{
6169
$this->type = $type;
6270
$this->table = $table;
71+
$this->lateral = $lateral;
6372
$this->parentClass = get_class($parentQuery);
6473
$this->parentGrammar = $parentQuery->getGrammar();
6574
$this->parentProcessor = $parentQuery->getProcessor();

0 commit comments

Comments
 (0)