Skip to content

Commit 515952a

Browse files
committed
fix: don't needlessly contrain join when the left side is allow to be null
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent 7930199 commit 515952a

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

lib/private/DB/QueryBuilder/Partitioned/PartitionQuery.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function __construct(
2626
public string $joinFromColumn,
2727
public string $joinToColumn,
2828
public string $joinMode,
29+
public string $joinToTableAlias,
2930
) {
3031
if ($joinMode !== self::JOIN_MODE_LEFT && $joinMode !== self::JOIN_MODE_INNER) {
3132
throw new InvalidPartitionedQueryException("$joinMode joins aren't allowed in partitioned queries");

lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,8 @@ public function join($fromAlias, $join, $alias, $condition = null, $joinMode = P
234234
$this->splitQueries[$partition->name] = new PartitionQuery(
235235
$this->newQuery(),
236236
$joinCondition->fromAlias ?? $joinCondition->fromColumn, $joinCondition->toAlias ?? $joinCondition->toColumn,
237-
$joinMode
237+
$joinMode,
238+
$alias,
238239
);
239240
$this->splitQueries[$partition->name]->query->from($join, $alias);
240241
$this->ensureSelect($joinCondition->fromColumn, $joinCondition->fromAlias);
@@ -269,7 +270,8 @@ public function join($fromAlias, $join, $alias, $condition = null, $joinMode = P
269270
$this->splitQueries[$partitionName] = new PartitionQuery(
270271
$this->newQuery(),
271272
$joinCondition->fromAlias ?? $joinCondition->fromColumn, $joinCondition->toAlias ?? $joinCondition->toColumn,
272-
$joinMode
273+
$joinMode,
274+
$alias,
273275
);
274276
$this->ensureSelect($joinCondition->fromColumn, $joinCondition->fromAlias);
275277
$this->ensureSelect($joinCondition->toColumn, $joinCondition->toAlias);
@@ -349,11 +351,14 @@ public function andWhere(...$where) {
349351
if ($where) {
350352
foreach ($this->splitPredicatesByParts($where) as $alias => $predicates) {
351353
if (isset($this->splitQueries[$alias])) {
354+
$mergedPredicate = new CompositeExpression(CompositeExpression::TYPE_AND, $predicates);
352355
// when there is a condition on a table being left-joined it starts to behave as if it's an inner join
353356
// since any joined column that doesn't have the left part will not match the condition
354357
// when there the condition is `$joinToColumn IS NULL` we instead mark the query as excluding the left half
355358
if ($this->splitQueries[$alias]->joinMode === PartitionQuery::JOIN_MODE_LEFT) {
356-
$this->splitQueries[$alias]->joinMode = PartitionQuery::JOIN_MODE_INNER;
359+
if ($this->constraintsTableNotNull($mergedPredicate, $this->splitQueries[$alias]->joinToTableAlias)) {
360+
$this->splitQueries[$alias]->joinMode = PartitionQuery::JOIN_MODE_INNER;
361+
}
357362

358363
$column = $this->quoteHelper->quoteColumnName($this->splitQueries[$alias]->joinToColumn);
359364
foreach ($predicates as $predicate) {
@@ -374,6 +379,33 @@ public function andWhere(...$where) {
374379
return $this;
375380
}
376381

382+
/**
383+
* Check if any part of a predicates constraints any part of a table to be not null
384+
*
385+
* @return bool
386+
*/
387+
private function constraintsTableNotNull($predicate, string $table): bool {
388+
if ($predicate instanceof CompositeExpression) {
389+
if ($predicate->getType() === CompositeExpression::TYPE_OR) {
390+
$all = true;
391+
foreach ($predicate->getParts() as $part) {
392+
$all = $all && $this->constraintsTableNotNull($part, $table);
393+
}
394+
return $all;
395+
} else {
396+
foreach ($predicate->getParts() as $part) {
397+
if ($this->constraintsTableNotNull($part, $table)) {
398+
return true;
399+
}
400+
}
401+
return false;
402+
}
403+
} else {
404+
$predicate = (string)$predicate;
405+
return str_starts_with($predicate, "`$table`.") && !str_ends_with($predicate, "IS NULL");
406+
}
407+
}
408+
377409
private function getPartitionForPredicate(string $predicate): ?PartitionSplit {
378410
foreach ($this->partitions as $partition) {
379411

0 commit comments

Comments
 (0)