diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index ebb833707b11..5e86bd0acff5 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -586,6 +586,39 @@ public function joinSub($query, $as, $first, $operator = null, $second = null, $ return $this->join(new Expression($expression), $first, $operator, $second, $type, $where); } + /** + * Add a lateral join clause to the query. + * + * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query + * @param string $as + * @param string $type + * @return $this + */ + public function joinLateral($query, string $as, string $type = 'inner'): static + { + [$query, $bindings] = $this->createSub($query); + + $expression = '('.$query.') as '.$this->grammar->wrapTable($as); + + $this->addBinding($bindings, 'join'); + + $this->joins[] = $this->newJoinClause($this, $type, new Expression($expression), true); + + return $this; + } + + /** + * Add a lateral left join to the query. + * + * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query + * @param string $as + * @return $this + */ + public function leftJoinLateral($query, string $as): static + { + return $this->joinLateral($query, $as, 'left'); + } + /** * Add a left join to the query. * @@ -718,11 +751,12 @@ public function crossJoinSub($query, $as) * @param \Illuminate\Database\Query\Builder $parentQuery * @param string $type * @param string $table + * @param bool $lateral * @return \Illuminate\Database\Query\JoinClause */ - protected function newJoinClause(self $parentQuery, $type, $table) + protected function newJoinClause(self $parentQuery, $type, $table, $lateral = false) { - return new JoinClause($parentQuery, $type, $table); + return new JoinClause($parentQuery, $type, $table, $lateral); } /** diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index a03cdcb03346..e9a7fc8267a6 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -182,10 +182,28 @@ protected function compileJoins(Builder $query, $joins) $tableAndNestedJoins = is_null($join->joins) ? $table : '('.$table.$nestedJoins.')'; + if ($join->lateral) { + return $this->compileJoinLateral($join, $tableAndNestedJoins); + } + return trim("{$join->type} join {$tableAndNestedJoins} {$this->compileWheres($join)}"); })->implode(' '); } + /** + * Compile a "lateral join" clause. + * + * @param \Illuminate\Database\Query\JoinClause $join + * @param string $expression + * @return string + * + * @throws \RuntimeException + */ + public function compileJoinLateral(JoinClause $join, string $expression): string + { + throw new RuntimeException('This database engine does not support lateral joins.'); + } + /** * Compile the "where" portions of the query. * diff --git a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php index 1365d8efe620..da9941debfc7 100755 --- a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php @@ -3,6 +3,7 @@ namespace Illuminate\Database\Query\Grammars; use Illuminate\Database\Query\Builder; +use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Str; class MySqlGrammar extends Grammar @@ -233,6 +234,18 @@ protected function compileUpdateColumns(Builder $query, array $values) })->implode(', '); } + /** + * Compile a "lateral join" clause. + * + * @param \Illuminate\Database\Query\JoinClause $join + * @param string $expression + * @return string + */ + public function compileJoinLateral(JoinClause $join, string $expression): string + { + return trim("{$join->type} join lateral {$expression} on true"); + } + /** * Compile an "upsert" statement into SQL. * diff --git a/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php index b39a20a0a5d0..8fa5d9952f63 100755 --- a/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php @@ -3,6 +3,7 @@ namespace Illuminate\Database\Query\Grammars; use Illuminate\Database\Query\Builder; +use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Arr; use Illuminate\Support\Str; @@ -385,6 +386,18 @@ protected function compileUpdateColumns(Builder $query, array $values) })->implode(', '); } + /** + * Compile a "lateral join" clause. + * + * @param \Illuminate\Database\Query\JoinClause $join + * @param string $expression + * @return string + */ + public function compileJoinLateral(JoinClause $join, string $expression): string + { + return trim("{$join->type} join lateral {$expression} on true"); + } + /** * Compile an "upsert" statement into SQL. * diff --git a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php index f68722a64bce..6fb32440c568 100755 --- a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php @@ -3,6 +3,7 @@ namespace Illuminate\Database\Query\Grammars; use Illuminate\Database\Query\Builder; +use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Arr; use Illuminate\Support\Str; @@ -386,6 +387,20 @@ protected function compileUpdateWithJoins(Builder $query, $table, $columns, $whe return "update {$alias} set {$columns} from {$table} {$joins} {$where}"; } + /** + * Compile a "lateral join" clause. + * + * @param \Illuminate\Database\Query\JoinClause $join + * @param string $expression + * @return string + */ + public function compileJoinLateral(JoinClause $join, string $expression): string + { + $type = $join->type == 'left' ? 'outer' : 'cross'; + + return trim("{$type} apply {$expression}"); + } + /** * Compile an "upsert" statement into SQL. * diff --git a/src/Illuminate/Database/Query/JoinClause.php b/src/Illuminate/Database/Query/JoinClause.php index aef1c9aa547d..2614bab0e900 100755 --- a/src/Illuminate/Database/Query/JoinClause.php +++ b/src/Illuminate/Database/Query/JoinClause.php @@ -20,6 +20,13 @@ class JoinClause extends Builder */ public $table; + /** + * Indicates if the join is a "lateral" join. + * + * @var bool + */ + public $lateral; + /** * The connection of the parent query builder. * @@ -54,12 +61,14 @@ class JoinClause extends Builder * @param \Illuminate\Database\Query\Builder $parentQuery * @param string $type * @param string $table + * @param bool $lateral * @return void */ - public function __construct(Builder $parentQuery, $type, $table) + public function __construct(Builder $parentQuery, $type, $table, $lateral = false) { $this->type = $type; $this->table = $table; + $this->lateral = $lateral; $this->parentClass = get_class($parentQuery); $this->parentGrammar = $parentQuery->getGrammar(); $this->parentProcessor = $parentQuery->getProcessor();