lateral join

This commit is contained in:
Yuri Kuznetsov
2025-06-07 20:34:31 +03:00
parent d35f00b2cb
commit 6aebcaed97
4 changed files with 87 additions and 9 deletions
+28
View File
@@ -47,6 +47,7 @@ class Join
private ?WhereItem $conditions = null;
private bool $onlyMiddle = false;
private bool $isLateral = false;
private function __construct(
private string|Select $target,
@@ -132,6 +133,16 @@ class Join
return $this->onlyMiddle;
}
/**
* Is LATERAL.
*
* @since 9.1.6
*/
public function isLateral(): bool
{
return $this->isLateral;
}
/**
* Create.
*
@@ -216,4 +227,21 @@ class Join
return $obj;
}
/**
* With LATERAL. Only for a sub-query join.
*
* @since 9.1.6
*/
public function withLateral(bool $isLateral = true): self
{
if (!$this->isSubQuery()) {
throw new LogicException("Lateral can be used only with sub-query joins.");
}
$obj = clone $this;
$obj->isLateral = $isLateral;
return $obj;
}
}
@@ -201,6 +201,7 @@ trait SelectingBuilderTrait
): self {
$onlyMiddle = false;
$isLateral = false;
/** @var string|Join|array<int, mixed> $target */
@@ -208,6 +209,8 @@ trait SelectingBuilderTrait
$alias = $alias ?? $target->getAlias();
$conditions = $conditions ?? $target->getConditions();
$onlyMiddle = $target->isOnlyMiddle();
$isLateral = $target->isLateral();
$target = $target->getTarget();
}
@@ -256,6 +259,10 @@ trait SelectingBuilderTrait
$params['onlyMiddle'] = true;
}
if ($isLateral) {
$params['isLateral'] = true;
}
if ($params !== []) {
$this->params[$type][] = [$target, $alias, $conditions, $params];
@@ -3140,6 +3140,18 @@ abstract class BaseQueryComposer implements QueryComposer
$value = $right;
if ($isNotValue) {
if (is_null($value)) {
return $sql;
}
$rightPart = $this->convertComplexExpression($entity, $value, false, $params);
$sql .= " " . $operator . " " . $rightPart;
return $sql;
}
if (is_null($value)) {
if ($operator === '=') {
$sql .= " IS NULL";
@@ -3150,14 +3162,6 @@ abstract class BaseQueryComposer implements QueryComposer
return $sql;
}
if ($isNotValue) {
$rightPart = $this->convertComplexExpression($entity, $value, false, $params);
$sql .= " " . $operator . " " . $rightPart;
return $sql;
}
$sql .= " " . $operator . " " . $this->quote($value);
return $sql;
@@ -3206,7 +3210,13 @@ abstract class BaseQueryComposer implements QueryComposer
$aliasPart = $this->quoteIdentifier($alias);
$sql = $prefixPart . "JOIN $targetPart AS $aliasPart";
$sql = $prefixPart . "JOIN ";
if (!empty($joinParams['isLateral'])) {
$sql .= "LATERAL ";
}
$sql .= "$targetPart AS $aliasPart";
if ($conditions === []) {
return $sql;
@@ -1057,6 +1057,39 @@ class MysqlQueryComposerTest extends \PHPUnit\Framework\TestCase
);
}
public function testJoinSubQueryLateral(): void
{
$sql =
"SELECT post.id AS `id` FROM `post` " .
"JOIN LATERAL (SELECT post.id AS `id` FROM `post` LIMIT 0, 1) AS `a` ON TRUE";
$select = SelectBuilder::create()
->select('id')
->from('Post')
->join(
Join::createWithSubQuery(
SelectBuilder::create()
->select('id')
->from('Post')
->limit(0, 1)
->withDeleted()
->build(),
'a',
)
->withLateral()
->withConditions(
Expression::value(true)
)
)
->withDeleted()
->build();
$this->assertEquals(
$sql,
$this->query->composeSelect($select)
);
}
public function testJoinSubQueryException1(): void
{
$this->expectException(LogicException::class);