From 0efafbe837143dc180ddbdcd0949b1811a88a70f Mon Sep 17 00:00:00 2001
From: Christian Kuhn <lolli@schwarzbu.ch>
Date: Wed, 3 Aug 2016 17:32:33 +0200
Subject: [PATCH] [TASK] Doctrine: Migrate indexed_search part 2

Resolves: #77390
Releases: master
Change-Id: I5e450498295a6dcce4c08dfd2147cd87a3a3af78
Reviewed-on: https://review.typo3.org/49354
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Bamboo TYPO3com <info@typo3.com>
Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
---
 ...ultRows_SQLpointerInIndexSearchChanged.rst |  32 ++
 .../Controller/AdministrationController.php   | 135 +++---
 .../Classes/Controller/SearchController.php   |  78 ++--
 .../Repository/AdministrationRepository.php   | 419 +++++++++++------
 .../Repository/IndexSearchRepository.php      | 440 +++++++++++-------
 .../Classes/Hook/CrawlerHook.php              | 317 ++++++++-----
 .../sysext/indexed_search/Classes/Indexer.php | 383 ++++++++++-----
 .../Classes/Utility/LikeWildcard.php          |  23 +-
 .../Utility/LikeWildcardTest.php              |  37 +-
 9 files changed, 1211 insertions(+), 653 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Breaking-77390-ReturnTypeOfHookGetResultRows_SQLpointerInIndexSearchChanged.rst
 rename typo3/sysext/indexed_search/Tests/{Unit => Functional}/Utility/LikeWildcardTest.php (73%)

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-77390-ReturnTypeOfHookGetResultRows_SQLpointerInIndexSearchChanged.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-77390-ReturnTypeOfHookGetResultRows_SQLpointerInIndexSearchChanged.rst
new file mode 100644
index 000000000000..66bd7e1855f8
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Breaking-77390-ReturnTypeOfHookGetResultRows_SQLpointerInIndexSearchChanged.rst
@@ -0,0 +1,32 @@
+==================================================================================================
+Breaking: #77390 - Expected return type of hook getResultRows_SQLpointer in Indexed Search changed
+==================================================================================================
+
+Description
+===========
+
+As part of the migration of the core code to use Doctrine the expected return value of the hook
+``getResultRows_SQLpointer`` in Indexed Search has been changed.
+
+It is required that :php:``\Doctrine\DBAL\Driver\Statement`` objects are returned instead of the
+previous types :php:``bool`` or :php:``\mysqli_result``.
+
+
+Impact
+======
+
+3rd party extensions implementing the hook :php:``getResultRows_SQLpointer`` need to provide the
+correct return type, otherwise fatal errors will occur when processing the search results.
+
+
+Affected Installations
+======================
+
+Installations using 3rd party extensions that implement the hook :php:``getResultRows_SQLpointer``
+for Indexed Search.
+
+
+Migration
+=========
+
+Migrate the implementation of the hook to provide the expected Doctrine Statement object.
diff --git a/typo3/sysext/indexed_search/Classes/Controller/AdministrationController.php b/typo3/sysext/indexed_search/Classes/Controller/AdministrationController.php
index 5742de04e3be..89637bc9e27a 100644
--- a/typo3/sysext/indexed_search/Classes/Controller/AdministrationController.php
+++ b/typo3/sysext/indexed_search/Classes/Controller/AdministrationController.php
@@ -17,7 +17,6 @@ use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Backend\View\BackendTemplateView;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Database\ConnectionPool;
-use TYPO3\CMS\Core\Database\DatabaseConnection;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
@@ -220,8 +219,12 @@ class AdministrationController extends ActionController
         ));
 
         if ($this->pageUid) {
-            $last24hours = ' AND tstamp > ' . ($GLOBALS['EXEC_TIME'] - 24 * 60 * 60);
-            $last30days = ' AND tstamp > ' . ($GLOBALS['EXEC_TIME'] - 30 * 24 * 60 * 60);
+            $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('index_stat_word')
+                ->expr();
+
+            $last24hours = $expressionBuilder->gt('tstamp', ($GLOBALS['EXEC_TIME'] - 86400));
+            $last30days = $expressionBuilder->gt('tstamp', ($GLOBALS['EXEC_TIME'] - 30 * 86400));
 
             $this->view->assignMultiple(array(
                 'pageUid' => $this->pageUid,
@@ -260,6 +263,7 @@ class AdministrationController extends ActionController
      */
     public function statisticDetailsAction($pageHash = 0)
     {
+        $pageHash = (int)$pageHash;
         // Set back button
         $icon = $this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-up', Icon::SIZE_SMALL);
         $backButton = $this->view->getModuleTemplate()->getDocHeaderComponent()
@@ -274,7 +278,7 @@ class AdministrationController extends ActionController
         $pageHashRow = $queryBuilder
             ->select('*')
             ->from('index_phash')
-            ->where($queryBuilder->expr()->eq('phash', (int)$pageHash))
+            ->where($queryBuilder->expr()->eq('phash', $pageHash))
             ->execute()
             ->fetch();
 
@@ -286,7 +290,7 @@ class AdministrationController extends ActionController
         $debugRow = $queryBuilder
             ->select('*')
             ->from('index_debug')
-            ->where($queryBuilder->expr()->eq('phash', (int)$pageHash))
+            ->where($queryBuilder->expr()->eq('phash', $pageHash))
             ->execute()
             ->fetchAll();
         $debugInfo = array();
@@ -298,13 +302,19 @@ class AdministrationController extends ActionController
         }
         $pageRecord = BackendUtility::getRecord('pages', $pageHashRow['data_page_id']);
         $keywords = is_array($pageRecord) ? array_flip(GeneralUtility::trimExplode(',', $pageRecord['keywords'], true)) : array();
-        $wordRecords = $db->exec_SELECTgetRows(
-            'index_words.*, index_rel.*',
-            'index_rel, index_words',
-            'index_rel.phash = ' . (int)$pageHash . ' AND index_words.wid = index_rel.wid',
-            '',
-            'index_words.baseword'
-        );
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
+        $wordRecords = $queryBuilder
+            ->select('index_words.*', 'index_rel.*')
+            ->from('index_words')
+            ->from('index_rel')
+            ->where(
+                $queryBuilder->expr()->eq('index_rel.phash', $pageHash),
+                $queryBuilder->expr()->eq('index_words.wid', $queryBuilder->quoteIdentifier('index_rel.wid'))
+            )
+            ->orderBy('index_words.baseword')
+            ->execute()
+            ->fetchAll();
         foreach ($wordRecords as $id => $row) {
             if (isset($keywords[$row['baseword']])) {
                 $wordRecords[$id]['is_keyword'] = true;
@@ -327,39 +337,61 @@ class AdministrationController extends ActionController
                 }
             }
         }
-        $this->view->assignMultiple(array(
+
+        // sections
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_section');
+        $sections = $queryBuilder
+            ->select('*')
+            ->from('index_section')
+            ->where($queryBuilder->expr()->eq('phash', $pageHash))
+            ->execute()
+            ->fetchAll();
+
+        // top words
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
+        $topCountWords = $queryBuilder
+            ->select('index_words.baseword', 'index_words.metaphone', 'index_rel.*')
+            ->from('index_words')
+            ->from('index_rel')
+            ->setMaxResults(20)
+            ->where(
+                $queryBuilder->expr()->eq('index_rel.phash', $pageHash),
+                $queryBuilder->expr()->eq('index_words.is_stopword', 0),
+                $queryBuilder->expr()->eq('index_words.wid', $queryBuilder->quoteIdentifier('index_rel.wid'))
+            )
+            ->orderBy('index_rel.count', 'DESC')
+            ->execute()
+            ->fetchAll();
+
+        // top frequency
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
+        $topFrequency = $queryBuilder
+            ->select('index_words.baseword', 'index_words.metaphone', 'index_rel.*')
+            ->from('index_words')
+            ->from('index_rel')
+            ->setMaxResults(20)
+            ->where(
+                $queryBuilder->expr()->eq('index_rel.phash', $pageHash),
+                $queryBuilder->expr()->eq('index_words.is_stopword', 0),
+                $queryBuilder->expr()->eq('index_words.wid', $queryBuilder->quoteIdentifier('index_rel.wid'))
+            )
+            ->orderBy('index_rel.freq', 'DESC')
+            ->execute()
+            ->fetchAll();
+
+        $this->view->assignMultiple([
             'phash' => (int)$pageHash,
             'phashRow' => $pageHashRow,
             'words' => $wordRecords,
-            'sections' => $db->exec_SELECTgetRows(
-                '*',
-                'index_section',
-                'index_section.phash = ' . (int)$pageHash
-            ),
-            'topCount' => $db->exec_SELECTgetRows(
-                'index_words.baseword, index_words.metaphone, index_rel.*',
-                'index_rel, index_words',
-                'index_rel.phash = ' . (int)$pageHash . ' AND index_words.wid = index_rel.wid
-					 AND index_words.is_stopword=0',
-                '',
-                'index_rel.count DESC',
-                 '20'
-             ),
-            'topFrequency' => $db->exec_SELECTgetRows(
-                'index_words.baseword, index_words.metaphone, index_rel.*',
-                'index_rel, index_words',
-                'index_rel.phash = ' . (int)$pageHash . ' AND index_words.wid = index_rel.wid
-					 AND index_words.is_stopword=0',
-                '',
-                'index_rel.freq DESC',
-                '20'
-            ),
+            'sections' => $sections,
+            'topCount' => $topCountWords,
+            'topFrequency' => $topFrequency,
             'debug' => $debugInfo,
             'lexer' => $lexer,
             'metaphone' => $metaphone,
             'page' => $pageRecord,
             'keywords' => $keywords
-    ));
+        ]);
     }
 
     /**
@@ -394,13 +426,20 @@ class AdministrationController extends ActionController
      */
     public function wordDetailAction($id = 0, $pageHash = 0)
     {
-        $rows = $this->getDatabaseConnection()->exec_SELECTgetRows(
-            'index_phash.*, index_section.*, index_rel.*',
-            'index_rel, index_section, index_phash',
-            'index_rel.wid = ' . (int)$id . ' AND index_rel.phash = index_section.phash' . ' AND index_section.phash = index_phash.phash',
-            '',
-            'index_rel.freq DESC'
-        );
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_phash');
+        $rows = $queryBuilder
+            ->select('index_phash.*', 'index_section.*', 'index_rel.*')
+            ->from('index_rel')
+            ->from('index_section')
+            ->from('index_phash')
+            ->where(
+                $queryBuilder->expr()->eq('index_rel.wid', (int)$id),
+                $queryBuilder->expr()->eq('index_rel.phash', $queryBuilder->quoteIdentifier('index_section.phash')),
+                $queryBuilder->expr()->eq('index_section.phash', $queryBuilder->quoteIdentifier('index_phash.phash'))
+            )
+            ->orderBy('index_rel.freq', 'desc')
+            ->execute()
+            ->fetchAll();
 
         $this->view->assignMultiple(array(
             'rows' => $rows,
@@ -469,14 +508,6 @@ class AdministrationController extends ActionController
         return $uriBuilder->reset()->uriFor($action, $parameters, $controller);
     }
 
-    /**
-     * @return DatabaseConnection
-     */
-    protected function getDatabaseConnection()
-    {
-        return $GLOBALS['TYPO3_DB'];
-    }
-
     /**
      * @return BackendUserAuthentication
      */
diff --git a/typo3/sysext/indexed_search/Classes/Controller/SearchController.php b/typo3/sysext/indexed_search/Classes/Controller/SearchController.php
index 2eff078a66f7..7d66c03aafcd 100644
--- a/typo3/sysext/indexed_search/Classes/Controller/SearchController.php
+++ b/typo3/sysext/indexed_search/Classes/Controller/SearchController.php
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\IndexedSearch\Controller;
 
 use TYPO3\CMS\Core\Charset\CharsetConverter;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
 use TYPO3\CMS\Core\Html\HtmlParser;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
@@ -258,7 +259,15 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
             // Create header if we are searching more than one indexing configuration
             if (count($indexCfgs) > 1) {
                 if ($freeIndexUid > 0) {
-                    $indexCfgRec = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('title', 'index_config', 'uid=' . (int)$freeIndexUid . $GLOBALS['TSFE']->cObj->enableFields('index_config'));
+                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                        ->getQueryBuilderForTable('index_config');
+                    $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+                    $indexCfgRec = $queryBuilder
+                        ->select('*')
+                        ->from('index_config')
+                        ->where($queryBuilder->expr()->eq('uid', (int)$freeIndexUid))
+                        ->execute()
+                        ->fetch();
                     $categoryTitle = $indexCfgRec['title'];
                 } else {
                     $categoryTitle = LocalizationUtility::translate('indexingConfigurationHeader.' . $freeIndexUid, 'IndexedSearch');
@@ -1042,11 +1051,16 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
         $blindSettings = $this->settings['blind'];
         if (!$blindSettings['languageUid']) {
             // Add search languages
-            $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_language', '1=1' . $GLOBALS['TSFE']->cObj->enableFields('sys_language'));
-            if ($res) {
-                while ($lang = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
-                    $allOptions[$lang['uid']] = $lang['title'];
-                }
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('sys_language');
+            $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+            $result = $queryBuilder
+                ->select('uid', 'title')
+                ->from('sys_language')
+                ->execute();
+
+            while ($lang = $result->fetch()) {
+                $allOptions[$lang['uid']] = $lang['title'];
             }
             // disable single entries by TypoScript
             $allOptions = $this->removeOptionsFromOptionList($allOptions, $blindSettings['languageUid']);
@@ -1123,11 +1137,17 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
             // add an additional index configuration
             if ($this->settings['defaultFreeIndexUidList']) {
                 $uidList = GeneralUtility::intExplode(',', $this->settings['defaultFreeIndexUidList']);
-                $indexCfgRecords = $this->getDatabaseConnection()->exec_SELECTgetRows('uid,title', 'index_config', 'uid IN (' . implode(',', $uidList) . ')' . $GLOBALS['TSFE']->cObj->enableFields('index_config'), '', '', '', 'uid');
-                foreach ($uidList as $uidValue) {
-                    if (is_array($indexCfgRecords[$uidValue])) {
-                        $allOptions[$uidValue] = $indexCfgRecords[$uidValue]['title'];
-                    }
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getQueryBuilderForTable('index_config');
+                $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+                $result = $queryBuilder
+                    ->select('uid', 'title')
+                    ->from('index_config')
+                    ->where($queryBuilder->expr()->in('uid', $uidList))
+                    ->execute();
+
+                while ($row = $result->fetch()) {
+                    $allOptions[$row['uid']]= $row['title'];
                 }
             }
             // disable single entries by TypoScript
@@ -1299,11 +1319,18 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
     {
         if ($this->settings['displayLevelxAllTypes']) {
             $menu = array();
-            $res = $this->getDatabaseConnection()->exec_SELECTquery('title,uid', 'pages', 'pid=' . (int)$pageUid . $GLOBALS['TSFE']->cObj->enableFields('pages'), '', 'sorting');
-            while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+            $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+            $result = $queryBuilder
+                ->select('uid', 'title')
+                ->from('pages')
+                ->where($queryBuilder->expr()->eq('pid', (int)$pageUid))
+                ->orderBy('sorting')
+                ->execute();
+
+            while ($row = $result->fetch()) {
                 $menu[$row['uid']] = $GLOBALS['TSFE']->sys_page->getPageOverlay($row);
             }
-            $this->getDatabaseConnection()->sql_free_result($res);
         } else {
             $menu = $GLOBALS['TSFE']->sys_page->getMenu($pageUid);
         }
@@ -1365,8 +1392,17 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
      */
     protected function getFirstSysDomainRecordForPage($id)
     {
-        $res = $this->getDatabaseConnection()->exec_SELECTquery('domainName', 'sys_domain', 'pid=' . (int)$id . $GLOBALS['TSFE']->cObj->enableFields('sys_domain'), '', 'sorting');
-        $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_domain');
+        $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
+        $row = $queryBuilder
+            ->select('domainName')
+            ->from('sys_domain')
+            ->where($queryBuilder->expr()->eq('pid', (int)$id))
+            ->orderBy('sorting')
+            ->setMaxResults(1)
+            ->execute()
+            ->fetch();
+
         return rtrim($row['domainName'], '/');
     }
 
@@ -1420,16 +1456,6 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
         return is_object($this->externalParsers[$item_type]) && $this->externalParsers[$item_type]->isMultiplePageExtension($item_type);
     }
 
-    /**
-     * Getter for database connection
-     *
-     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
-     */
-    protected function getDatabaseConnection()
-    {
-        return $GLOBALS['TYPO3_DB'];
-    }
-
     /**
      * Load settings and apply stdWrap to them
      */
diff --git a/typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php b/typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php
index 78deac20fdf0..2258862feebb 100644
--- a/typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php
+++ b/typo3/sysext/indexed_search/Classes/Domain/Repository/AdministrationRepository.php
@@ -20,11 +20,11 @@ use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\QueryHelper;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Dbal\Database\DatabaseConnection;
 use TYPO3\CMS\IndexedSearch\FileContentParser;
 
 /**
@@ -58,16 +58,14 @@ class AdministrationRepository
     public function getGrlistRecord($phash)
     {
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_grlist');
-        $res = $queryBuilder
+        $result = $queryBuilder
             ->select('*')
             ->from('index_grlist')
-            ->where(
-                $queryBuilder->expr()->eq('phash', (int)$phash)
-            )
+            ->where($queryBuilder->expr()->eq('phash', (int)$phash))
             ->execute();
-        $numberOfRows = $res->rowCount();
+        $numberOfRows = $result->rowCount();
         $allRows = [];
-        while ($row = $res->fetch()) {
+        while ($row = $result->fetch()) {
             $row['pcount'] = $numberOfRows;
             $allRows[] = $row;
         }
@@ -117,34 +115,61 @@ class AdministrationRepository
     {
         $result = array();
 
-        $db = $this->getDatabaseConnection();
-        $res = $db->exec_SELECTquery(
-            'count(*) AS pcount,index_phash.*',
-            'index_phash',
-            'item_type<>\'0\'',
-            'phash_grouping,phash,cHashParams,data_filename,data_page_id,data_page_reg1,data_page_type,data_page_mp,gr_list,item_type,item_title,item_description,item_mtime,tstamp,item_size,contentHash,crdate,parsetime,sys_language_uid,item_crdate,externalUrl,recordUid,freeIndexUid,freeIndexSetId',
-            'item_type'
-        );
-        while ($row = $db->sql_fetch_assoc($res)) {
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_phash');
+        $res = $queryBuilder
+            ->select('index_phash.*')
+            ->addSelectLiteral($queryBuilder->expr()->count('*', 'pcount'))
+            ->from('index_phash')
+            ->where($queryBuilder->expr()->neq('item_type', 0))
+            ->groupBy(
+                'phash_grouping',
+                'phash',
+                'cHashParams',
+                'data_filename',
+                'data_page_id',
+                'data_page_reg1',
+                'data_page_type',
+                'data_page_mp',
+                'gr_list',
+                'item_type',
+                'item_title',
+                'item_description',
+                'item_mtime',
+                'tstamp',
+                'item_size',
+                'contentHash',
+                'crdate',
+                'parsetime',
+                'sys_language_uid',
+                'item_crdate',
+                'externalUrl',
+                'recordUid',
+                'freeIndexUid',
+                'freeIndexSetId'
+            )
+            ->orderBy('item_type')
+            ->execute();
+
+        while ($row = $res->fetch()) {
             $this->addAdditionalInformation($row);
 
             $result[] = $row;
 
             if ($row['pcount'] > 1) {
-                $res2 = $db->exec_SELECTquery(
-                    'index_phash.*',
-                    'index_phash',
-                    'phash_grouping=' . (int)$row['phash_grouping'] . ' AND phash<>' . (int)$row['phash']
-                );
-                while ($row2 = $db->sql_fetch_assoc($res2)) {
+                $res2 = $queryBuilder
+                    ->select('*')
+                    ->from('index_phash')
+                    ->where(
+                        $queryBuilder->expr()->eq('phash_grouping', (int)$row['phash_grouping']),
+                        $queryBuilder->expr()->neq('phash', (int)$row['phash'])
+                    )
+                    ->execute();
+                while ($row2 = $res2->fetch()) {
                     $this->addAdditionalInformation($row2);
                     $result[] = $row2;
                 }
-                $db->sql_free_result($res2);
             }
         }
-        $db->sql_free_result($res);
-
         return $result;
     }
 
@@ -192,19 +217,25 @@ class AdministrationRepository
         );
         $revTypes = array_flip($types);
         $revTypes[0] = 'TYPO3 page';
-        $db = $this->getDatabaseConnection();
-        $res = $db->exec_SELECTquery('count(*),item_type', 'index_phash', '', 'item_type', 'item_type');
-        while ($row = $db->sql_fetch_row($res)) {
-            $itemType = $row[1];
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_phash');
+        $res = $queryBuilder
+            ->select('item_type')
+            ->addSelectLiteral($queryBuilder->expr()->count('*', 'count'))
+            ->from('index_phash')
+            ->groupBy('item_type')
+            ->orderBy('item_type')
+            ->execute();
+
+        while ($row = $res->fetch()) {
+            $itemType = $row['item_type'];
             $counts[] = array(
-                'count' => $row[0],
+                'count' => $row['count'],
                 'name' => $revTypes[$itemType],
                 'type' => $itemType,
                 'uniqueCount' => $this->countUniqueTypes($itemType),
             );
         }
-        $db->sql_free_result($res);
-
         return $counts;
     }
 
@@ -216,17 +247,15 @@ class AdministrationRepository
      */
     protected function countUniqueTypes($itemType)
     {
-        $items = array();
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_phash');
-        $res = $queryBuilder
+        $items = $queryBuilder
             ->count('*')
             ->from('index_phash')
             ->where($queryBuilder->expr()->eq('item_type', $queryBuilder->createNamedParameter($itemType)))
             ->groupBy('phash_grouping')
-            ->execute();
-        while ($row = $res->fetch()) {
-            $items[] = $row;
-        }
+            ->execute()
+            ->fetchAll();
+
         return count($items);
     }
 
@@ -239,7 +268,7 @@ class AdministrationRepository
     public function getNumberOfSections($pageHash)
     {
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_section');
-        return $queryBuilder
+        return (int)$queryBuilder
             ->count('phash')
             ->from('index_section')
             ->where($queryBuilder->expr()->eq('phash', (int)$pageHash))
@@ -255,33 +284,60 @@ class AdministrationRepository
     public function getPageStatistic()
     {
         $result = array();
-        $db = $this->getDatabaseConnection();
-        $res = $db->exec_SELECTquery(
-            'count(*) AS pcount,index_phash.*',
-            'index_phash',
-            'data_page_id<>0',
-            'phash_grouping,phash,cHashParams,data_filename,data_page_id,data_page_reg1,data_page_type,data_page_mp,gr_list,item_type,item_title,item_description,item_mtime,tstamp,item_size,contentHash,crdate,parsetime,sys_language_uid,item_crdate,externalUrl,recordUid,freeIndexUid,freeIndexSetId',
-            'data_page_id'
-        );
-        while ($row = $db->sql_fetch_assoc($res)) {
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_phash');
+        $res = $queryBuilder
+            ->select('index_phash.*')
+            ->addSelectLiteral($queryBuilder->expr()->count('*', 'pcount'))
+            ->from('index_phash')
+            ->where($queryBuilder->expr()->neq('data_page_id', 0))
+            ->groupBy(
+                'phash_grouping',
+                'phash',
+                'cHashParams',
+                'data_filename',
+                'data_page_id',
+                'data_page_reg1',
+                'data_page_type',
+                'data_page_mp',
+                'gr_list',
+                'item_type',
+                'item_title',
+                'item_description',
+                'item_mtime',
+                'tstamp',
+                'item_size',
+                'contentHash',
+                'crdate',
+                'parsetime',
+                'sys_language_uid',
+                'item_crdate',
+                'externalUrl',
+                'recordUid',
+                'freeIndexUid',
+                'freeIndexSetId'
+            )
+            ->orderBy('data_page_id')
+            ->execute();
+
+        while ($row = $res->fetch()) {
             $this->addAdditionalInformation($row);
             $result[] = $row;
 
             if ($row['pcount'] > 1) {
-                $res2 = $db->exec_SELECTquery(
-                    'index_phash.*',
-                    'index_phash',
-                    'phash_grouping=' . (int)$row['phash_grouping'] . ' AND phash<>' . (int)$row['phash']
-                );
-                while ($row2 = $db->sql_fetch_assoc($res2)) {
+                $res2 = $queryBuilder
+                    ->select('*')
+                    ->from('index_phash')
+                    ->where(
+                        $queryBuilder->expr()->eq('phash_grouping', (int)$row['phash_grouping']),
+                        $queryBuilder->expr()->neq('phash', (int)$row['phash'])
+                    )
+                    ->execute();
+                while ($row2 = $res2->fetch()) {
                     $this->addAdditionalInformation($row2);
                     $result[] = $row2;
                 }
-                $db->sql_free_result($res2);
             }
         }
-        $db->sql_free_result($res);
-
         return $result;
     }
 
@@ -295,46 +351,34 @@ class AdministrationRepository
      */
     public function getGeneralSearchStatistic($additionalWhere, $pageUid, $max = 50)
     {
-        $queryParts = array(
-            'SELECT' => 'word, COUNT(*) AS c',
-            'FROM' => 'index_stat_word',
-            'WHERE' => sprintf('pageid= %d ' . $additionalWhere, $pageUid),
-            'GROUPBY' => 'word',
-            'ORDERBY' => '',
-            'LIMIT' => (int)$max
-        );
-        $db = $this->getDatabaseConnection();
-        $res = $db->exec_SELECTquery(
-            $queryParts['SELECT'],
-            $queryParts['FROM'],
-            $queryParts['WHERE'],
-            $queryParts['GROUPBY'],
-            $queryParts['ORDERBY'],
-            $queryParts['LIMIT']
-        );
-
-        $count = 0;
-        if ($res) {
-            $count = $db->sql_num_rows($res);
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_stat_word');
+        $queryBuilder
+            ->select('word')
+            ->from('index_stat_word')
+            ->addSelectLiteral($queryBuilder->expr()->count('*', 'c'))
+            ->where($queryBuilder->expr()->eq('pageid', (int)$pageUid))
+            ->groupBy('word')
+            ->setMaxResults((int)$max);
+
+        if (!empty($additionalWhere)) {
+            $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($additionalWhere));
         }
 
-        $db->sql_free_result($res);
+        $result = $queryBuilder->execute();
+        $count = (int)$result->rowCount();
+        $result->closeCursor();
 
         // exist several statistics for this page?
-        if ($count == 0) {
+        if ($count === 0) {
             // Limit access to pages of the current site
-            $secureAddWhere = ' AND pageid IN (' . $this->extGetTreeList((int)$pageUid, 100, 0, '1=1') . ') ';
-            $queryParts['WHERE'] = '1=1 ' . $additionalWhere . $secureAddWhere;
+            $queryBuilder->where(
+                $queryBuilder->expr()->in('pageid', $this->extGetTreeList((int)$pageUid, 100, 0, '1=1')),
+                QueryHelper::stripLogicalOperatorPrefix($additionalWhere)
+            );
         }
 
-        return $db->exec_SELECTgetRows(
-            $queryParts['SELECT'],
-            $queryParts['FROM'],
-            $queryParts['WHERE'],
-            $queryParts['GROUPBY'],
-            $queryParts['ORDERBY'],
-            $queryParts['LIMIT']
-        );
+        return $queryBuilder->execute()->fetchAll();
     }
 
     /**
@@ -384,52 +428,138 @@ class AdministrationRepository
         if ($depth > 0) {
             $tree->getTree((int)$pageId, $depth, '');
         }
-        $db = $this->getDatabaseConnection();
+
         foreach ($tree->tree as $singleLine) {
-            $res = $db->exec_SELECTquery(
-                'ISEC.phash_t3, ISEC.rl0, ISEC.rl1, ISEC.rl2, ISEC.page_id, ISEC.uniqid, ' .
-                'IP.phash, IP.phash_grouping, IP.cHashParams, IP.data_filename, IP.data_page_id, ' .
-                'IP.data_page_reg1, IP.data_page_type, IP.data_page_mp, IP.gr_list, IP.item_type, ' .
-                'IP.item_title, IP.item_description, IP.item_mtime, IP.tstamp, IP.item_size, ' .
-                'IP.contentHash, IP.crdate, IP.parsetime, IP.sys_language_uid, IP.item_crdate, ' .
-                'IP.externalUrl, IP.recordUid, IP.freeIndexUid, IP.freeIndexSetId, count(*) AS count_val',
-                'index_phash IP, index_section ISEC',
-                'IP.phash = ISEC.phash AND ISEC.page_id = ' . (int)$singleLine['row']['uid'],
-                'IP.phash,IP.phash_grouping,IP.cHashParams,IP.data_filename,IP.data_page_id,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2,ISEC.page_id,ISEC.uniqid,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId',
-                'IP.item_type, IP.tstamp',
-                10 + 1
-            );
-            $lines = array();
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_phash');
+            $result = $queryBuilder->select(
+                'ISEC.phash_t3',
+                'ISEC.rl0',
+                'ISEC.rl1',
+                'ISEC.rl2',
+                'ISEC.page_id',
+                'ISEC.uniqid',
+                'IP.phash',
+                'IP.phash_grouping',
+                'IP.cHashParams',
+                'IP.data_filename',
+                'IP.data_page_id',
+                'IP.data_page_reg1',
+                'IP.data_page_type',
+                'IP.data_page_mp',
+                'IP.gr_list',
+                'IP.item_type',
+                'IP.item_title',
+                'IP.item_description',
+                'IP.item_mtime',
+                'IP.tstamp',
+                'IP.item_size',
+                'IP.contentHash',
+                'IP.crdate',
+                'IP.parsetime',
+                'IP.sys_language_uid',
+                'IP.item_crdate',
+                'IP.externalUrl',
+                'IP.recordUid',
+                'IP.freeIndexUid',
+                'IP.freeIndexSetId'
+            )
+            ->addSelectLiteral($queryBuilder->expr()->count('*', 'count_val'))
+            ->from('index_phash', 'IP')
+            ->from('index_section', 'ISEC')
+            ->where(
+                $queryBuilder->expr()->eq('IP.phash', $queryBuilder->quoteIdentifier('ISEC.phash')),
+                $queryBuilder->expr()->eq('ISEC.page_id', (int)$singleLine['row']['uid'])
+            )
+            ->groupBy(
+                'IP.phash',
+                'IP.phash_grouping',
+                'IP.cHashParams',
+                'IP.data_filename',
+                'IP.data_page_id',
+                'IP.data_page_reg1',
+                'IP.data_page_type',
+                'IP.data_page_mp',
+                'IP.gr_list',
+                'IP.item_type',
+                'IP.item_title',
+                'IP.item_description',
+                'IP.item_mtime',
+                'IP.tstamp',
+                'IP.item_size',
+                'IP.contentHash',
+                'IP.crdate',
+                'IP.parsetime',
+                'IP.sys_language_uid',
+                'IP.item_crdate',
+                'ISEC.phash',
+                'ISEC.phash_t3',
+                'ISEC.rl0',
+                'ISEC.rl1',
+                'ISEC.rl2',
+                'ISEC.page_id',
+                'ISEC.uniqid',
+                'IP.externalUrl',
+                'IP.recordUid',
+                'IP.freeIndexUid',
+                'IP.freeIndexSetId'
+            )
+            ->orderBy('IP.item_type')
+            ->addOrderBy('IP.tstamp')
+            ->setMaxResults(11)
+            ->execute();
+
+            $lines = [];
             // Collecting phash values (to remove local indexing for)
             // Traverse the result set of phash rows selected:
-            while ($row = $db->sql_fetch_assoc($res)) {
+            while ($row = $result->fetch()) {
+                $row['icon'] = $this->makeItemTypeIcon($row['item_type']);
                 $this->allPhashListed[] = $row['phash'];
+
                 // Adds a display row:
-                $row['icon'] = $this->makeItemTypeIcon($row['item_type']);
-                $row['wordCount'] = count($db->exec_SELECTgetRows(
-                    'index_words.baseword, index_rel.*',
-                    'index_rel, index_words',
-                    'index_rel.phash = ' . (int)$row['phash'] . ' AND index_words.wid = index_rel.wid',
-                    '',
-                    '',
-                    '',
-                    'baseword'
-                ));
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getQueryBuilderForTable('index_rel');
+                $wordCountResult = $queryBuilder->count('index_words.baseword')
+                    ->from('index_rel')
+                    ->from('index_words')
+                    ->where(
+                        $queryBuilder->expr()->eq('index_rel.phash', (int)$row['phash']),
+                        $queryBuilder->expr()->eq('index_words.wid', $queryBuilder->quoteIdentifier('index_rel.wid'))
+                    )
+                    ->groupBy('index_words.baseword')
+                    ->execute();
+
+                $row['wordCount'] = $wordCountResult->rowCount();
+                $wordCountResult->closeCursor();
 
                 if ($mode === 'content') {
-                    $row['fulltextData'] = $db->exec_SELECTgetSingleRow(
-                        '*',
-                        'index_fulltext',
-                        'phash = ' . $row['phash']);
-                    $wordRecords = $db->exec_SELECTgetRows(
-                        'index_words.baseword, index_rel.*',
-                        'index_rel, index_words',
-                        'index_rel.phash = ' . (int)$row['phash'] . ' AND index_words.wid = index_rel.wid',
-                        '', '', '', 'baseword');
+                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                        ->getQueryBuilderForTable('index_fulltext');
+                    $row['fulltextData'] = $queryBuilder->select('*')
+                        ->from('index_fulltext')
+                        ->where($queryBuilder->expr()->eq('phash', (int)$row['phash']))
+                        ->setMaxResults(1)
+                        ->execute()
+                        ->fetch();
+
+                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                        ->getQueryBuilderForTable('index_rel');
+                    $wordRecords = $queryBuilder->select('index_words.baseword')
+                        ->from('index_rel')
+                        ->from('index_words')
+                        ->where(
+                            $queryBuilder->expr()->eq('index_rel.phash', (int)$row['phash']),
+                            $queryBuilder->expr()->eq(
+                                'index_words.wid',
+                                $queryBuilder->quoteIdentifier('index_rel.wid')
+                            )
+                        )
+                        ->groupBy('index_words.baseword')
+                        ->orderBy('index_words.baseword')
+                        ->execute()
+                        ->fetchAll();
+
                     if (is_array($wordRecords)) {
-                        $indexed_words = array_keys($wordRecords);
-                        sort($indexed_words);
-                        $row['allWords'] = $indexed_words;
+                        $row['allWords'] = array_column($wordRecords, 'baseword');
                     }
                 }
 
@@ -455,7 +585,8 @@ class AdministrationRepository
      */
     protected function extGetTreeList($id, $depth, $begin = 0, $perms_clause)
     {
-        $list = GeneralUtility::makeInstance(FrontendBackendUserAuthentication::class)->extGetTreeList($id, $depth, $begin, $perms_clause);
+        $list = GeneralUtility::makeInstance(FrontendBackendUserAuthentication::class)
+            ->extGetTreeList($id, $depth, $begin, $perms_clause);
 
         if (empty($list)) {
             $list = $id;
@@ -488,7 +619,8 @@ class AdministrationRepository
             $phash = (int)$phash;
             if ($phash > 0) {
                 $idList = array();
-                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_section');
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getQueryBuilderForTable('index_section');
                 $res = $queryBuilder
                     ->select('page_id')
                     ->from('index_section')
@@ -509,15 +641,18 @@ class AdministrationRepository
                 }
 
                 // Removing old registrations for all tables.
-                $tableArr = array('index_phash', 'index_rel', 'index_section', 'index_grlist', 'index_fulltext', 'index_debug');
+                $tableArr = [
+                    'index_phash',
+                    'index_rel',
+                    'index_section',
+                    'index_grlist',
+                    'index_fulltext',
+                    'index_debug'
+                ];
                 foreach ($tableArr as $table) {
-                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
-                    $queryBuilder
-                        ->delete($table)
-                        ->where(
-                            $queryBuilder->expr()->eq('phash', (int)$phash)
-                        )
-                        ->execute();
+                    GeneralUtility::makeInstance(ConnectionPool::class)
+                        ->getConnectionForTable($table)
+                        ->delete($table, ['phash' => (int)$phash]);
                 }
             }
         }
@@ -592,14 +727,6 @@ class AdministrationRepository
         return $this->iconFileNameCache[$itemType];
     }
 
-    /**
-     * @return DatabaseConnection
-     */
-    protected function getDatabaseConnection()
-    {
-        return $GLOBALS['TYPO3_DB'];
-    }
-
     /**
      * @return BackendUserAuthentication
      */
diff --git a/typo3/sysext/indexed_search/Classes/Domain/Repository/IndexSearchRepository.php b/typo3/sysext/indexed_search/Classes/Domain/Repository/IndexSearchRepository.php
index 676ff9c5ef15..27d52f34462b 100644
--- a/typo3/sysext/indexed_search/Classes/Domain/Repository/IndexSearchRepository.php
+++ b/typo3/sysext/indexed_search/Classes/Domain/Repository/IndexSearchRepository.php
@@ -14,6 +14,9 @@ namespace TYPO3\CMS\IndexedSearch\Domain\Repository;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Doctrine\DBAL\Driver\Statement;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\QueryHelper;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
@@ -207,15 +210,15 @@ class IndexSearchRepository
         // Getting SQL result pointer:
         $this->getTimeTracker()->push('Searching result');
         if ($hookObj = &$this->hookRequest('getResultRows_SQLpointer')) {
-            $res = $hookObj->getResultRows_SQLpointer($searchWords, $freeIndexUid);
+            $result = $hookObj->getResultRows_SQLpointer($searchWords, $freeIndexUid);
         } else {
-            $res = $this->getResultRows_SQLpointer($searchWords, $freeIndexUid);
+            $result = $this->getResultRows_SQLpointer($searchWords, $freeIndexUid);
         }
         $this->getTimeTracker()->pull();
         // Organize and process result:
-        if ($res) {
+        if ($result) {
             // Total search-result count
-            $count = $this->getDatabaseConnection()->sql_num_rows($res);
+            $count = $result->rowCount();
             // The pointer is set to the result page that is currently being viewed
             $pointer = MathUtility::forceIntegerInRange($this->resultpagePointer, 0, floor($count / $this->numberOfResults));
             // Initialize result accumulation variables:
@@ -232,7 +235,7 @@ class IndexSearchRepository
             // Now, traverse result and put the rows to be displayed into an array
             // Each row should contain the fields from 'ISEC.*, IP.*' combined
             // + artificial fields "show_resume" (bool) and "result_number" (counter)
-            while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
+            while ($row = $result->fetch()) {
                 // Set first row
                 if (!$c) {
                     $firstRow = $row;
@@ -276,7 +279,7 @@ class IndexSearchRepository
                 }
             }
 
-            $this->getDatabaseConnection()->sql_free_result($res);
+            $result->closeCursor();
 
             return array(
                 'resultRows' => $resultRows,
@@ -294,7 +297,7 @@ class IndexSearchRepository
      *
      * @param array $searchWords Search words
      * @param int $freeIndexUid Pointer to which indexing configuration you want to search in. -1 means no filtering. 0 means only regular indexed content.
-     * @return bool|\mysqli_result
+     * @return Statement
      */
     protected function getResultRows_SQLpointer($searchWords, $freeIndexUid = -1)
     {
@@ -387,10 +390,9 @@ class IndexSearchRepository
             if ($res) {
                 // Get phash list by searching for it:
                 $phashList = array();
-                while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
+                while ($row = $res->fetch()) {
                     $phashList[] = $row['phash'];
                 }
-                $this->getDatabaseConnection()->sql_free_result($res);
                 // Here the phash list are merged with the existing result based on whether we are dealing with OR, NOT or AND operations.
                 if ($c) {
                     switch ($v['oper']) {
@@ -420,16 +422,25 @@ class IndexSearchRepository
      *
      * @param string $wordSel WHERE clause selecting the word from phash
      * @param string $additionalWhereClause Additional AND clause in the end of the query.
-     * @return bool|\mysqli_result SQL result pointer
+     * @return Statement
      */
     protected function execPHashListQuery($wordSel, $additionalWhereClause = '')
     {
-        return $this->getDatabaseConnection()->exec_SELECTquery(
-            'IR.phash',
-            'index_words IW, index_rel IR, index_section ISEC',
-            $wordSel . ' AND IW.wid=IR.wid AND ISEC.phash=IR.phash' . $this->sectionTableWhere() . $additionalWhereClause,
-            'IR.phash'
-        );
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
+        $queryBuilder->select('IR.phash')
+            ->from('index_words', 'IW')
+            ->from('index_rel', 'IR')
+            ->from('index_section', 'ISEC')
+            ->where(
+                QueryHelper::stripLogicalOperatorPrefix($wordSel),
+                $queryBuilder->expr()->eq('IW.wid', $queryBuilder->quoteIdentifier('IR.wid')),
+                $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IR.phash')),
+                QueryHelper::stripLogicalOperatorPrefix($this->sectionTableWhere()),
+                QueryHelper::stripLogicalOperatorPrefix($additionalWhereClause)
+            )
+            ->groupBy('IR.phash');
+
+        return $queryBuilder->execute();
     }
 
     /**
@@ -437,7 +448,7 @@ class IndexSearchRepository
      *
      * @param string $sWord the search word
      * @param int $wildcard Bit-field of Utility\LikeWildcard
-     * @return bool|\mysqli_result SQL result pointer
+     * @return Statement
      */
     protected function searchWord($sWord, $wildcard)
     {
@@ -455,20 +466,23 @@ class IndexSearchRepository
      * Search for one distinct word
      *
      * @param string $sWord the search word
-     * @return bool|\mysqli_result SQL result pointer
+     * @return Statement
      */
     protected function searchDistinct($sWord)
     {
-        $wSel = 'IW.wid=' . $this->md5inthash($sWord);
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_words')
+            ->expr();
+        $wSel = $expressionBuilder->eq('IW.wid', $this->md5inthash($sWord));
         $this->wSelClauses[] = $wSel;
-        return $this->execPHashListQuery($wSel, ' AND is_stopword=0');
+        return $this->execPHashListQuery($wSel, $expressionBuilder->eq('is_stopword', 0));
     }
 
     /**
      * Search for a sentence
      *
      * @param string $sWord the search word
-     * @return bool|\mysqli_result SQL result pointer
+     * @return Statement
      */
     protected function searchSentence($sWord)
     {
@@ -480,25 +494,33 @@ class IndexSearchRepository
             $sWord
         );
 
-        return $this->getDatabaseConnection()->exec_SELECTquery(
-            'ISEC.phash',
-            'index_section ISEC, index_fulltext IFT',
-            $likePart . ' AND ISEC.phash = IFT.phash' . $this->sectionTableWhere(),
-            'ISEC.phash'
-        );
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_section');
+        return $queryBuilder->select('ISEC.phash')
+            ->from('index_section', 'ISEC')
+            ->from('index_fulltext', 'IFT')
+            ->where(
+                QueryHelper::stripLogicalOperatorPrefix($likePart),
+                $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier(('IFT.phash'))),
+                QueryHelper::stripLogicalOperatorPrefix($this->sectionTableWhere())
+            )
+            ->groupBy('ISEC.phash')
+            ->execute();
     }
 
     /**
      * Search for a metaphone word
      *
      * @param string $sWord the search word
-     * @return bool|\mysqli_result SQL result pointer
+     * @return Statement
      */
     protected function searchMetaphone($sWord)
     {
-        $wSel = 'IW.metaphone=' . $sWord;
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_words')
+            ->expr();
+        $wSel = $expressionBuilder->eq('IW.metaphone', $expressionBuilder->literal($sWord));
         $this->wSelClauses[] = $wSel;
-        return $this->execPHashListQuery($wSel, ' AND is_stopword=0');
+        return $this->execPHashListQuery($wSel, $expressionBuilder->eq('is_stopword', 0));
     }
 
     /**
@@ -508,25 +530,37 @@ class IndexSearchRepository
      */
     public function sectionTableWhere()
     {
-        $whereClause = '';
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_section')
+            ->expr();
+
+        $whereClause = $expressionBuilder->andX();
         $match = false;
         if (!($this->searchRootPageIdList < 0)) {
-            $whereClause = ' AND ISEC.rl0 IN (' . $this->searchRootPageIdList . ') ';
+            $whereClause->add(
+                $expressionBuilder->in('ISEC.rl0', GeneralUtility::intExplode(',', $this->searchRootPageIdList, true))
+            );
         }
         if (substr($this->sections, 0, 4) == 'rl1_') {
-            $list = implode(',', GeneralUtility::intExplode(',', substr($this->sections, 4)));
-            $whereClause .= ' AND ISEC.rl1 IN (' . $list . ')';
+            $whereClause->add(
+                $expressionBuilder->in('ISEC.rl1', GeneralUtility::intExplode(',', substr($this->sections, 4)))
+            );
             $match = true;
         } elseif (substr($this->sections, 0, 4) == 'rl2_') {
-            $list = implode(',', GeneralUtility::intExplode(',', substr($this->sections, 4)));
-            $whereClause .= ' AND ISEC.rl2 IN (' . $list . ')';
+            $whereClause->add(
+                $expressionBuilder->in('ISEC.rl2', GeneralUtility::intExplode(',', substr($this->sections, 4)))
+            );
             $match = true;
         } elseif (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'])) {
             // Traversing user configured fields to see if any of those are used to limit search to a section:
             foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'] as $fieldName => $rootLineLevel) {
                 if (substr($this->sections, 0, strlen($fieldName) + 1) == $fieldName . '_') {
-                    $list = implode(',', GeneralUtility::intExplode(',', substr($this->sections, strlen($fieldName) + 1)));
-                    $whereClause .= ' AND ISEC.' . $fieldName . ' IN (' . $list . ')';
+                    $whereClause->add(
+                        $expressionBuilder->in(
+                            'ISEC.' . $fieldName,
+                            GeneralUtility::intExplode(',', substr($this->sections, strlen($fieldName) + 1))
+                        )
+                    );
                     $match = true;
                     break;
                 }
@@ -536,17 +570,20 @@ class IndexSearchRepository
         if (!$match) {
             switch ((string)$this->sections) {
                 case '-1':
-                    $whereClause .= ' AND ISEC.page_id=' . $this->getTypoScriptFrontendController()->id;
+                    $whereClause->add(
+                        $expressionBuilder->eq('ISEC.page_id', (int)$this->getTypoScriptFrontendController()->id)
+                    );
                     break;
                 case '-2':
-                    $whereClause .= ' AND ISEC.rl2=0';
+                    $whereClause->add($expressionBuilder->eq('ISEC.rl2', 0));
                     break;
                 case '-3':
-                    $whereClause .= ' AND ISEC.rl2>0';
+                    $whereClause->add($expressionBuilder->gt('ISEC.rl2', 0));
                     break;
             }
         }
-        return $whereClause;
+
+        return $whereClause->count() ? ' AND ' . $whereClause : '';
     }
 
     /**
@@ -556,25 +593,28 @@ class IndexSearchRepository
      */
     public function mediaTypeWhere()
     {
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_phash')
+            ->expr();
         switch ($this->mediaType) {
             case '0':
-                // '0' => 'Kun TYPO3 sider',
-                $whereClause = ' AND IP.item_type=' . $this->getDatabaseConnection()->fullQuoteStr('0', 'index_phash');
+                // '0' => 'only TYPO3 pages',
+                $whereClause = $expressionBuilder->eq('IP.item_type', $expressionBuilder->literal('0'));
                 break;
             case '-2':
                 // All external documents
-                $whereClause = ' AND IP.item_type!=' . $this->getDatabaseConnection()->fullQuoteStr('0', 'index_phash');
+                $whereClause = $expressionBuilder->neq('IP.item_type', $expressionBuilder->literal('0'));
                 break;
             case false:
-
+                // Intentional fall-through
             case '-1':
                 // All content
                 $whereClause = '';
                 break;
             default:
-                $whereClause = ' AND IP.item_type=' . $this->getDatabaseConnection()->fullQuoteStr($this->mediaType, 'index_phash');
+                $whereClause = $expressionBuilder->eq('IP.item_type', $expressionBuilder->literal($this->mediaType));
         }
-        return $whereClause;
+        return $whereClause ? ' AND ' . $whereClause : '';
     }
 
     /**
@@ -585,10 +625,15 @@ class IndexSearchRepository
     public function languageWhere()
     {
         // -1 is the same as ALL language.
-        if ($this->languageUid >= 0) {
-            return ' AND IP.sys_language_uid=' . (int)$this->languageUid;
+        if ($this->languageUid < 0) {
+            return '';
         }
-        return '';
+
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_phash')
+            ->expr();
+
+        return ' AND ' . $expressionBuilder->eq('IP.sys_language_uid', (int)$this->languageUid);
     }
 
     /**
@@ -600,38 +645,63 @@ class IndexSearchRepository
     public function freeIndexUidWhere($freeIndexUid)
     {
         $freeIndexUid = (int)$freeIndexUid;
-        if ($freeIndexUid >= 0) {
-            // First, look if the freeIndexUid is a meta configuration:
-            $indexCfgRec = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('indexcfgs', 'index_config', 'type=5 AND uid=' . $freeIndexUid . $this->enableFields('index_config'));
-            if (is_array($indexCfgRec)) {
-                $refs = GeneralUtility::trimExplode(',', $indexCfgRec['indexcfgs']);
-                // Default value to protect against empty array.
-                $list = array(-99);
-                foreach ($refs as $ref) {
-                    list($table, $uid) = GeneralUtility::revExplode('_', $ref, 2);
-                    $uid = (int)$uid;
-                    switch ($table) {
-                        case 'index_config':
-                            $idxRec = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', 'index_config', 'uid=' . $uid . $this->enableFields('index_config'));
-                            if ($idxRec) {
-                                $list[] = $uid;
-                            }
-                            break;
-                        case 'pages':
-                            $indexCfgRecordsFromPid = $this->getDatabaseConnection()->exec_SELECTgetRows('uid', 'index_config', 'pid=' . $uid . $this->enableFields('index_config'));
-                            foreach ($indexCfgRecordsFromPid as $idxRec) {
-                                $list[] = $idxRec['uid'];
-                            }
-                            break;
-                    }
+        if ($freeIndexUid < 0) {
+            return '';
+        }
+        // First, look if the freeIndexUid is a meta configuration:
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_config');
+        $queryBuilder->getRestrictions()->removeAll();
+        $indexCfgRec = $queryBuilder->select('indexcfgs')
+            ->from('index_config')
+            ->where(
+                $queryBuilder->expr()->eq('type', 5),
+                $queryBuilder->expr()->eq('uid', $freeIndexUid),
+                QueryHelper::stripLogicalOperatorPrefix($this->enableFields('index_config'))
+            )
+            ->execute()
+            ->fetch();
+
+        if (is_array($indexCfgRec)) {
+            $refs = GeneralUtility::trimExplode(',', $indexCfgRec['indexcfgs']);
+            // Default value to protect against empty array.
+            $list = array(-99);
+            foreach ($refs as $ref) {
+                list($table, $uid) = GeneralUtility::revExplode('_', $ref, 2);
+                $uid = (int)$uid;
+                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getQueryBuilderForTable('index_config');
+                $queryBuilder->getRestrictions()->removeAll();
+                $queryBuilder->select('uid')
+                    ->from('index_config')
+                    ->where(QueryHelper::stripLogicalOperatorPrefix($this->enableFields('index_config')));
+                switch ($table) {
+                    case 'index_config':
+                        $idxRec = $queryBuilder->andWhere($queryBuilder->expr()->eq('uid', $uid))
+                            ->execute()
+                            ->fetch();
+                        if ($idxRec) {
+                            $list[] = $uid;
+                        }
+                        break;
+                    case 'pages':
+                        $indexCfgRecordsFromPid = $queryBuilder->andWhere($queryBuilder->expr()->eq('pid', $uid))
+                            ->execute();
+                        while ($idxRec = $indexCfgRecordsFromPid->fetch()) {
+                            $list[] = $idxRec['uid'];
+                        }
+                        break;
                 }
-                $list = array_unique($list);
-            } else {
-                $list = array($freeIndexUid);
             }
-            return ' AND IP.freeIndexUid IN (' . implode(',', $list) . ')';
+            $list = array_unique($list);
+        } else {
+            $list = [$freeIndexUid];
         }
-        return '';
+
+        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_phash')
+            ->expr();
+        return ' AND ' . $expressionBuilder->in('IP.freeIndexUid', array_map('intval', $list));
     }
 
     /**
@@ -639,28 +709,74 @@ class IndexSearchRepository
      *
      * @param string $list List of phash integers which match the search.
      * @param int $freeIndexUid Pointer to which indexing configuration you want to search in. -1 means no filtering. 0 means only regular indexed content.
-     * @return bool|\mysqli_result Query result pointer
+     * @return Statement
      */
     protected function execFinalQuery($list, $freeIndexUid = -1)
     {
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
+        $queryBuilder->select('ISEC.*', 'IP.*')
+            ->from('index_phash', 'IP')
+            ->from('index_section', 'ISEC')
+            ->where(
+                $queryBuilder->expr()->in('IP.phash', GeneralUtility::intExplode(',', $list, true)),
+                QueryHelper::stripLogicalOperatorPrefix($this->mediaTypeWhere()),
+                QueryHelper::stripLogicalOperatorPrefix($this->languageWhere()),
+                QueryHelper::stripLogicalOperatorPrefix($this->freeIndexUidWhere($freeIndexUid)),
+                $queryBuilder->expr()->eq('IP.phash', $queryBuilder->quoteIdentifier('IR.phash'))
+            )
+            ->groupBy(
+                'IP.phash',
+                'ISEC.phash',
+                'ISEC.phash_t3',
+                'ISEC.rl0',
+                'ISEC.rl1',
+                'ISEC.rl2',
+                'ISEC.page_id',
+                'ISEC.uniqid',
+                'IP.phash_grouping',
+                'IP.data_filename',
+                'IP.data_page_id',
+                'IP.data_page_reg1',
+                'IP.data_page_type',
+                'IP.data_page_mp',
+                'IP.gr_list',
+                'IP.item_type',
+                'IP.item_title',
+                'IP.item_description',
+                'IP.item_mtime',
+                'IP.tstamp',
+                'IP.item_size',
+                'IP.contentHash',
+                'IP.crdate',
+                'IP.parsetime',
+                'IP.sys_language_uid',
+                'IP.item_crdate',
+                'IP.cHashParams',
+                'IP.externalUrl',
+                'IP.recordUid',
+                'IP.freeIndexUid',
+                'IP.freeIndexSetId'
+            );
+
         // Setting up methods of filtering results
         // based on page types, access, etc.
-        $page_join = '';
-        // Indexing configuration clause:
-        $freeIndexUidClause = $this->freeIndexUidWhere($freeIndexUid);
-        // Calling hook for alternative creation of page ID list
         if ($hookObj = $this->hookRequest('execFinalQuery_idList')) {
-            $page_where = $hookObj->execFinalQuery_idList($list);
+            // Calling hook for alternative creation of page ID list
+            $hookWhere = QueryHelper::stripLogicalOperatorPrefix($hookObj->execFinalQuery_idList($list));
+            if (!empty($hookWhere)) {
+                $queryBuilder->andWhere($hookWhere);
+            }
         } elseif ($this->joinPagesForQuery) {
             // Alternative to getting all page ids by ->getTreeList() where
             // "excludeSubpages" is NOT respected.
-            $page_join = ',
-				pages';
-            $page_where = 'pages.uid = ISEC.page_id
-				' . $this->enableFields('pages') . '
-				AND pages.no_search=0
-				AND pages.doktype<200
-			';
+            $queryBuilder->getRestrictions()->removeAll();
+            $queryBuilder->from('pages');
+            $queryBuilder->andWhere(
+                $queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('ISEC.page')),
+                QueryHelper::stripLogicalOperatorPrefix($this->enableFields('pages')),
+                $queryBuilder->expr()->eq('pages.no_search', 0),
+                $queryBuilder->expr()->lt('pages.doktype', 200)
+            );
         } elseif ($this->searchRootPageIdList >= 0) {
             // Collecting all pages IDs in which to search;
             // filtering out ALL pages that are not accessible due to enableFields.
@@ -670,80 +786,85 @@ class IndexSearchRepository
             foreach ($siteIdNumbers as $rootId) {
                 $pageIdList[] = $this->getTypoScriptFrontendController()->cObj->getTreeList(-1 * $rootId, 9999);
             }
-            $page_where = 'ISEC.page_id IN (' . implode(',', $pageIdList) . ')';
-        } else {
-            // Disable everything... (select all)
-            $page_where = '1=1';
+            $queryBuilder->andWhere(
+                $queryBuilder->expr()->in(
+                    'ISEC.page_id',
+                    array_unique(GeneralUtility::intExplode(',', implode(',', $pageIdList), true))
+                )
+            );
         }
         // otherwise select all / disable everything
         // If any of the ranking sortings are selected, we must make a
         // join with the word/rel-table again, because we need to
         // calculate ranking based on all search-words found.
         if (substr($this->sortOrder, 0, 5) === 'rank_') {
+            $queryBuilder
+                ->from('index_words', 'IW')
+                ->from('index_rel', 'IR')
+                ->andWhere(
+                    $queryBuilder->expr()->eq('IW.wid', $queryBuilder->quoteIdentifier('IR.wid')),
+                    $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IR.phash'))
+                );
             switch ($this->sortOrder) {
                 case 'rank_flag':
                     // This gives priority to word-position (max-value) so that words in title, keywords, description counts more than in content.
                     // The ordering is refined with the frequency sum as well.
-                    $grsel = 'MAX(IR.flags) AS order_val1, SUM(IR.freq) AS order_val2';
-                    $orderBy = 'order_val1' . $this->getDescendingSortOrderFlag() . ', order_val2' . $this->getDescendingSortOrderFlag();
+                    $queryBuilder
+                        ->addSelectLiteral(
+                            $queryBuilder->expr()->max('IR.flags', 'order_val1'),
+                            $queryBuilder->expr()->sum('IR.freq', 'order_val2')
+                        )
+                        ->orderBy('order_val1', $this->getDescendingSortOrderFlag())
+                        ->addOrderBy('order_val2', $this->getDescendingSortOrderFlag());
                     break;
                 case 'rank_first':
                     // Results in average position of search words on page.
                     // Must be inversely sorted (low numbers are closer to top)
-                    $grsel = 'AVG(IR.first) AS order_val';
-                    $orderBy = 'order_val' . $this->getDescendingSortOrderFlag(true);
+                    $queryBuilder
+                        ->addSelectLiteral($queryBuilder->expr()->avg('IR.first', 'order_val'))
+                        ->orderBy('order_val', $this->getDescendingSortOrderFlag(true));
                     break;
                 case 'rank_count':
                     // Number of words found
-                    $grsel = 'SUM(IR.count) AS order_val';
-                    $orderBy = 'order_val' . $this->getDescendingSortOrderFlag();
+                    $queryBuilder
+                        ->addSelectLiteral($queryBuilder->expr()->sum('IR.count', 'order_val'))
+                        ->orderBy('order_val', $this->getDescendingSortOrderFlag());
                     break;
                 default:
                     // Frequency sum. I'm not sure if this is the best way to do
                     // it (make a sum...). Or should it be the average?
-                    $grsel = 'SUM(IR.freq) AS order_val';
-                    $orderBy = 'order_val' . $this->getDescendingSortOrderFlag();
+                    $queryBuilder
+                        ->addSelectLiteral($queryBuilder->expr()->sum('IR.freq', 'order_val'))
+                        ->orderBy('order_val', $this->getDescendingSortOrderFlag());
             }
-            $wordSel = '';
+
             if (!empty($this->wSelClauses)) {
-                // So, words are imploded into an OR statement (no "sentence search" should be done here - may deselect results)
-                $wordSel = '(' . implode(' OR ', $this->wSelClauses) . ') AND ';
+                // So, words are combined in an OR statement
+                // (no "sentence search" should be done here - may deselect results)
+                $wordSel = $queryBuilder->expr()->orX();
+                foreach ($this->wSelClauses as $wSelClause) {
+                    $wordSel->add(QueryHelper::stripLogicalOperatorPrefix($wSelClause));
+                }
+                $queryBuilder->andWhere($wordSel);
             }
-            $res = $this->getDatabaseConnection()->exec_SELECTquery(
-                'ISEC.*, IP.*, ' . $grsel,
-                'index_words IW,
-					index_rel IR,
-					index_section ISEC,
-					index_phash IP' . $page_join,
-                $wordSel .
-                'IP.phash IN (' . $list . ') ' .
-                    $this->mediaTypeWhere() . ' ' . $this->languageWhere() . $freeIndexUidClause . '
-					AND IW.wid=IR.wid
-					AND ISEC.phash = IR.phash
-					AND IP.phash = IR.phash
-					AND ' . $page_where,
-                'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2 ,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId',
-                $orderBy
-            );
         } else {
             // Otherwise, if sorting are done with the pages table or other fields,
             // there is no need for joining with the rel/word tables:
             $orderBy = '';
             switch ((string)$this->sortOrder) {
                 case 'title':
-                    $orderBy = 'IP.item_title' . $this->getDescendingSortOrderFlag();
+                    $queryBuilder->orderBy('IP.item_title', $this->getDescendingSortOrderFlag());
                     break;
                 case 'crdate':
-                    $orderBy = 'IP.item_crdate' . $this->getDescendingSortOrderFlag();
+                    $queryBuilder->orderBy('IP.item_crdate', $this->getDescendingSortOrderFlag());
                     break;
                 case 'mtime':
-                    $orderBy = 'IP.item_mtime' . $this->getDescendingSortOrderFlag();
+                    $queryBuilder->orderBy('IP.item_mtime', $this->getDescendingSortOrderFlag());
                     break;
             }
-            $res = $this->getDatabaseConnection()->exec_SELECTquery('ISEC.*, IP.*', 'index_phash IP,index_section ISEC' . $page_join, 'IP.phash IN (' . $list . ') ' . $this->mediaTypeWhere() . $this->languageWhere() . $freeIndexUidClause . '
-							AND IP.phash = ISEC.phash AND ' . $page_where, 'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2 ,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId', $orderBy);
         }
-        return $res;
+
+        return $queryBuilder->execute();
     }
 
     /**
@@ -764,35 +885,44 @@ class IndexSearchRepository
         }
         // Evaluate regularly indexed pages based on item_type:
         // External media:
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_grlist');
         if ($row['item_type']) {
             // For external media we will check the access of the parent page on which the media was linked from.
-            // "phash_t3" is the phash of the parent TYPO3 page row which initiated the indexing of the documents in this section.
-            // So, selecting for the grlist records belonging to the parent phash-row where the current users gr_list exists will help us to know.
-            // If this is NOT found, there is still a theoretical possibility that another user accessible page would display a link, so maybe the resume of such a document here may be unjustified hidden. But better safe than sorry.
-            if ($this->isTableUsed('index_grlist')) {
-                $res = $this->getDatabaseConnection()->exec_SELECTquery('phash', 'index_grlist', 'phash=' . (int)$row['phash_t3'] . ' AND gr_list=' . $this->getDatabaseConnection()->fullQuoteStr($this->frontendUserGroupList, 'index_grlist'));
-            } else {
-                $res = false;
-            }
-            if ($res && $this->getDatabaseConnection()->sql_num_rows($res)) {
-                return true;
-            } else {
+            // "phash_t3" is the phash of the parent TYPO3 page row which initiated the indexing of the documents
+            // in this section. So, selecting for the grlist records belonging to the parent phash-row where the
+            // current users gr_list exists will help us to know. If this is NOT found, there is still a theoretical
+            // possibility that another user accessible page would display a link, so maybe the resume of such a
+            // document here may be unjustified hidden. But better safe than sorry.
+            if (!$this->isTableUsed('index_grlist')) {
                 return false;
             }
+
+            return (bool)$connection->count(
+                'phash',
+                'index_grlist',
+                [
+                    'phash' => (int)$row['phash_t3'],
+                    'gr_list' => $this->frontendUserGroupList
+                ]
+            );
         } else {
             // Ordinary TYPO3 pages:
             if ((string)$row['gr_list'] !== (string)$this->frontendUserGroupList) {
-                // Selecting for the grlist records belonging to the phash-row where the current users gr_list exists. If it is found it is proof that this user has direct access to the phash-rows content although he did not himself initiate the indexing...
-                if ($this->isTableUsed('index_grlist')) {
-                    $res = $this->getDatabaseConnection()->exec_SELECTquery('phash', 'index_grlist', 'phash=' . (int)$row['phash'] . ' AND gr_list=' . $this->getDatabaseConnection()->fullQuoteStr($this->frontendUserGroupList, 'index_grlist'));
-                } else {
-                    $res = false;
-                }
-                if ($res && $this->getDatabaseConnection()->sql_num_rows($res)) {
-                    return true;
-                } else {
+                // Selecting for the grlist records belonging to the phash-row where the current users gr_list exists.
+                // If it is found it is proof that this user has direct access to the phash-rows content although
+                // he did not himself initiate the indexing...
+                if (!$this->isTableUsed('index_grlist')) {
                     return false;
                 }
+
+                return (bool)$connection->count(
+                    'phash',
+                    'index_grlist',
+                    [
+                        'phash' => (int)$row['phash'],
+                        'gr_list' => $this->frontendUserGroupList
+                    ]
+                );
             } else {
                 return true;
             }
@@ -926,16 +1056,6 @@ class IndexSearchRepository
         return $this->joinPagesForQuery;
     }
 
-    /**
-     * Returns the database connection
-     *
-     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
-     */
-    protected function getDatabaseConnection()
-    {
-        return $GLOBALS['TYPO3_DB'];
-    }
-
     /**
      * @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
      */
diff --git a/typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php b/typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php
index 793449476316..91b14aeffdb7 100644
--- a/typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php
+++ b/typo3/sysext/indexed_search/Classes/Hook/CrawlerHook.php
@@ -17,7 +17,9 @@ namespace TYPO3\CMS\IndexedSearch\Hook;
 use TYPO3\CMS\Backend\Form\FormEngine;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
 
 /**
  * Crawler hook for indexed search. Works with the "crawler" extension
@@ -66,25 +68,35 @@ class CrawlerHook
     public function crawler_init(&$pObj)
     {
         // Select all indexing configuration which are waiting to be activated:
-        $indexingConfigurations = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'index_config', 'hidden=0
-				AND (starttime=0 OR starttime<=' . $GLOBALS['EXEC_TIME'] . ')
-				AND timer_next_indexing<' . $GLOBALS['EXEC_TIME'] . '
-				AND set_id=0
-				' . BackendUtility::deleteClause('index_config'));
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_config');
+        $queryBuilder = $connection->createQueryBuilder();
+
+        $result = $queryBuilder->select('*')
+            ->from('index_config')
+            ->where(
+                $queryBuilder->expr()->lt('timer_next_indexing', (int)$GLOBALS['EXEC_TIME']),
+                $queryBuilder->expr()->eq('set_id', 0)
+            )
+            ->execute();
+
         // For each configuration, check if it should be executed and if so, start:
-        foreach ($indexingConfigurations as $cfgRec) {
+        while ($cfgRec = $result->fetch()) {
             // Generate a unique set-ID:
             $setId = GeneralUtility::md5int(microtime());
             // Get next time:
             $nextTime = $this->generateNextIndexingTime($cfgRec);
             // Start process by updating index-config record:
-            $field_array = array(
-                'set_id' => $setId,
-                'timer_next_indexing' => $nextTime,
-                'session_data' => ''
+            $connection->update(
+                'index_config',
+                [
+                    'set_id' => $setId,
+                    'timer_next_indexing' => $nextTime,
+                    'session_data' => ''
+                ],
+                [
+                    'uid' => (int)$cfgRec['uid']
+                ]
             );
-            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_config');
-            $connection->update('index_config', $field_array, ['uid' => (int)$cfgRec['uid']]);
             // Based on configuration type:
             switch ($cfgRec['type']) {
                 case 1:
@@ -176,8 +188,18 @@ class CrawlerHook
     {
         // Indexer configuration ID must exist:
         if ($params['indexConfigUid']) {
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('index_config');
+            $queryBuilder->getRestrictions()->removeAll();
             // Load the indexing configuration record:
-            $cfgRec = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'index_config', 'uid=' . (int)$params['indexConfigUid']);
+            $cfgRec = $queryBuilder
+                ->select('*')
+                ->from('index_config')
+                ->where(
+                    $queryBuilder->expr()->eq('uid', (int)$params['indexConfigUid'])
+                )
+                ->execute()
+                ->fetch();
             if (is_array($cfgRec)) {
                 // Unpack session data:
                 $session_data = unserialize($cfgRec['session_data']);
@@ -214,14 +236,13 @@ class CrawlerHook
                         }
                 }
                 // Save process data which might be modified:
-                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_config');
-                $queryBuilder
-                    ->update('index_config')
-                    ->where(
-                        $queryBuilder->expr()->eq('uid', (int)$cfgRec['uid'])
-                    )
-                    ->set('session_data', serialize($session_data))
-                    ->execute();
+                GeneralUtility::makeInstance(ConnectionPool::class)
+                    ->getConnectionForTable('index_config')
+                    ->update(
+                        'index_config',
+                        ['session_data' => serialize($session_data)],
+                        ['uid' => (int)$cfgRec['uid']]
+                    );
             }
         }
         return array('log' => $params);
@@ -247,26 +268,41 @@ class CrawlerHook
             }
             // Init:
             $pid = (int)$cfgRec['alternative_source_pid'] ?: $cfgRec['pid'];
-            $numberOfRecords = $cfgRec['recordsbatch'] ? \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($cfgRec['recordsbatch'], 1) : 100;
+            $numberOfRecords = $cfgRec['recordsbatch']
+                ? MathUtility::forceIntegerInRange($cfgRec['recordsbatch'], 1)
+                : 100;
+
             // Get root line:
-            $rl = $this->getUidRootLineForClosestTemplate($cfgRec['pid']);
+            $rootLine = $this->getUidRootLineForClosestTemplate($cfgRec['pid']);
             // Select
-            $recs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', $cfgRec['table2index'], 'pid = ' . $pid . '
-							AND uid > ' . (int)$session_data['uid'] . BackendUtility::deleteClause($cfgRec['table2index']) . BackendUtility::BEenableFields($cfgRec['table2index']), '', 'uid', $numberOfRecords);
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable($cfgRec['table2index']);
+
+            $result = $queryBuilder->select('*')
+                ->from($cfgRec['table2index'])
+                ->where(
+                    $queryBuilder->expr()->eq('pid', $pid),
+                    $queryBuilder->expr()->gt('uid', (int)$session_data['uid'])
+                )
+                ->setMaxResults($numberOfRecords)
+                ->orderBy('uid')
+                ->execute();
+
             // Traverse:
-            if (!empty($recs)) {
-                foreach ($recs as $r) {
-                    // Index single record:
-                    $this->indexSingleRecord($r, $cfgRec, $rl);
-                    // Update the UID we last processed:
-                    $session_data['uid'] = $r['uid'];
-                }
-                // Finally, set entry for next indexing of batch of records:
-                $nparams = array(
+            while ($row = $result->fetch()) {
+                // Index single record:
+                $this->indexSingleRecord($row, $cfgRec, $rootLine);
+                // Update the UID we last processed:
+                $session_data['uid'] = $row['uid'];
+            }
+
+            // Finally, set entry for next indexing of batch of records:
+            if ($result->rowCount()) {
+                $nparams = [
                     'indexConfigUid' => $cfgRec['uid'],
-                    'url' => 'Records from UID#' . ($r['uid'] + 1) . '-?',
-                    'procInstructions' => array('[Index Cfg UID#' . $cfgRec['uid'] . ']')
-                );
+                    'url' => 'Records from UID#' . ($session_data['uid'] + 1) . '-?',
+                    'procInstructions' => ['[Index Cfg UID#' . $cfgRec['uid'] . ']']
+                ];
                 $pObj->addQueueEntry_callBack($cfgRec['set_id'], $nparams, $this->callBack, $cfgRec['pid']);
             }
         }
@@ -404,22 +440,33 @@ class CrawlerHook
         // Add subpages to log now:
         if ($params['depth'] < $cfgRec['depth']) {
             // Subpages selected
-            $recs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title', 'pages', 'pid = ' . $pageUid . BackendUtility::deleteClause('pages'));
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+            $result = $queryBuilder->select('uid', 'title')
+                ->from('pages')
+                ->where($queryBuilder->expr()->eq('pid', $pageUid))
+                ->execute();
             // Traverse subpages and add to queue:
-            if (!empty($recs)) {
-                foreach ($recs as $r) {
-                    $this->instanceCounter++;
-                    $url = 'pages:' . $r['uid'] . ': ' . $r['title'];
-                    $session_data['urlLog'][] = $url;
-                    // Parameters:
-                    $nparams = array(
-                        'indexConfigUid' => $cfgRec['uid'],
-                        'url' => $r['uid'],
-                        'procInstructions' => array('[Index Cfg UID#' . $cfgRec['uid'] . ']'),
-                        'depth' => $params['depth'] + 1
-                    );
-                    $pObj->addQueueEntry_callBack($cfgRec['set_id'], $nparams, $this->callBack, $cfgRec['pid'], $GLOBALS['EXEC_TIME'] + $this->instanceCounter * $this->secondsPerExternalUrl);
-                }
+            while ($row = $result->fetch()) {
+                $this->instanceCounter++;
+                $url = 'pages:' . $row['uid'] . ': ' . $row['title'];
+                $session_data['urlLog'][] = $url;
+                // Parameters:
+                $nparams = array(
+                    'indexConfigUid' => $cfgRec['uid'],
+                    'url' => $row['uid'],
+                    'procInstructions' => array('[Index Cfg UID#' . $cfgRec['uid'] . ']'),
+                    'depth' => $params['depth'] + 1
+                );
+                $pObj->addQueueEntry_callBack(
+                    $cfgRec['set_id'],
+                    $nparams,
+                    $this->callBack,
+                    $cfgRec['pid'],
+                    $GLOBALS['EXEC_TIME'] + $this->instanceCounter * $this->secondsPerExternalUrl
+                );
             }
         }
     }
@@ -442,29 +489,62 @@ class CrawlerHook
             'index_debug'
         ];
 
+        $queryBuilder = $connectionPool->getQueryBuilderForTable('index_config');
+        $queryBuilder->getRestrictions()
+            ->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
         // Lookup running index configurations:
-        $runningIndexingConfigurations = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,set_id', 'index_config', 'set_id<>0' . BackendUtility::deleteClause('index_config'));
-        // For each running configuration, look up how many log entries there are which are scheduled for execution and if none, clear the "set_id" (means; Processing was DONE)
+        $runningIndexingConfigurations = $queryBuilder->select('*')
+            ->from('index_config')
+            ->where($queryBuilder->expr()->neq('set_id', 0))
+            ->execute()
+            ->fetchAll();
+        // For each running configuration, look up how many log entries there are which are scheduled
+        // for execution and if none, clear the "set_id" (means; Processing was DONE)
         foreach ($runningIndexingConfigurations as $cfgRec) {
             // Look for ended processes:
-            $queued_items = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('*', 'tx_crawler_queue', 'set_id=' . (int)$cfgRec['set_id'] . ' AND exec_time=0');
+            $queued_items = $connectionPool->getConnectionForTable('tx_crawler_queue')
+                ->count(
+                    '*',
+                    'tx_crawler_queue',
+                    [
+                        'set_id' => (int)$cfgRec['set_id'],
+                        'exec_time' => 0
+                    ]
+                );
             if (!$queued_items) {
                 // Lookup old phash rows:
-                $oldPhashRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('phash', 'index_phash', 'freeIndexUid=' . (int)$cfgRec['uid'] . ' AND freeIndexSetId<>' . (int)$cfgRec['set_id']);
+                $queryBuilder = $connectionPool->getQueryBuilderForTable('index_phash');
+                $oldPhashRows = $queryBuilder
+                    ->select('phash')
+                    ->from('index_phash')
+                    ->where(
+                        $queryBuilder->expr()->eq('freeIndexUid', (int)$cfgRec['uid']),
+                        $queryBuilder->expr()->neq('freeIndexSetId', (int)$cfgRec['set_id'])
+                    )
+                    ->execute()
+                    ->fetchAll();
+
+                $oldPhashRows = array_map('intval', array_column($oldPhashRows, 'phash'));
                 // Removing old registrations for all tables
                 foreach ($tablesToClean as $table) {
                     $queryBuilder = $connectionPool->getQueryBuilderForTable($table);
                     $queryBuilder->delete($table)
-                        ->where($queryBuilder->expr()->in('phash', array_column($oldPhashRows, 'phash')))
+                        ->where($queryBuilder->expr()->in('phash', $oldPhashRows))
                         ->execute();
                 }
 
                 // End process by updating index-config record:
-                $field_array = array(
-                    'set_id' => 0,
-                    'session_data' => ''
-                );
-                $GLOBALS['TYPO3_DB']->exec_UPDATEquery('index_config', 'uid=' . (int)$cfgRec['uid'], $field_array);
+                $connectionPool->getConnectionForTable('index_config')
+                    ->update(
+                        'index_config',
+                        [
+                            'set_id' => 0,
+                            'session_data' => ''
+                        ],
+                        ['uid' => (int)$cfgRec['uid']]
+                    );
             }
         }
     }
@@ -618,8 +698,8 @@ class CrawlerHook
             $aMidNight = mktime(0, 0, 0, date('m', $lastTime), date('d', $lastTime), date('y', $lastTime));
         }
         // Find last offset time plus frequency in seconds:
-        $lastSureOffset = $aMidNight + \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($cfgRec['timer_offset'], 0, 86400);
-        $frequencySeconds = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($cfgRec['timer_frequency'], 1);
+        $lastSureOffset = $aMidNight + MathUtility::forceIntegerInRange($cfgRec['timer_offset'], 0, 86400);
+        $frequencySeconds = MathUtility::forceIntegerInRange($cfgRec['timer_frequency'], 1);
         // Now, find out how many blocks of the length of frequency there is until the next time:
         $frequencyBlocksUntilNextTime = ceil(($currentTime - $lastSureOffset) / $frequencySeconds);
         // Set next time to the offset + the frequencyblocks multiplied with the frequency length in seconds.
@@ -672,25 +752,35 @@ class CrawlerHook
      */
     public function deleteFromIndex($id)
     {
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
+
         // Lookup old phash rows:
-        $oldPhashRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('phash', 'index_section', 'page_id=' . (int)$id);
-        if (!empty($oldPhashRows)) {
-            $pHashesToDelete = array_column($oldPhashRows, 'phash');
-            $tables = array(
-                'index_debug',
-                'index_fulltext',
-                'index_grlist',
-                'index_phash',
-                'index_rel',
-                'index_section',
-            );
-            foreach ($tables as $table) {
-                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                    ->getQueryBuilderForTable($table);
-                $queryBuilder->delete($table)
-                    ->where($queryBuilder->expr()->in('phash', $pHashesToDelete))
-                    ->execute();
-            }
+
+        $queryBuilder = $connectionPool->getQueryBuilderForTable('index_section');
+        $oldPhashRows = $queryBuilder->select('phash')
+            ->from('index_section')
+            ->where($queryBuilder->expr()->eq('page_id', (int)$id))
+            ->execute()
+            ->fetchAll();
+
+        if (empty($oldPhashRows)) {
+            return;
+        }
+
+        $pHashesToDelete = array_map('intval', array_column($oldPhashRows, 'phash'));
+        $tables = array(
+            'index_debug',
+            'index_fulltext',
+            'index_grlist',
+            'index_phash',
+            'index_rel',
+            'index_section',
+        );
+        foreach ($tables as $table) {
+            $queryBuilder = $connectionPool->getQueryBuilderForTable($table);
+            $queryBuilder->delete($table)
+                ->where($queryBuilder->expr()->in('phash', $pHashesToDelete))
+                ->execute();
         }
     }
 
@@ -730,32 +820,43 @@ class CrawlerHook
     public function processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $pObj)
     {
         // Check if any fields are actually updated:
-        if (!empty($fieldArray)) {
-            // Translate new ids.
-            if ($status === 'new') {
-                $id = $pObj->substNEWwithIDs[$id];
-            } elseif ($table === 'pages' && $status === 'update' && (array_key_exists('hidden', $fieldArray) && $fieldArray['hidden'] == 1 || array_key_exists('no_search', $fieldArray) && $fieldArray['no_search'] == 1)) {
-                // If the page should be hidden or not indexed after update, delete index for this page
-                $this->deleteFromIndex($id);
-            }
-            // Get full record and if exists, search for indexing configurations:
-            $currentRecord = BackendUtility::getRecord($table, $id);
-            if (is_array($currentRecord)) {
-                // Select all (not running) indexing configurations of type "record" (1) and which points to this table and is located on the same page as the record or pointing to the right source PID
-                $indexingConfigurations = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'index_config', 'hidden=0
-						AND (starttime=0 OR starttime<=' . $GLOBALS['EXEC_TIME'] . ')
-						AND set_id=0
-						AND type=1
-						AND table2index=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'index_config') . '
-						AND (
-								(alternative_source_pid=0 AND pid=' . (int)$currentRecord['pid'] . ')
-								OR (alternative_source_pid=' . (int)$currentRecord['pid'] . ')
-							)
-						AND records_indexonchange=1
-						' . BackendUtility::deleteClause('index_config'));
-                foreach ($indexingConfigurations as $cfgRec) {
-                    $this->indexSingleRecord($currentRecord, $cfgRec);
-                }
+        if (empty($fieldArray)) {
+            return;
+        }
+        // Translate new ids.
+        if ($status === 'new') {
+            $id = $pObj->substNEWwithIDs[$id];
+        } elseif ($table === 'pages' && $status === 'update' && (array_key_exists('hidden', $fieldArray) && $fieldArray['hidden'] == 1 || array_key_exists('no_search', $fieldArray) && $fieldArray['no_search'] == 1)) {
+            // If the page should be hidden or not indexed after update, delete index for this page
+            $this->deleteFromIndex($id);
+        }
+        // Get full record and if exists, search for indexing configurations:
+        $currentRecord = BackendUtility::getRecord($table, $id);
+        if (is_array($currentRecord)) {
+            // Select all (not running) indexing configurations of type "record" (1) and
+            // which points to this table and is located on the same page as the record
+            // or pointing to the right source PID
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('index_config');
+            $result = $queryBuilder->select('*')
+                ->from('index_config')
+                ->where(
+                    $queryBuilder->expr()->eq('set_id', 0),
+                    $queryBuilder->expr()->eq('type', 1),
+                    $queryBuilder->expr()->eq('table2index', $queryBuilder->createNamedParameter($table)),
+                    $queryBuilder->expr()->orX(
+                        $queryBuilder->expr()->andX(
+                            $queryBuilder->expr()->eq('alternative_source_pid', 0),
+                            $queryBuilder->expr()->eq('pid', (int)$currentRecord['pid'])
+                        ),
+                        $queryBuilder->expr()->eq('alternative_source_pid', (int)$currentRecord['pid'])
+                    ),
+                    $queryBuilder->expr()->eq('records_indexonchange', 1)
+                )
+                ->execute();
+
+            while ($cfgRec = $result->fetch()) {
+                $this->indexSingleRecord($currentRecord, $cfgRec);
             }
         }
     }
diff --git a/typo3/sysext/indexed_search/Classes/Indexer.php b/typo3/sysext/indexed_search/Classes/Indexer.php
index f07c06108df8..966957c4adc5 100644
--- a/typo3/sysext/indexed_search/Classes/Indexer.php
+++ b/typo3/sysext/indexed_search/Classes/Indexer.php
@@ -1581,30 +1581,22 @@ class Indexer
      */
     public function removeOldIndexedPages($phash)
     {
-        // Removing old registrations for all tables. Because the pages are TYPO3 pages there can be nothing else than 1-1 relations here.
-        $tableArray = explode(',', 'index_phash,index_section,index_grlist,index_fulltext,index_debug');
+        // Removing old registrations for all tables. Because the pages are TYPO3 pages
+        // there can be nothing else than 1-1 relations here.
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
+        $tableArray = ['index_phash', 'index_section', 'index_grlist', 'index_fulltext', 'index_debug'];
         foreach ($tableArray as $table) {
             if (IndexedSearchUtility::isTableUsed($table)) {
-                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                    ->getQueryBuilderForTable($table);
-                $queryBuilder
-                    ->delete($table)
-                    ->where(
-                        $queryBuilder->expr()->eq('phash', (int)$phash)
-                    )
-                    ->execute();
+                $connectionPool->getConnectionForTable($table)->delete($table, ['phash' => (int)$phash]);
             }
         }
-        // Removing all index_section records with hash_t3 set to this hash (this includes such records set for external media on the page as well!). The re-insert of these records are done in indexRegularDocument($file).
+
+        // Removing all index_section records with hash_t3 set to this hash (this includes such
+        // records set for external media on the page as well!). The re-insert of these records
+        // are done in indexRegularDocument($file).
         if (IndexedSearchUtility::isTableUsed('index_section')) {
-            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                ->getQueryBuilderForTable('index_section');
-            $queryBuilder
-                ->delete('index_section')
-                ->where(
-                    $queryBuilder->expr()->eq('phash_t3', (int)$phash)
-                )
-                ->execute();
+            $connectionPool->getConnectionForTable('index_section')
+                ->delete('index_section', ['phash_t3' => (int)$phash]);
         }
     }
 
@@ -1705,11 +1697,32 @@ class Indexer
     public function submitFile_grlist($hash)
     {
         // Testing if there is a gr_list record for a non-logged in user and if so, there is no need to place another one.
-        if (IndexedSearchUtility::isTableUsed('index_grlist')) {
-            $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('phash', 'index_grlist', 'phash=' . (int)$hash . ' AND (hash_gr_list=' . IndexedSearchUtility::md5inthash($this->defaultGrList) . ' OR hash_gr_list=' . IndexedSearchUtility::md5inthash($this->conf['gr_list']) . ')');
-            if ($count == 0) {
-                $this->submit_grlist($hash, $hash);
-            }
+        if (!IndexedSearchUtility::isTableUsed('index_grlist')) {
+            return;
+        }
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_grlist');
+        $count = (int)$queryBuilder->count('*')
+            ->from('index_grlist')
+            ->where(
+                $queryBuilder->expr()->eq('phash', (int)$hash),
+                $queryBuilder->expr()->orX(
+                    $queryBuilder->expr()->eq(
+                        'hash_gr_list',
+                        IndexedSearchUtility::md5inthash($this->defaultGrList)
+                    ),
+                    $queryBuilder->expr()->eq(
+                        'hash_gr_list',
+                        IndexedSearchUtility::md5inthash($this->conf['gr_list'])
+                    )
+                )
+            )
+            ->execute()
+            ->fetchColumn();
+
+        if ($count === 0) {
+            $this->submit_grlist($hash, $hash);
         }
     }
 
@@ -1722,11 +1735,23 @@ class Indexer
     public function submitFile_section($hash)
     {
         // Testing if there is already a section
-        if (IndexedSearchUtility::isTableUsed('index_section')) {
-            $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('phash', 'index_section', 'phash=' . (int)$hash . ' AND page_id=' . (int)$this->conf['id']);
-            if ($count == 0) {
-                $this->submit_section($hash, $this->hash['phash']);
-            }
+        if (!IndexedSearchUtility::isTableUsed('index_section')) {
+            return;
+        }
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('index_section');
+        $count = (int)$queryBuilder->count('phash')
+            ->from('index_section')
+            ->where(
+                $queryBuilder->expr()->eq('phash', (int)$hash),
+                $queryBuilder->expr()->eq('page_id', (int)$this->conf['id'])
+            )
+            ->execute()
+            ->fetchColumn();
+
+        if ($count === 0) {
+            $this->submit_section($hash, $this->hash['phash']);
         }
     }
 
@@ -1738,19 +1763,14 @@ class Indexer
      */
     public function removeOldIndexedFiles($phash)
     {
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
         // Removing old registrations for tables.
-        $tableArray = explode(',', 'index_phash,index_grlist,index_fulltext,index_debug');
+        $tableArray = ['index_phash', 'index_grlist', 'index_fulltext', 'index_debug'];
         foreach ($tableArray as $table) {
-            if (IndexedSearchUtility::isTableUsed($table)) {
-                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                    ->getQueryBuilderForTable($table);
-                $queryBuilder
-                    ->delete($table)
-                    ->where(
-                        $queryBuilder->expr()->eq('phash', (int)$phash)
-                    )
-                    ->execute();
+            if (!IndexedSearchUtility::isTableUsed($table)) {
+                continue;
             }
+            $connectionPool->getConnectionForTable($table)->delete($table, ['phash' => (int)$phash]);
         }
     }
 
@@ -1773,9 +1793,18 @@ class Indexer
             // Not indexed (not in index_phash)
             $result = 4;
         } else {
-            $row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('item_mtime,tstamp', 'index_phash', 'phash=' . (int)$phash);
+            $row = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_phash')
+                ->select(
+                    ['item_mtime', 'tstamp'],
+                    'index_phash',
+                    ['phash' => (int)$phash],
+                    [],
+                    [],
+                    1
+                )
+                ->fetch();
             // If there was an indexing of the page...:
-            if ($row) {
+            if (!empty($row)) {
                 if ($this->tstamp_maxAge && $row['tstamp'] + $this->tstamp_maxAge < $GLOBALS['EXEC_TIME']) {
                     // If max age is exceeded, index the page
                     // The configured max-age was exceeded for the document and thus it's indexed.
@@ -1826,8 +1855,21 @@ class Indexer
         // With this query the page will only be indexed if it's content is different from the same "phash_grouping" -page.
         $result = true;
         if (IndexedSearchUtility::isTableUsed('index_phash')) {
-            $row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('phash', 'index_phash', 'phash_grouping=' . (int)$this->hash['phash_grouping'] . ' AND contentHash=' . (int)$this->content_md5h);
-            if ($row) {
+            $row = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_phash')
+                ->select(
+                    ['item_mtime', 'tstamp'],
+                    'index_phash',
+                    [
+                        'phash_grouping' => (int)$this->hash['phash_grouping'],
+                        'contentHash' => (int)$this->content_md5h
+                    ],
+                    [],
+                    [],
+                    1
+                )
+                ->fetch();
+
+            if (!empty($row)) {
                 $result = $row;
             }
         }
@@ -1846,8 +1888,18 @@ class Indexer
     {
         $result = true;
         if (IndexedSearchUtility::isTableUsed('index_phash')) {
-            $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('*', 'index_phash', 'phash_grouping=' . (int)$hashGr . ' AND contentHash=' . (int)$content_md5h);
-            $result = $count == 0;
+            $count = (int)GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getConnectionForTable('index_phash')
+                ->count(
+                    '*',
+                    'index_phash',
+                    [
+                        'phash_grouping' => (int)$hashGr,
+                        'contentHash' => (int)$content_md5h
+                    ]
+                );
+
+            $result = $count === 0;
         }
         return $result;
     }
@@ -1862,7 +1914,14 @@ class Indexer
     {
         $result = false;
         if (IndexedSearchUtility::isTableUsed('index_grlist')) {
-            $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('phash_x', 'index_grlist', 'phash_x=' . (int)$phash_x);
+            $count = (int)GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getConnectionForTable('index_grlist')
+                ->count(
+                    'phash_x',
+                    'index_grlist',
+                    ['phash_x' => (int)$phash_x]
+                );
+
             $result = $count > 0;
         }
         return $result;
@@ -1879,8 +1938,18 @@ class Indexer
     public function update_grlist($phash, $phash_x)
     {
         if (IndexedSearchUtility::isTableUsed('index_grlist')) {
-            $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('phash', 'index_grlist', 'phash=' . (int)$phash . ' AND hash_gr_list=' . IndexedSearchUtility::md5inthash($this->conf['gr_list']));
-            if ($count == 0) {
+            $count = (int)GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getConnectionForTable('index_grlist')
+                ->count(
+                    'phash',
+                    'index_grlist',
+                    [
+                        'phash' => (int)$phash,
+                        'hash_gr_list' => IndexedSearchUtility::md5inthash($this->conf['gr_list'])
+                    ]
+                );
+
+            if ($count === 0) {
                 $this->submit_grlist($phash, $phash_x);
                 $this->log_setTSlogMessage('Inserted gr_list \'' . $this->conf['gr_list'] . '\' for phash \'' . $phash . '\'', 1);
             }
@@ -1896,15 +1965,27 @@ class Indexer
      */
     public function updateTstamp($phash, $mtime = 0)
     {
-        if (IndexedSearchUtility::isTableUsed('index_phash')) {
-            $updateFields = array(
-                'tstamp' => $GLOBALS['EXEC_TIME']
-            );
-            if ($mtime) {
-                $updateFields['item_mtime'] = (int)$mtime;
-            }
-            $GLOBALS['TYPO3_DB']->exec_UPDATEquery('index_phash', 'phash=' . (int)$phash, $updateFields);
+        if (!IndexedSearchUtility::isTableUsed('index_phash')) {
+            return;
         }
+
+        $updateFields = [
+            'tstamp' => $GLOBALS['EXEC_TIME']
+        ];
+
+        if ($mtime) {
+            $updateFields['item_mtime'] = (int)$mtime;
+        }
+
+        GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getConnectionForTable('index_phash')
+            ->update(
+                'index_phash',
+                $updateFields,
+                [
+                    'phash' => (int)$phash
+                ]
+            );
     }
 
     /**
@@ -1915,12 +1996,21 @@ class Indexer
      */
     public function updateSetId($phash)
     {
-        if (IndexedSearchUtility::isTableUsed('index_phash')) {
-            $updateFields = array(
-                'freeIndexSetId' => (int)$this->conf['freeIndexSetId']
+        if (!IndexedSearchUtility::isTableUsed('index_phash')) {
+            return;
+        }
+
+        GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getConnectionForTable('index_phash')
+            ->update(
+                'index_phash',
+                [
+                    'freeIndexSetId' => (int)$this->conf['freeIndexSetId']
+                ],
+                [
+                    'phash' => (int)$phash
+                ]
             );
-            $GLOBALS['TYPO3_DB']->exec_UPDATEquery('index_phash', 'phash=' . (int)$phash, $updateFields);
-        }
     }
 
     /**
@@ -1932,12 +2022,21 @@ class Indexer
      */
     public function updateParsetime($phash, $parsetime)
     {
-        if (IndexedSearchUtility::isTableUsed('index_phash')) {
-            $updateFields = array(
-                'parsetime' => (int)$parsetime
+        if (!IndexedSearchUtility::isTableUsed('index_phash')) {
+            return;
+        }
+
+        GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getConnectionForTable('index_phash')
+            ->update(
+                'index_phash',
+                [
+                    'parsetime' => (int)$parsetime
+                ],
+                [
+                    'phash' => (int)$phash
+                ]
             );
-            $GLOBALS['TYPO3_DB']->exec_UPDATEquery('index_phash', 'phash=' . (int)$phash, $updateFields);
-        }
     }
 
     /**
@@ -1947,11 +2046,22 @@ class Indexer
      */
     public function updateRootline()
     {
-        if (IndexedSearchUtility::isTableUsed('index_section')) {
-            $updateFields = array();
-            $this->getRootLineFields($updateFields);
-            $GLOBALS['TYPO3_DB']->exec_UPDATEquery('index_section', 'page_id=' . (int)$this->conf['id'], $updateFields);
+        if (!IndexedSearchUtility::isTableUsed('index_section')) {
+            return;
         }
+
+        $updateFields = [];
+        $this->getRootLineFields($updateFields);
+
+        GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getConnectionForTable('index_section')
+            ->update(
+                'index_section',
+                $updateFields,
+                [
+                    'page_id' => (int)$this->conf['id']
+                ]
+            );
     }
 
     /**
@@ -1996,34 +2106,50 @@ class Indexer
      */
     public function checkWordList($wordListArray)
     {
-        if (IndexedSearchUtility::isTableUsed('index_words')) {
-            if (!empty($wordListArray)) {
-                $phashArray = array();
-                foreach ($wordListArray as $value) {
-                    $phashArray[] = (int)$value['hash'];
-                }
-                $cwl = implode(',', $phashArray);
-                $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('baseword', 'index_words', 'wid IN (' . $cwl . ')');
-                $wordListArrayCount = count($wordListArray);
-                if ($count !== $wordListArrayCount) {
-                    $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('baseword', 'index_words', 'wid IN (' . $cwl . ')');
-                    $this->log_setTSlogMessage('Inserting words: ' . ($wordListArrayCount - $count), 1);
-                    while (false != ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
-                        unset($wordListArray[$row['baseword']]);
-                    }
-                    $GLOBALS['TYPO3_DB']->sql_free_result($res);
-                    foreach ($wordListArray as $key => $val) {
-                        $insertFields = array(
-                            'wid' => $val['hash'],
-                            'baseword' => $key,
-                            'metaphone' => $val['metaphone']
-                        );
-                        // A duplicate-key error will occur here if a word is NOT unset in the unset() line. However as long as the words in $wl are NOT longer as 60 chars (the baseword varchar is 60 characters...) this is not a problem.
-                        $connection = GeneralUtility::makeInstance(ConnectionPool::class)
-                            ->getConnectionForTable('index_words');
-                        $connection->insert('index_words', $insertFields);
-                    }
-                }
+        if (!IndexedSearchUtility::isTableUsed('index_words') || empty($wordListArray)) {
+            return;
+        }
+
+        $wordListArrayCount = count($wordListArray);
+        $phashArray = array_map('intval', array_column($wordListArray, 'hash'));
+
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
+        $count = (int)$queryBuilder->count('baseword')
+            ->from('index_words')
+            ->where(
+                $queryBuilder->expr()->in('wid', $phashArray)
+            )
+            ->execute()
+            ->fetchColumn();
+
+        if ($count !== $wordListArrayCount) {
+            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_words');
+            $queryBuilder = $connection->createQueryBuilder();
+
+            $result = $queryBuilder->select('baseword')
+                ->from('index_words')
+                ->where(
+                    $queryBuilder->expr()->in('wid', $phashArray)
+                )
+                ->execute();
+
+            $this->log_setTSlogMessage('Inserting words: ' . ($wordListArrayCount - $count), 1);
+            while ($row = $result->fetch()) {
+                unset($wordListArray[$row['baseword']]);
+            }
+
+            foreach ($wordListArray as $key => $val) {
+                // A duplicate-key error will occur here if a word is NOT unset in the unset() line. However as
+                // long as the words in $wl are NOT longer as 60 chars (the baseword varchar is 60 characters...)
+                // this is not a problem.
+                $connection->insert(
+                    'index_words',
+                    [
+                        'wid' => $val['hash'],
+                        'baseword' => $key,
+                        'metaphone' => $val['metaphone']
+                    ]
+                );
             }
         }
     }
@@ -2037,36 +2163,43 @@ class Indexer
      */
     public function submitWords($wordList, $phash)
     {
-        if (IndexedSearchUtility::isTableUsed('index_rel')) {
-            $stopWords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('wid', 'index_words', 'is_stopword != 0', '', '', '', 'wid');
+        if (!IndexedSearchUtility::isTableUsed('index_rel')) {
+            return;
+        }
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
+        $queryBuilder = $connectionPool->getQueryBuilderForTable('index_words');
+        $result = $queryBuilder->select('wid')
+            ->from('index_words')
+            ->where(
+                $queryBuilder->expr()->neq('is_stopword', 0)
+            )
+            ->groupBy('wid')
+            ->execute();
 
-            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                ->getQueryBuilderForTable('index_rel');
-            $queryBuilder
-                ->delete('index_rel')
-                ->where(
-                    $queryBuilder->expr()->eq('phash', (int)$phash)
-                )
-                ->execute();
-            $fields = array('phash', 'wid', 'count', 'first', 'freq', 'flags');
-            $rows = array();
-            foreach ($wordList as $val) {
-                if (isset($stopWords[$val['hash']])) {
-                    continue;
-                }
-                $rows[] = array(
-                    (int)$phash,
-                    (int)$val['hash'],
-                    (int)$val['count'],
-                    (int)$val['first'],
-                    $this->freqMap($val['count'] / $this->wordcount),
-                    $val['cmp'] & $this->flagBitMask
-                );
+        $stopWords = [];
+        while ($row = $result->fetch()) {
+            $stopWords[$row['wid']] = $row;
+        }
+
+        $connectionPool->getConnectionForTable('index_rel')->delete('index_rel', ['phash' => (int)$phash]);
+
+        $fields = ['phash', 'wid', 'count', 'first', 'freq', 'flags'];
+        $rows = [];
+        foreach ($wordList as $val) {
+            if (isset($stopWords[$val['hash']])) {
+                continue;
             }
-            GeneralUtility::makeInstance(ConnectionPool::class)
-                ->getConnectionForTable('index_rel')
-                ->bulkInsert('index_rel', $rows, $fields);
+            $rows[] = [
+                (int)$phash,
+                (int)$val['hash'],
+                (int)$val['count'],
+                (int)$val['first'],
+                $this->freqMap($val['count'] / $this->wordcount),
+                $val['cmp'] & $this->flagBitMask
+            ];
         }
+
+        $connectionPool->getConnectionForTable('index_rel')->bulkInsert('index_rel', $rows, $fields);
     }
 
     /**
diff --git a/typo3/sysext/indexed_search/Classes/Utility/LikeWildcard.php b/typo3/sysext/indexed_search/Classes/Utility/LikeWildcard.php
index 1553fbb00de3..867703c56bf1 100644
--- a/typo3/sysext/indexed_search/Classes/Utility/LikeWildcard.php
+++ b/typo3/sysext/indexed_search/Classes/Utility/LikeWildcard.php
@@ -13,6 +13,8 @@ namespace TYPO3\CMS\IndexedSearch\Utility;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Enumeration object for LikeWildcard
@@ -40,21 +42,16 @@ class LikeWildcard extends \TYPO3\CMS\Core\Type\Enumeration
      * @param string $fieldName The name of the field to query with LIKE.
      * @param string $likeValue The value for the LIKE clause operation.
      * @return string
-     * @throws \TYPO3\CMS\Core\Type\Exception\InvalidEnumerationValueException
      */
     public function getLikeQueryPart($tableName, $fieldName, $likeValue)
     {
-        $databaseConnection = $GLOBALS['TYPO3_DB'];
-
-        $likeValue = $databaseConnection->quoteStr(
-            $databaseConnection->escapeStrForLike($likeValue, $tableName),
-            $tableName
-        );
-
-        return $fieldName . ' LIKE \''
-            . ($this->value & self::LEFT ? '%' : '')
-            . $likeValue
-            . ($this->value & self::RIGHT ? '%' : '')
-            . '\'';
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable($tableName);
+
+        $string = ($this->value & self::LEFT ? '%' : '')
+            . $queryBuilder->escapeLikeWildcards($likeValue)
+            . ($this->value & self::RIGHT ? '%' : '');
+
+        return $queryBuilder->expr()->like($fieldName, $queryBuilder->quote($string));
     }
 }
diff --git a/typo3/sysext/indexed_search/Tests/Unit/Utility/LikeWildcardTest.php b/typo3/sysext/indexed_search/Tests/Functional/Utility/LikeWildcardTest.php
similarity index 73%
rename from typo3/sysext/indexed_search/Tests/Unit/Utility/LikeWildcardTest.php
rename to typo3/sysext/indexed_search/Tests/Functional/Utility/LikeWildcardTest.php
index 1a095567defc..0cdb0bdefb52 100644
--- a/typo3/sysext/indexed_search/Tests/Unit/Utility/LikeWildcardTest.php
+++ b/typo3/sysext/indexed_search/Tests/Functional/Utility/LikeWildcardTest.php
@@ -13,27 +13,16 @@ namespace TYPO3\CMS\IndexedSearch\Tests\Unit\Utility;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Tests\FunctionalTestCase;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\IndexedSearch\Utility\LikeWildcard;
 
 /**
  * This class contains unit tests for the LikeQueryUtility
  */
-class LikeWildcardTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
+class LikeWildcardTest extends FunctionalTestCase
 {
-    /**
-     * Sets up this test case.
-     */
-    protected function setUp()
-    {
-        /** @var $databaseConnectionMock \TYPO3\CMS\Core\Database\DatabaseConnection|\PHPUnit_Framework_MockObject_MockObject */
-        $databaseConnectionMock = $this->getMockBuilder(\TYPO3\CMS\Core\Database\DatabaseConnection::class)
-            ->setMethods(array('quoteStr'))
-            ->getMock();
-        $databaseConnectionMock->method('quoteStr')
-            ->will($this->returnArgument(0));
-        $GLOBALS['TYPO3_DB'] = $databaseConnectionMock;
-    }
-
     /**
      * @test
      * @param string $tableName
@@ -45,7 +34,9 @@ class LikeWildcardTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
      */
     public function getLikeQueryPart($tableName, $fieldName, $likeValue, $wildcard, $expected)
     {
-        $subject = \TYPO3\CMS\IndexedSearch\Utility\LikeWildcard::cast($wildcard);
+        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName);
+        $subject = LikeWildcard::cast($wildcard);
+        $expected = $connection->quoteIdentifier($fieldName) . ' ' . $expected;
         $this->assertSame($expected, $subject->getLikeQueryPart($tableName, $fieldName, $likeValue));
     }
 
@@ -68,49 +59,49 @@ class LikeWildcardTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
                 'body',
                 'searchstring',
                 LikeWildcard::NONE,
-                'body LIKE \'searchstring\''
+                "LIKE 'searchstring'"
             ],
             'no placeholders and left wildcard mode' => [
                 'tt_content',
                 'body',
                 'searchstring',
                 LikeWildcard::LEFT,
-                'body LIKE \'%searchstring\''
+                "LIKE '%searchstring'"
             ],
             'no placeholders and right wildcard mode' => [
                 'tt_content',
                 'body',
                 'searchstring',
                 LikeWildcard::RIGHT,
-                'body LIKE \'searchstring%\''
+                "LIKE 'searchstring%'"
             ],
             'no placeholders and both wildcards mode' => [
                 'tt_content',
                 'body',
                 'searchstring',
                 LikeWildcard::BOTH,
-                'body LIKE \'%searchstring%\''
+                "LIKE '%searchstring%'"
             ],
             'underscore placeholder and left wildcard mode' => [
                 'tt_content',
                 'body',
                 'search_string',
                 LikeWildcard::LEFT,
-                'body LIKE \'%search\\_string\''
+                "LIKE '%search\\\\_string'"
             ],
             'percent placeholder and right wildcard mode' => [
                 'tt_content',
                 'body',
                 'search%string',
                 LikeWildcard::RIGHT,
-                'body LIKE \'search\\%string%\''
+                "LIKE 'search\\\\%string%'"
             ],
             'percent and underscore placeholder and both wildcards mode' => [
                 'tt_content',
                 'body',
                 '_search%string_',
                 LikeWildcard::RIGHT,
-                'body LIKE \'\\_search\\%string\\_%\''
+                "LIKE '\\\\_search\\\\%string\\\\_%'"
             ],
         ];
     }
-- 
GitLab