From 7b130e2fbe368b28a114eef5f5cfa965bcf2d175 Mon Sep 17 00:00:00 2001 From: Wouter Wolters Date: Mon, 17 Aug 2026 18:48:43 +0200 Subject: [PATCH] [TASK] Speed up functional data set imports CSV data set imports currently perform full Doctrine schema introspection for every imported table. PostgreSQL additionally queries several system catalog tables before synchronizing each auto-increment sequence. Use TYPO3's cached schema information to obtain column types and identify the auto-increment column. Skip sequence handling for tables without such a column. Pass the known column name to the sequence reset helper and use PG_GET_SERIAL_SEQUENCE() to synchronize PostgreSQL sequences in a single query. Retain the existing catalog lookup as a fallback for callers that do not provide column metadata. In a complete PostgreSQL functional suite run with the same ten-way split as CI, aggregate runtime decreased from 2:26:36.598 to 2:14:04.534, saving 12:32.064 or 8.55%. The slowest chunk improved from 16:04.463 to 14:47.040, saving 8.03%. All 12,484 tests passed. --- .../Framework/DataHandling/DataSet.php | 16 +++++++++---- Classes/Core/Testbase.php | 23 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Classes/Core/Functional/Framework/DataHandling/DataSet.php b/Classes/Core/Functional/Framework/DataHandling/DataSet.php index e079ddc0..ade2f3d1 100644 --- a/Classes/Core/Functional/Framework/DataHandling/DataSet.php +++ b/Classes/Core/Functional/Framework/DataHandling/DataSet.php @@ -69,14 +69,20 @@ public static function import(string $path): void foreach ($dataSet->getTableNames() as $tableName) { $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName); $platform = $connection->getDatabasePlatform(); - // @todo Check if we can use the cached schema information here instead. - $tableDetails = $connection->createSchemaManager()->introspectTable($tableName); + $columnInfos = $connection->getSchemaInformation()->listTableColumnInfos($tableName); + $autoIncrementColumnName = null; + foreach ($columnInfos as $columnInfo) { + if ($columnInfo->autoincrement) { + $autoIncrementColumnName = $columnInfo->name; + break; + } + } foreach ($dataSet->getElements($tableName) as $element) { // Some DBMS like postgresql are picky about inserting blob types with correct cast, setting // types correctly (like Connection::PARAM_LOB) allows doctrine to create valid SQL $types = []; foreach ($element as $columnName => $columnValue) { - $types[$columnName] = $columnType = $tableDetails->getColumn($columnName)->getType(); + $types[$columnName] = $columnType = $columnInfos[$columnName]->getType(); // JSON-Field data is converted (json-encode'd) within $connection->insert(), and since json field // data can only be provided json encoded in the csv dataset files, we need to decode them here. if ($columnValue !== null && $columnType instanceof JsonType) { @@ -86,7 +92,9 @@ public static function import(string $path): void // Insert the row $connection->insert($tableName, $element, $types); } - Testbase::resetTableSequences($connection, $tableName); + if ($autoIncrementColumnName !== null) { + Testbase::resetTableSequences($connection, $tableName, $autoIncrementColumnName); + } } } diff --git a/Classes/Core/Testbase.php b/Classes/Core/Testbase.php index 9fc1f898..ffbc2a0a 100644 --- a/Classes/Core/Testbase.php +++ b/Classes/Core/Testbase.php @@ -929,14 +929,27 @@ public function createDatabaseStructure(ContainerInterface $container): void } /** - * Perform post processing of database tables after an insert has been performed. - * Doing this once per insert is rather slow, but due to the soft reference behavior - * this needs to be done after every row to ensure consistent results. + * Synchronize an auto-increment sequence after inserting records with explicit IDs. */ - public static function resetTableSequences(Connection $connection, string $tableName): void - { + public static function resetTableSequences( + Connection $connection, + string $tableName, + ?string $autoIncrementColumnName = null + ): void { $platform = $connection->getDatabasePlatform(); if ($platform instanceof DoctrinePostgreSQLPlatform) { + if ($autoIncrementColumnName !== null) { + $connection->executeStatement( + sprintf( + 'SELECT SETVAL(PG_GET_SERIAL_SEQUENCE(%s, %s), COALESCE(MAX(%s), 0)+1, FALSE) FROM %s', + $connection->quote($connection->quoteIdentifier($tableName)), + $connection->quote($autoIncrementColumnName), + $connection->quoteIdentifier($autoIncrementColumnName), + $connection->quoteIdentifier($tableName) + ) + ); + return; + } $queryBuilder = $connection->createQueryBuilder(); $queryBuilder->getRestrictions()->removeAll(); $row = $queryBuilder->select('PGT.schemaname', 'S.relname', 'C.attname', 'T.relname AS tablename')