From 512351a425cd8b01d5236bea295952e48f250169 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 23 Jul 2024 17:10:01 +0200 Subject: [PATCH] fix: fill null values when doing a left join where the left part doesn't exist Signed-off-by: Robin Appelman --- .../Partitioned/PartitionQuery.php | 11 ++++- .../Sharded/ShardedQueryBuilder.php | 46 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/lib/private/DB/QueryBuilder/Partitioned/PartitionQuery.php b/lib/private/DB/QueryBuilder/Partitioned/PartitionQuery.php index 9e97bce67b910..74357870a56d7 100644 --- a/lib/private/DB/QueryBuilder/Partitioned/PartitionQuery.php +++ b/lib/private/DB/QueryBuilder/Partitioned/PartitionQuery.php @@ -23,6 +23,7 @@ namespace OC\DB\QueryBuilder\Partitioned; +use OC\DB\QueryBuilder\Sharded\ShardedQueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder; class PartitionQuery { @@ -34,7 +35,7 @@ class PartitionQuery { public const JOIN_MODE_RIGHT = 'right'; public function __construct( - public IQueryBuilder $query, + public ShardedQueryBuilder $query, public string $joinFromColumn, public string $joinToColumn, public string $joinMode, @@ -55,8 +56,14 @@ public function mergeWith(array $rows): array { $joinFromValues = array_map(function (array $row) use ($joinFromColumn) { return $row[$joinFromColumn]; }, $rows); + $joinFromValues = array_filter($joinFromValues, function($value) { + return $value !== null; + }); $this->query->andWhere($this->query->expr()->in($this->joinToColumn, $this->query->createNamedParameter($joinFromValues, IQueryBuilder::PARAM_STR_ARRAY, ':' . uniqid()))); + $columns = $this->query->getOutputColumns(); + $nullResult = array_combine($this->query->getOutputColumns(), array_fill(0, count($columns), null)); + $s = $this->query->getSQL(); $partitionedRows = $this->query->executeQuery()->fetchAll(); $partitionedRowsByKey = []; @@ -70,7 +77,7 @@ public function mergeWith(array $rows): array { $result[] = array_merge($row, $partitionedRowsByKey[$row[$joinFromColumn]]); } } elseif ($this->joinMode === self::JOIN_MODE_LEFT || $this->joinMode === self::JOIN_MODE_LEFT_NULL) { - $result[] = $row; + $result[] = array_merge($nullResult, $row); } } return $result; diff --git a/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php b/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php index 5fdd59951502a..2514e64e86492 100644 --- a/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php +++ b/lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php @@ -34,6 +34,7 @@ class ShardedQueryBuilder extends ExtendedQueryBuilder { private mixed $lastInsertId = null; private ?IDBConnection $lastInsertConnection = null; private ?int $updateShardKey = null; + private array $outputColumns = []; /** * @param ConnectionAdapter $connection @@ -83,6 +84,40 @@ private function getKeyValue($value): array { } } + public function select(...$selects) { + $this->addOutputColumns($selects); + return parent::select(...$selects); + } + + public function selectAlias($select, $alias) { + $this->addOutputColumns([$alias]); + return parent::selectAlias($select, $alias); + } + + public function selectDistinct($select) { + $this->addOutputColumns([$select]); + return parent::selectDistinct($select); + } + + public function addSelect(...$select) { + $this->addOutputColumns($select); + return parent::addSelect(...$select); + } + + private function addOutputColumns(array $columns) { + foreach ($columns as $column) { + if (is_array($column)) { + $this->addOutputColumns($column); + } elseif (is_string($column) && !str_contains($column, '*')) { + if (str_contains($column, '.')) { + [, $column] = explode('.', $column); + } + $this->outputColumns[] = $column; + } + } + } + + public function where(...$predicates) { return $this->andWhere(...$predicates); } @@ -371,5 +406,14 @@ public function getLastInsertId(): int { } } - + public function getOutputColumns(): array { + return array_unique(array_map(function(string $column) { + if (str_contains($column, '.')) { + [, $column] = explode('.', $column); + return $column; + } else { + return $column; + } + }, $this->outputColumns)); + } }