From 2c8ee8ed7b93b7d51585042b93499b00cc485d9d Mon Sep 17 00:00:00 2001
From: Christian Kuhn <lolli@schwarzbu.ch>
Date: Tue, 23 May 2017 22:39:31 +0200
Subject: [PATCH] Revert "[BUGFIX] Avoid duplicates if ReferenceIndex is unable
 to finish"

This reverts commit c2a9726c4efb23d76c084e3319b29f74c14ccdce
from review https://review.typo3.org/#/c/50803/
due to regression in #81320

Change-Id: I496e13ce9efe743a5607e12b46a3fdc90e2f17c2
Resolves: #81320
Reverts: #78829
Releases: master, 8.7
Reviewed-on: https://review.typo3.org/52921
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Thomas Hohn <thomas@hohn.dk>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
---
 .../core/Classes/Database/ReferenceIndex.php  |  21 +-
 .../Updates/SysRefindexHashUpdater.php        | 251 ------------------
 typo3/sysext/install/ext_localconf.php        |   2 -
 3 files changed, 6 insertions(+), 268 deletions(-)
 delete mode 100644 typo3/sysext/install/Classes/Updates/SysRefindexHashUpdater.php

diff --git a/typo3/sysext/core/Classes/Database/ReferenceIndex.php b/typo3/sysext/core/Classes/Database/ReferenceIndex.php
index 66fb1f710190..4598632c2075 100644
--- a/typo3/sysext/core/Classes/Database/ReferenceIndex.php
+++ b/typo3/sysext/core/Classes/Database/ReferenceIndex.php
@@ -122,7 +122,7 @@ class ReferenceIndex
      * @var int
      * @see updateRefIndexTable()
      */
-    public $hashVersion = 2;
+    public $hashVersion = 1;
 
     /**
      * Current workspace id
@@ -209,8 +209,8 @@ class ReferenceIndex
 
         $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_refindex');
 
-        // Get current index from database with hash as index using $uidIndexField
-        // No restrictions are needed, since sys_refindex is not a TCA table
+        // Get current index from Database with hash as index using $uidIndexField
+        // no restrictions are needed, since sys_refindex is not a TCA table
         $queryBuilder = $connection->createQueryBuilder();
         $queryBuilder->getRestrictions()->removeAll();
         $queryResult = $queryBuilder->select('hash')->from('sys_refindex')->where(
@@ -248,23 +248,14 @@ class ReferenceIndex
                     if (!is_array($relation)) {
                         continue;
                     }
-
-                    // Exclude sorting from the list of hashed fields as generateRefIndexData()
-                    // can generate arbitrary sorting values
-                    // @see createEntryData_dbRels and createEntryData_fileRels
-                    $relation['hash'] = md5(
-                        implode('///', array_diff_key($relation, ['sorting' => true]))
-                        . '///'
-                        . $this->hashVersion
-                    );
-
-                    // First, check if already indexed and if so, unset that row
-                    // (so in the end we know which rows to remove!)
+                    $relation['hash'] = md5(implode('///', $relation) . '///' . $this->hashVersion);
+                    // First, check if already indexed and if so, unset that row (so in the end we know which rows to remove!)
                     if (isset($currentRelationHashes[$relation['hash']])) {
                         unset($currentRelationHashes[$relation['hash']]);
                         $result['keptNodes']++;
                         $relation['_ACTION'] = 'KEPT';
                     } else {
+                        // If new, add it:
                         if (!$testOnly) {
                             $connection->insert('sys_refindex', $relation);
                         }
diff --git a/typo3/sysext/install/Classes/Updates/SysRefindexHashUpdater.php b/typo3/sysext/install/Classes/Updates/SysRefindexHashUpdater.php
deleted file mode 100644
index ddab44306e4a..000000000000
--- a/typo3/sysext/install/Classes/Updates/SysRefindexHashUpdater.php
+++ /dev/null
@@ -1,251 +0,0 @@
-<?php
-declare(strict_types=1);
-namespace TYPO3\CMS\Install\Updates;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use Doctrine\DBAL\DBALException;
-use Doctrine\DBAL\Platforms\SqlitePlatform;
-use Doctrine\DBAL\Platforms\SQLServerPlatform;
-use TYPO3\CMS\Core\Database\ConnectionPool;
-use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
-/**
- * Storing new hashes without sorting column in sys_refindex
- */
-class SysRefindexHashUpdater extends AbstractUpdate
-{
-    /**
-     * @var string
-     */
-    protected $title = 'Update the hash field of sys_refindex to exclude the sorting field';
-
-    /**
-     * Fields that make up the hash value
-     *
-     * @var array
-     */
-    protected $hashMemberFields = [
-        'tablename',
-        'recuid',
-        'field',
-        'flexpointer',
-        'softref_key',
-        'softref_id',
-        'deleted',
-        'workspace',
-        'ref_table',
-        'ref_uid',
-        'ref_string'
-    ];
-
-    /**
-     * The new hash version
-     *
-     * @var int
-     */
-    protected $hashVersion = 2;
-
-    /**
-     * Checks if an update is needed
-     *
-     * @param string &$description The description for the update
-     * @return bool Whether an update is needed (true) or not (false)
-     * @throws \InvalidArgumentException
-     */
-    public function checkForUpdate(&$description)
-    {
-        if ($this->isWizardDone()) {
-            return false;
-        }
-
-        $description = 'The hash calculation for records within the table sys_refindex was changed'
-            . ' to exclude the sorting field. The records need to be updated with a newly calculated hash.<br />'
-            . '<b>Important:</b> If this online migration times out you can perform an offline update using the'
-            . ' command-line instead of the wizard, by executing the following command: '
-            . '<code>TYPO3_PATH_ROOT=$PWD/web vendor/bin/typo3 referenceindex:update</code>';
-
-        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_refindex');
-
-        // SQLite does not have any helpful string/hash functions, unless the wizard is marked done
-        // we need to assume this updater needs to run.
-        if ($connection->getDatabasePlatform() instanceof SqlitePlatform) {
-            return true;
-        }
-
-        $queryBuilder = $connection->createQueryBuilder();
-        $count = (int)$queryBuilder->count('*')
-            ->from('sys_refindex')
-            ->where($queryBuilder->expr()->neq('hash', $this->calculateHashFragment()))
-            ->execute()
-            ->fetchColumn(0);
-
-        return $count !== 0;
-    }
-
-    /**
-     * Performs the hash update for sys_refindex records
-     *
-     * @param array &$databaseQueries Queries done in this update
-     * @param string &$customMessage Custom messages
-     *
-     * @return bool
-     * @throws \InvalidArgumentException
-     * @throws \Doctrine\DBAL\DBALException
-     * @throws \Doctrine\DBAL\ConnectionException
-     */
-    public function performUpdate(array &$databaseQueries, &$customMessage)
-    {
-        $this->deleteDuplicateRecords();
-
-        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_refindex');
-        $queryBuilder = $connection->createQueryBuilder();
-
-        $statement = $queryBuilder->select('hash', ...$this->hashMemberFields)
-            ->from('sys_refindex')
-            ->where($queryBuilder->expr()->neq('hash', $this->calculateHashFragment()))
-            ->execute();
-
-        $updateQueryBuilder = $connection->createQueryBuilder();
-        $updateQueryBuilder->update('sys_refindex')
-            ->set('hash', $updateQueryBuilder->createPositionalParameter('', \PDO::PARAM_STR), false)
-            ->where(
-                $updateQueryBuilder->expr()->eq(
-                    'hash',
-                    $updateQueryBuilder->createPositionalParameter('', \PDO::PARAM_STR)
-                )
-            );
-        $databaseQueries[] = $updateQueryBuilder->getSQL();
-        $updateStatement = $connection->prepare($updateQueryBuilder->getSQL());
-
-        $connection->beginTransaction();
-        try {
-            while ($row = $statement->fetch()) {
-                $newHash = md5(implode('///', array_diff_key($row, ['hash' => true])) . '///' . $this->hashVersion);
-                $updateStatement->execute([$newHash, $row['hash']]);
-            }
-            $connection->commit();
-            $this->markWizardAsDone();
-        } catch (DBALException $e) {
-            $customMessage = 'SQL-ERROR: ' . htmlspecialchars($e->getPrevious()->getMessage());
-            $connection->rollBack();
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Build the DBMS specific SQL fragment that calculates the MD5 hash for the given fields within the database.
-     *
-     * @return string
-     * @throws \InvalidArgumentException
-     */
-    protected function calculateHashFragment(): string
-    {
-        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_refindex');
-        $databasePlatform = $connection->getDatabasePlatform();
-
-        $quotedFields = array_map(
-            function ($fieldName) use ($connection) {
-                return sprintf('CAST(%s AS CHAR)', $connection->quoteIdentifier($fieldName));
-            },
-            $this->hashMemberFields
-        );
-
-        // Add the new hash version to the list of fields
-        $quotedFields[] = $connection->quote('2');
-
-        if ($databasePlatform instanceof SQLServerPlatform) {
-            $concatFragment = sprintf('CONCAT_WS(%s, %s)', $connection->quote('///'), implode(', ', $quotedFields));
-            return sprintf(
-                'LOWER(CONVERT(NVARCHAR(32),HashBytes(%s, %s), 2))',
-                $connection->quote('MD5'),
-                $concatFragment
-            );
-        } elseif ($databasePlatform instanceof SqlitePlatform) {
-            // SQLite cannot do MD5 in database, so update all records which have a hash
-            return $connection->quote('');
-        } else {
-            $concatFragment = sprintf('CONCAT_WS(%s, %s)', $connection->quote('///'), implode(', ', $quotedFields));
-            return sprintf('LOWER(MD5(%s))', $concatFragment);
-        }
-    }
-
-    /**
-     * Remove records from the sys_refindex table which will end up with identical hash values
-     * when used with hash version 2. These records can show up when the rows are identical in
-     * all fields besides hash and sorting. Due to sorting being ignored in the new hash version
-     * these will end up having identical hashes and resulting in a DUPLICATE KEY violation due
-     * to the hash field being the primary (unique) key.
-     */
-    public function deleteDuplicateRecords()
-    {
-        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_refindex');
-
-        // Find all rows which are identical except for the hash and sorting value
-        $dupesQueryBuilder = $connection->createQueryBuilder();
-        $dupesQueryBuilder->select(...$this->hashMemberFields)
-            ->addSelectLiteral($dupesQueryBuilder->expr()->min('sorting', 'min_sorting'))
-            ->from('sys_refindex')
-            ->groupBy(...$this->hashMemberFields)
-            ->having(
-                $dupesQueryBuilder->expr()->comparison(
-                    $dupesQueryBuilder->expr()->count('sorting'),
-                    ExpressionBuilder::GT,
-                    1
-                )
-            );
-
-        // Find all hashes for rows which would have identical hashes using the new algorithm.
-        // This query will not return the row with the lowest sorting value. In the next step
-        // this will ensure we keep it to be updated to the new hash format.
-        $hashQueryBuilder = $connection->createQueryBuilder();
-        // Add the derived table for finding identical hashes.
-        $hashQueryBuilder->getConcreteQueryBuilder()->from(
-            sprintf('(%s)', $dupesQueryBuilder->getSQL()),
-            $hashQueryBuilder->quoteIdentifier('t')
-        );
-        $hashQueryBuilder->select('s.hash')
-            ->from('sys_refindex', 's')
-            ->where($hashQueryBuilder->expr()->gt('s.sorting', $hashQueryBuilder->quoteIdentifier('t.min_sorting')));
-
-        foreach ($this->hashMemberFields as $field) {
-            $hashQueryBuilder->andWhere(
-                $hashQueryBuilder->expr()->eq('s.' . $field, $hashQueryBuilder->quoteIdentifier('t.' . $field))
-            );
-        }
-
-        // Wrap the previous query in another derived table. This indirection is required to use the
-        // sys_refindex table in the final delete statement as well as in the subselect used to determine
-        // the records to be deleted.
-        $selectorQueryBuilder = $connection->createQueryBuilder()->select('d.hash');
-        $selectorQueryBuilder->getConcreteQueryBuilder()->from(
-            sprintf(('(%s)'), $hashQueryBuilder->getSQL()),
-            $selectorQueryBuilder->quoteIdentifier('d')
-        );
-
-        $deleteQueryBuilder = $connection->createQueryBuilder();
-        $deleteQueryBuilder->delete('sys_refindex')
-            ->where(
-                $deleteQueryBuilder->expr()->comparison(
-                    $deleteQueryBuilder->quoteIdentifier('sys_refindex.hash'),
-                    'IN',
-                    sprintf('(%s)', $selectorQueryBuilder->getSQL())
-                )
-            )
-            ->execute();
-    }
-}
diff --git a/typo3/sysext/install/ext_localconf.php b/typo3/sysext/install/ext_localconf.php
index 6bb198dd2e79..816ce89a38c8 100644
--- a/typo3/sysext/install/ext_localconf.php
+++ b/typo3/sysext/install/ext_localconf.php
@@ -43,8 +43,6 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\TYPO3\CMS\In
     = \TYPO3\CMS\Install\Updates\UploadContentElementUpdate::class;
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\TYPO3\CMS\Install\Updates\MigrateFscStaticTemplateUpdate::class]
     = \TYPO3\CMS\Install\Updates\MigrateFscStaticTemplateUpdate::class;
-$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\TYPO3\CMS\Install\Updates\SysRefindexHashUpdater::class]
-    = \TYPO3\CMS\Install\Updates\SysRefindexHashUpdater::class;
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\TYPO3\CMS\Install\Updates\MigrateFeSessionDataUpdate::class]
     = \TYPO3\CMS\Install\Updates\MigrateFeSessionDataUpdate::class;
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['compatibility7Extension']
-- 
GitLab