Skip to content

Commit 705a14c

Browse files
committed
perf(files): keep systemtag and filecache joins index-friendly
Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
1 parent a8428f8 commit 705a14c

4 files changed

Lines changed: 25 additions & 3 deletions

File tree

‎lib/private/DB/QueryBuilder/Partitioned/JoinCondition.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ private static function clearConditionPart(string $part): string {
149149
} elseif (str_starts_with($part, 'to_number(to_char(')) {
150150
// oracle cast to int
151151
$part = substr($part, strlen('to_number(to_char('), -2);
152-
} elseif (str_starts_with($part, 'to_number(to_char(')) {
152+
} elseif (str_starts_with($part, 'to_char(')) {
153153
// oracle cast to string
154154
$part = substr($part, strlen('to_char('), -1);
155155
}

‎lib/private/Files/Cache/CacheQueryBuilder.php‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ public function selectTagUsage(): self {
3333
->selectAlias($this->func()->count('filecache.fileid'), 'number_files')
3434
->selectAlias($this->func()->max('filecache.fileid'), 'ref_file_id')
3535
->from('filecache', 'filecache')
36+
// Compare as strings (objectid = CAST(fileid AS CHAR)) so the systag_by_objectid
37+
// index on the string column objectid stays usable; casting objectid to int (or
38+
// the implicit string->number coercion against the bigint fileid) defeats it.
3639
->leftJoin('filecache', 'systemtag_object_mapping', 'systemtagmap', $this->expr()->andX(
37-
$this->expr()->eq('filecache.fileid', $this->expr()->castColumn('systemtagmap.objectid', IQueryBuilder::PARAM_INT)),
40+
$this->expr()->eq('systemtagmap.objectid', $this->expr()->castColumn('filecache.fileid', IQueryBuilder::PARAM_STR)),
3841
$this->expr()->eq('systemtagmap.objecttype', $this->createNamedParameter('files'))
3942
))
4043
->leftJoin('systemtagmap', 'systemtag', 'systemtag', $this->expr()->andX(

‎lib/private/Files/Cache/QuerySearchHelper.php‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,13 @@ public function findUsedTagsInCaches(ISearchQuery $searchQuery, array $caches):
9797
}
9898

9999
protected function equipQueryForSystemTags(CacheQueryBuilder $query, IUser $user): void {
100+
// Compare the join as strings (objectid = CAST(fileid AS CHAR)) instead of
101+
// casting objectid to an integer. objectid is a string column, so casting it
102+
// (or letting MySQL implicitly coerce the string column to a number when comparing
103+
// to the bigint fileid) makes the systag_by_objectid index unusable and turns this
104+
// into a full/BNL join. Casting fileid instead keeps the index on objectid usable.
100105
$query->leftJoin('file', 'systemtag_object_mapping', 'systemtagmap', $query->expr()->andX(
101-
$query->expr()->eq('file.fileid', $query->expr()->castColumn('systemtagmap.objectid', IQueryBuilder::PARAM_INT)),
106+
$query->expr()->eq('systemtagmap.objectid', $query->expr()->castColumn('file.fileid', IQueryBuilder::PARAM_STR)),
102107
$query->expr()->eq('systemtagmap.objecttype', $query->createNamedParameter('files'))
103108
));
104109
$on = $query->expr()->andX($query->expr()->eq('systemtag.id', 'systemtagmap.systemtagid'));

‎tests/lib/DB/QueryBuilder/Partitioned/JoinConditionTest.php‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,18 @@ public function testParseCastCondition(string $platform): void {
7272
$this->assertEquals('f.fileid', $parsed->toColumn);
7373
$this->assertEquals([], $parsed->fromConditions);
7474
}
75+
76+
#[\PHPUnit\Framework\Attributes\DataProvider('platformProvider')]
77+
public function testParseStringCastCondition(string $platform): void {
78+
$query = $this->getBuilder($platform);
79+
80+
// Mirrors the systemtag<->filecache join: the string objectid column is compared
81+
// to fileid cast to a string (so the index on objectid stays usable). The string
82+
// cast must be stripped on every platform when parsing the join condition.
83+
$condition = $query->expr()->eq('m.objectid', $query->expr()->castColumn('f.fileid', IQueryBuilder::PARAM_STR));
84+
$parsed = JoinCondition::parse($condition, 'filecache', 'f', 'm');
85+
$this->assertEquals('m.objectid', $parsed->fromColumn);
86+
$this->assertEquals('f.fileid', $parsed->toColumn);
87+
$this->assertEquals([], $parsed->fromConditions);
88+
}
7589
}

0 commit comments

Comments
 (0)