Skip to content

Commit 3869823

Browse files
committed
[TASK] Avoid costly database schema queries and class state
Some places in our code base need to deal with database schema information, which are quite costly doing them over and over again. This change streamlines occurrences to use the internal cached schema information (`Connection->getSchemaInformation()`) to replace the occurrences and centralizing them to one place. With that, instance state can be removed in two cases and in one case the uncached usage replaced by the cached schema information. `SchemaInformation->listTableColumns()` is added to mimic the `AbstractSchemaManager->listTableColumns()` method and return same structure `array<string, Column>` instead of `Column[]`, considerable not a feature and doable as task because the complete class is marked as `@internal`. Note that `SchemaInformation` uses only a code cache right now and will get a second level (`runtime`) cache in a dedicated change eliminating the need to read it from PHP files over and over again. Resolves: #107390 Releases: main Change-Id: Ia7e60a0691eebf91a2fc284c17b7f424efd406c6 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/90562 Tested-by: Stefan Froemken <froemken@gmail.com> Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Stefan Froemken <froemken@gmail.com> Tested-by: Stefan Bürk <stefan@buerk.tech> Tested-by: core-ci <typo3@b13.com> Reviewed-by: Stefan Bürk <stefan@buerk.tech> Reviewed-by: Benni Mack <benni@typo3.org>
1 parent a8f3ce3 commit 3869823

File tree

4 files changed

+25
-33
lines changed

4 files changed

+25
-33
lines changed

typo3/sysext/core/Classes/Database/Schema/SchemaInformation.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
namespace TYPO3\CMS\Core\Database\Schema;
1919

2020
use Doctrine\DBAL\Connection;
21+
use Doctrine\DBAL\Schema\Column;
2122
use Doctrine\DBAL\Schema\Schema;
2223
use Doctrine\DBAL\Schema\Table;
2324
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
@@ -27,6 +28,8 @@
2728
* specific schema related information. This should only be used in context where no changes are expected to happen.
2829
*
2930
* @internal This class is only for internal core usage and is not part of the public core API.
31+
* @todo Add `runtime` cache as a second layer cache to reduce filesystem operation due to the used filesystem cache.
32+
* @todo Investigate if filesystem cache layer can be removed AFTER runtime level cache has been added.
3033
*/
3134
final class SchemaInformation
3235
{
@@ -65,6 +68,19 @@ public function listTableNames(): array
6568
return $tableNames;
6669
}
6770

71+
/**
72+
* @return array<string, Column>
73+
*/
74+
public function listTableColumns(string $table): array
75+
{
76+
$columnsIndexedByName = [];
77+
$columns = $this->introspectTable($table)->getColumns();
78+
array_walk($columns, static function (Column $column) use (&$columnsIndexedByName): void {
79+
$columnsIndexedByName[$column->getName()] = $column;
80+
});
81+
return $columnsIndexedByName;
82+
}
83+
6884
/**
6985
* Similar to doctrine DBAL/AbstractSchemaManager, but with a cache-layer.
7086
* This is used core internally to auto-add types, for instance in Connection::insert().

typo3/sysext/core/Classes/Resource/Index/MetaDataRepository.php

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,6 @@ class MetaDataRepository implements SingletonInterface
4545
*/
4646
protected $tableName = 'sys_file_metadata';
4747

48-
/**
49-
* Internal storage for database table fields
50-
*
51-
* @var array
52-
*/
53-
protected $tableFields = [];
54-
5548
public function __construct(
5649
protected readonly EventDispatcherInterface $eventDispatcher,
5750
) {}
@@ -228,13 +221,9 @@ public function removeByFileUid($fileUid)
228221
*/
229222
protected function getTableFields(): array
230223
{
231-
if (empty($this->tableFields)) {
232-
$this->tableFields = GeneralUtility::makeInstance(ConnectionPool::class)
233-
->getConnectionForTable($this->tableName)
234-
->createSchemaManager()
235-
->listTableColumns($this->tableName);
236-
}
237-
238-
return $this->tableFields;
224+
return GeneralUtility::makeInstance(ConnectionPool::class)
225+
->getConnectionForTable($this->tableName)
226+
->getSchemaInformation()
227+
->listTableColumns($this->tableName);
239228
}
240229
}

typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,6 @@ class ProcessedFileRepository implements LoggerAwareInterface, SingletonInterfac
3939
{
4040
use LoggerAwareTrait;
4141

42-
/**
43-
* As determining the table columns is a costly operation this is done only once
44-
* during runtime and cached afterward.
45-
*
46-
* @see cleanUnavailableColumns()
47-
*/
48-
protected array $tableColumns = [];
49-
5042
public function __construct(
5143
protected readonly ResourceFactory $factory,
5244
protected readonly TaskTypeRegistry $taskTypeRegistry
@@ -329,15 +321,10 @@ protected function createDomainObject(array $databaseRow): ProcessedFile
329321
*/
330322
protected function cleanUnavailableColumns(array $data): array
331323
{
332-
// As determining the table columns is a costly operation this is done only once during runtime and cached then
333-
if ($this->tableColumns === []) {
334-
$this->tableColumns = GeneralUtility::makeInstance(ConnectionPool::class)
335-
->getConnectionForTable('sys_file_processedfile')
336-
->createSchemaManager()
337-
->listTableColumns('sys_file_processedfile');
338-
}
339-
340-
return array_intersect_key($data, $this->tableColumns);
324+
return array_intersect_key($data, GeneralUtility::makeInstance(ConnectionPool::class)
325+
->getConnectionForTable('sys_file_processedfile')
326+
->getSchemaInformation()
327+
->listTableColumns('sys_file_processedfile'));
341328
}
342329

343330
/**

typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2711,7 +2711,7 @@ protected function search(ServerRequestInterface $request): string
27112711
// Get fields list
27122712
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
27132713
$identifierQuoteCharacter = $this->platformHelper->getIdentifierQuoteCharacter($connection->getDatabasePlatform());
2714-
$tableColumns = $connection->createSchemaManager()->listTableColumns($table);
2714+
$tableColumns = $connection->getSchemaInformation()->listTableColumns($table);
27152715
$normalizedTableColumns = [];
27162716
$fields = [];
27172717
foreach ($tableColumns as $column) {

0 commit comments

Comments
 (0)