Skip to content
Snippets Groups Projects
Commit 3e5c0604 authored by Stefan Bürk's avatar Stefan Bürk Committed by Anja Leichsenring
Browse files

[BUGFIX] Adopt doctrine/dbal sqlite autoincrement detection

sqlite basicly knows about two kind of autoincrement column
definition. One which is an alias on the provided "row_id"
field for an integer field which is also the single field
of the primary key index. And the second one with a real
autoincrement notation, which is not an alias like the first
one. Basicly this differes in some behaviours, mainly that
the second one will never reassign ids which has been deleted.
The first one will eventually reassign deleted ids to fill up
spaces in between.

This counts for inserts where the column data has a null value.
If a real value is provided, this will be used.

"ext:indexed_search" defines some tables with integer columns
which are the primary key, which are not flagged as autoincrement
fields. This is not needed as the inserted data always uses pre
calculated hash values.

This works pretty fine on most dbms. sqlite automatically assumes
the autoincrement field (row_id) alias type for creation, which
works so far pretty well.

doctrine/dbal uses 'PRAGMA table_info(<table-name>)' to read
the table structure, which do not containts the information
if the field is created with the AUTOINCREMENT keyword or not.
Reading the table struncture from a sqlite database sets the
"autoincrement: true" value for colums of type integer, if
they are the only included column of an primary key.

On the other side, reading/building the meta schema based on
the TYPO3 "ext_tables.sql" files of extensions and using the
default table definition building based on TCA, the column
will get "autoincrement: false" if the auto_increment keyword
is not used.

This will popup these tables again and again, as it always will
detect the column as changed, because of the not matching column
autoincrement value.

This patch adopts this assumptions from doctrine/dbal and set the
autoincrement to true, if a table has one primary key colum of
type integer without a autoincremet flag. This will create the
real autoincrement column instead, which is a safer variant for it.

doctrine/dbal added this detection based on these assumptions with:
https://github.com/doctrine/dbal/commit/33555d36e7e7d07a5880e01

Resolves: #97447
Releases: main, 11.5
Change-Id: I410da0db7a8f0748e5a8ee099cd36ee121cafa60
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/74362


Tested-by: default avatarcore-ci <typo3@b13.com>
Tested-by: default avatarBenni Mack <benni@typo3.org>
Tested-by: default avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: default avatarOliver Klee <typo3-coding@oliverklee.de>
Reviewed-by: default avatarBenni Mack <benni@typo3.org>
Reviewed-by: default avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
parent c36d12f6
Branches
Tags
No related merge requests found
......@@ -18,9 +18,12 @@ declare(strict_types=1);
namespace TYPO3\CMS\Core\Database\Schema;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaDiff;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\IntegerType;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Schema\Exception\StatementException;
use TYPO3\CMS\Core\Database\Schema\Parser\Parser;
......@@ -61,6 +64,7 @@ class SchemaMigrator
$updateSuggestions = [];
foreach ($connectionPool->getConnectionNames() as $connectionName) {
$this->adoptDoctrineAutoincrementDetectionForSqlite($tables, $connectionPool->getConnectionByName($connectionName));
$connectionMigrator = ConnectionMigrator::create(
$connectionName,
$tables
......@@ -296,4 +300,36 @@ class SchemaMigrator
return $tables;
}
/**
* doctrine/dbal detects both sqlite autoincrement variants (row_id alias and autoincrement) through assumptions
* which have been made. TYPO3 reads the ext_tables.sql files as MySQL/MariaDB variant, thus not setting the
* autoincrement value to true for the row_id alias variant, which leads to a endless missmatch during database
* comparison. This method adopts the doctrine/dbal assumption and apply it to the meta schema to mitigate
* endless database compare detections in these cases.
*
* @see https://github.com/doctrine/dbal/commit/33555d36e7e7d07a5880e01
*
* @param Table[] $tables
*/
protected function adoptDoctrineAutoincrementDetectionForSqlite(array $tables, Connection $connection): void
{
if (!($connection->getDatabasePlatform() instanceof SqlitePlatform)) {
return;
}
array_walk($tables, static function (Table $table): void {
$primaryColumns = $table->getPrimaryKey()?->getColumns() ?? [];
$primaryKeyColumnCount = count($primaryColumns);
$firstPrimaryKeyColumnName = $primaryColumns[0] ?? '';
$singlePrimaryKeyColumn = $table->hasColumn($firstPrimaryKeyColumnName)
? $table->getColumn($firstPrimaryKeyColumnName)
: null;
if ($primaryKeyColumnCount === 1
&& $singlePrimaryKeyColumn !== null
&& $singlePrimaryKeyColumn->getType() instanceof IntegerType
) {
$singlePrimaryKeyColumn->setAutoincrement(true);
}
});
}
}
......@@ -41,22 +41,16 @@ class MaintenanceCest extends AbstractCest
/**
* @throws \Exception
* @todo Marked as skipped for sqlite, as database compare is not clean with ext:indexed_search installed
* and sqlite database backend. Fix related issue and reactivate test afterwards.
*/
public function analyzeDatabaseStructureWorks(ApplicationTester $I): void
{
if ((string)getenv('typo3DatabaseDriver') !== 'pdo_sqlite') {
$I->click('Analyze database');
$I->waitForElementVisible('.modal-dialog');
$I->see('Analyze Database Structure', '.modal-dialog h4');
$I->waitForElementVisible('.callout-success');
$I->see('Database schema is up to date. Good job!', '.callout-success h4');
$I->click('.t3js-modal-close');
$I->waitForElementNotVisible('.modal-dialog');
} else {
$I->markTestSkipped('Skipped. Database compare on sqlite is not clean with ext:indexed_search installed.');
}
$I->click('Analyze database');
$I->waitForElementVisible('.modal-dialog');
$I->see('Analyze Database Structure', '.modal-dialog h4');
$I->waitForElementVisible('.callout-success');
$I->see('Database schema is up to date. Good job!', '.callout-success h4');
$I->click('.t3js-modal-close');
$I->waitForElementNotVisible('.modal-dialog');
}
/**
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment