From 755a948bea34d2269dc77720d37091bba5105a0b Mon Sep 17 00:00:00 2001
From: Alexander Stehlik <alexander.stehlik@gmail.com>
Date: Mon, 24 Nov 2014 17:15:17 +0100
Subject: [PATCH] [FEATURE] Relations to the same table in Extbase
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This patch introduces a mapping for properties to table aliases in
the Extbase Typo3DbQueryParser. This allows relations between
identical classes / tables.

Additionally the configured match fields of a table are now
respected in UNION statement (MM_match_fields,
foreign_match_fields or foreign_table_field).

Releases: master
Resolves: #27057
Change-Id: If1e88c994b8a1179f6babf2bdfc1da3e61b2c658
Reviewed-on: http://review.typo3.org/33956
Reviewed-by: Stephan Großberndt <stephan@grossberndt.de>
Tested-by: Stephan Großberndt <stephan@grossberndt.de>
Reviewed-by: Alexander Opitz <opitz.alexander@googlemail.com>
Tested-by: Alexander Opitz <opitz.alexander@googlemail.com>
---
 ...27057-RelationsToTheSameTableInExtbase.rst |  64 ++++
 .../Generic/Storage/Typo3DbQueryParser.php    | 279 ++++++++++++++----
 .../Classes/Domain/Model/Person.php           |  10 +
 .../Domain/Repository/PostRepository.php      |   2 +
 .../blog_example/Configuration/TCA/Person.php |  44 ++-
 .../Private/Language/locallang_db.xml         |   2 +
 .../Extensions/blog_example/ext_tables.sql    |  27 +-
 .../Functional/Persistence/CountTest.php      |  57 +++-
 .../Functional/Persistence/Fixtures/blogs.xml |  31 ++
 .../Persistence/Fixtures/persons.xml          |  27 ++
 .../Persistence/Fixtures/post-post-mm.xml     |  10 +
 .../Persistence/Fixtures/post-tag-mm.xml      |  12 +
 .../Functional/Persistence/Fixtures/posts.xml |  69 +++++
 .../Persistence/Fixtures/tags-mm.xml          |  35 +++
 .../Functional/Persistence/Fixtures/tags.xml  |  21 ++
 .../Tests/Functional/Persistence/InTest.php   |   8 +-
 .../Persistence/QueryParserTest.php           |  49 +++
 .../Functional/Persistence/RelationTest.php   |  86 +++---
 .../Storage/Typo3DbQueryParserTest.php        |  95 +++---
 19 files changed, 754 insertions(+), 174 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-27057-RelationsToTheSameTableInExtbase.rst
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/persons.xml
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-post-mm.xml
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags-mm.xml

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-27057-RelationsToTheSameTableInExtbase.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-27057-RelationsToTheSameTableInExtbase.rst
new file mode 100644
index 000000000000..0fdc835cebe1
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-27057-RelationsToTheSameTableInExtbase.rst
@@ -0,0 +1,64 @@
+========================================================
+Feature: #27057 - Relations to the same table in Extbase
+========================================================
+
+Description
+===========
+
+It is now possible to use a domain model where an object is connected to another object of the same class directly
+
+.. code-block:: php
+
+	namespace \Vendor\Extension\Domain\Model;
+	class A {
+		/**
+		* @var \Vendor\Extension\Domain\Model\A
+		*/
+		protected $parent;
+
+as well as using a domain model where an object has multiple relations to objects of the same class
+
+.. code-block:: php
+
+	namespace \Vendor\Extension\Domain\Model;
+	class A {
+		/**
+		* @var \Vendor\Extension\Domain\Model\B
+		*/
+		protected $x;
+
+		/**
+		* @var \Vendor\Extension\Domain\Model\B
+		*/
+		protected $y;
+
+as well as indirectly
+
+.. code-block:: php
+
+	namespace \Vendor\Extension\Domain\Model;
+	class A {
+		/**
+		* @var \Vendor\Extension\Domain\Model\B
+		*/
+		protected $b;
+
+		/**
+		* @var \Vendor\Extension\Domain\Model\C
+		*/
+		protected $c;
+
+	namespace \Vendor\Extension\Domain\Model;
+	class B {
+		/**
+		* @var \Vendor\Extension\Domain\Model\C
+		*/
+		protected $c;
+
+Using this kind of relations was possible before only by overriding the Extbase query builder and doing manual queries because the Extbase query builder created wrong SQL statements. Now Extbase properly supports these cases.
+
+
+Impact
+======
+
+Extbase now correctly handles relations to objects of the same class.
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbQueryParser.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbQueryParser.php
index b1a6058bec0e..0f51fe82bb4c 100644
--- a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbQueryParser.php
+++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbQueryParser.php
@@ -80,6 +80,16 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 		$this->environmentService = $environmentService;
 	}
 
+	/**
+	 * Maps domain model properties to their corresponding table aliases that are used in the query, e.g.:
+	 *
+	 * 'property1' => 'tableName',
+	 * 'property1.property2' => 'tableName1',
+	 *
+	 * @var array
+	 */
+	protected $tablePropertyMap = array();
+
 	/**
 	 * Constructor. takes the database handle from $GLOBALS['TYPO3_DB']
 	 */
@@ -202,6 +212,7 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 	 * @return array The SQL statement parts
 	 */
 	public function parseQuery(QueryInterface $query) {
+		$this->tablePropertyMap = array();
 		$sql = array();
 		$sql['keywords'] = array();
 		$sql['tables'] = array();
@@ -212,15 +223,17 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 		$sql['orderings'] = array();
 		$sql['limit'] = ((int)$query->getLimit() ?: NULL);
 		$sql['offset'] = ((int)$query->getOffset() ?: NULL);
+		$sql['tableAliasMap'] = array();
 		$source = $query->getSource();
 		$this->parseSource($source, $sql);
 		$this->parseConstraint($query->getConstraint(), $source, $sql);
 		$this->parseOrderings($query->getOrderings(), $source, $sql);
 
-		$tableNames = array_unique(array_keys($sql['tables'] + $sql['unions']));
-		foreach ($tableNames as $tableName) {
-			if (is_string($tableName) && !empty($tableName)) {
-				$this->addAdditionalWhereClause($query->getQuerySettings(), $tableName, $sql);
+		foreach ($sql['tableAliasMap'] as $tableAlias => $tableName) {
+			$additionalWhereClause = $this->getAdditionalWhereClause($query->getQuerySettings(), $tableName, $tableAlias);
+			if ($additionalWhereClause !== '') {
+				$additionalWhereClause = $this->addNullConditionToStatementIfRequired($sql, $additionalWhereClause, $tableAlias);
+				$sql['additionalWhereClause'][] = $additionalWhereClause;
 			}
 		}
 
@@ -240,14 +253,35 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 		if (!isset($sql['additionalWhereClause'])) {
 			throw new \InvalidArgumentException('Invalid statement given.', 1399512421);
 		}
-		$tableNames = array_unique(array_keys($sql['tables'] + $sql['unions']));
-		foreach ($tableNames as $tableName) {
-			if (is_string($tableName) && !empty($tableName)) {
-				$this->addVisibilityConstraintStatement($querySettings, $tableName, $sql);
+		foreach ($sql['tableAliasMap'] as $tableAlias => $tableName) {
+			$statement = $this->getVisibilityConstraintStatement($querySettings, $tableName, $tableAlias);
+			if ($statement !== '') {
+				$statement = $this->addNullConditionToStatementIfRequired($sql, $statement, $tableAlias);
+				$sql['additionalWhereClause'][] = $statement;
 			}
 		}
 	}
 
+	/**
+	 * If the given table alias is used in a UNION statement it is required to
+	 * add an additional condition that allows the fields of the joined table
+	 * to be NULL. Otherwise the condition would be too strict and filter out
+	 * records that are actually valid.
+	 *
+	 * @param array $sql The current SQL query parts.
+	 * @param string $statement The SQL statement to which the NULL condition should be added.
+	 * @param string $tableAlias The table alias used in the SQL statement.
+	 * @return string The statement including the NULL condition or the original statement.
+	 */
+	protected function addNullConditionToStatementIfRequired(array $sql, $statement, $tableAlias) {
+
+		if (isset($sql['unions'][$tableAlias])) {
+			$statement = '((' . $statement . ') OR ' . $tableAlias . '.uid' . ' IS NULL)';
+		}
+
+		return $statement;
+	}
+
 	/**
 	 * Transforms a Query Source into SQL and parameter arrays
 	 *
@@ -260,6 +294,7 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 			$className = $source->getNodeTypeName();
 			$tableName = $this->dataMapper->getDataMap($className)->getTableName();
 			$this->addRecordTypeConstraint($className, $sql);
+			$tableName = $this->getUniqueAlias($sql, $tableName);
 			$sql['fields'][$tableName] = $tableName . '.*';
 			$sql['tables'][$tableName] = $tableName;
 		} elseif ($source instanceof Qom\JoinInterface) {
@@ -323,8 +358,9 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 			if ($source instanceof Qom\SelectorInterface) {
 				$className = $source->getNodeTypeName();
 				$tableName = $this->dataMapper->convertClassNameToTableName($className);
+				$fullPropertyPath = '';
 				while (strpos($propertyName, '.') !== FALSE) {
-					$this->addUnionStatement($className, $tableName, $propertyName, $sql);
+					$this->addUnionStatement($className, $tableName, $propertyName, $sql, $fullPropertyPath);
 				}
 			} elseif ($source instanceof Qom\JoinInterface) {
 				$tableName = $source->getLeft()->getSelectorName();
@@ -378,8 +414,9 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 				$tableName = $this->dataMapper->convertClassNameToTableName($className);
 				$operand1 = $comparison->getOperand1();
 				$propertyName = $operand1->getPropertyName();
+				$fullPropertyPath = '';
 				while (strpos($propertyName, '.') !== FALSE) {
-					$this->addUnionStatement($className, $tableName, $propertyName, $sql);
+					$this->addUnionStatement($className, $tableName, $propertyName, $sql, $fullPropertyPath);
 				}
 				$columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
 				$dataMap = $this->dataMapper->getDataMap($className);
@@ -387,16 +424,7 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 				$typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : NULL;
 				if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
 					$relationTableName = $columnMap->getRelationTableName();
-					$relationTableMatchFields = $columnMap->getRelationTableMatchFields();
-					if (is_array($relationTableMatchFields)) {
-						$additionalWhere = array();
-						foreach ($relationTableMatchFields as $fieldName => $value) {
-							$additionalWhere[] = $fieldName . ' = ' . $this->databaseHandle->fullQuoteStr($value, $relationTableName);
-						}
-						$additionalWhereForMatchFields = ' AND ' . implode(' AND ', $additionalWhere);
-					} else {
-						$additionalWhereForMatchFields = '';
-					}
+					$additionalWhereForMatchFields = $this->getAdditionalMatchFieldsStatement($columnMap, $relationTableName, $relationTableName);
 					$sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=' . $parameterIdentifier . $additionalWhereForMatchFields . ')';
 				} elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
 					$parentKeyFieldName = $columnMap->getParentKeyFieldName();
@@ -457,8 +485,9 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 				// @todo Only necessary to differ from  Join
 				$className = $source->getNodeTypeName();
 				$tableName = $this->dataMapper->convertClassNameToTableName($className);
+				$fullPropertyPath = '';
 				while (strpos($propertyName, '.') !== FALSE) {
-					$this->addUnionStatement($className, $tableName, $propertyName, $sql);
+					$this->addUnionStatement($className, $tableName, $propertyName, $sql, $fullPropertyPath);
 				}
 			} elseif ($source instanceof Qom\JoinInterface) {
 				$tableName = $source->getJoinCondition()->getSelector1Name();
@@ -504,21 +533,68 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 		}
 	}
 
+	/**
+	 * Builds a condition for filtering records by the configured match field,
+	 * e.g. MM_match_fields, foreign_match_fields or foreign_table_field.
+	 *
+	 * @param ColumnMap $columnMap The column man for which the condition should be build.
+	 * @param string $childTableName The real name of the child record table.
+	 * @param string $childTableAlias The alias of the child record table used in the query.
+	 * @param string $parentTable The real name of the parent table (used for building the foreign_table_field condition).
+	 * @return string The match field conditions or an empty string.
+	 */
+	protected function getAdditionalMatchFieldsStatement($columnMap, $childTableName, $childTableAlias, $parentTable = NULL) {
+
+		$additionalWhereForMatchFields = '';
+
+		$relationTableMatchFields = $columnMap->getRelationTableMatchFields();
+		if (is_array($relationTableMatchFields) && !empty($relationTableMatchFields)) {
+			$additionalWhere = array();
+			foreach ($relationTableMatchFields as $fieldName => $value) {
+				$additionalWhere[] = $childTableAlias . '.' . $fieldName . ' = ' . $this->databaseHandle->fullQuoteStr($value, $childTableName);
+			}
+			$additionalWhereForMatchFields .= ' AND ' . implode(' AND ', $additionalWhere);
+		}
+
+		if (isset($parentTable)) {
+			$parentTableFieldName = $columnMap->getParentTableFieldName();
+			if (isset($parentTableFieldName) && $parentTableFieldName !== '') {
+				$additionalWhereForMatchFields .= ' AND ' . $childTableAlias . '.' . $parentTableFieldName . ' = ' . $this->databaseHandle->fullQuoteStr($parentTable, $childTableAlias);
+			}
+		}
+
+		return $additionalWhereForMatchFields;
+	}
+
 	/**
 	 * Adds additional WHERE statements according to the query settings.
 	 *
 	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
 	 * @param string $tableName The table name to add the additional where clause for
-	 * @param string &$sql
-	 * @return void
+	 * @param string $tableAlias The table alias used in the query.
+	 * @return string
 	 */
-	protected function addAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableName, &$sql) {
+	protected function getAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableName, $tableAlias = NULL) {
+
+		$sysLanguageStatement = '';
 		if ($querySettings->getRespectSysLanguage()) {
-			$this->addSysLanguageStatement($tableName, $sql, $querySettings);
+			$sysLanguageStatement = $this->getSysLanguageStatement($tableName, $tableAlias, $querySettings);
 		}
+
+		$pageIdStatement = '';
 		if ($querySettings->getRespectStoragePage()) {
-			$this->addPageIdStatement($tableName, $sql, $querySettings->getStoragePageIds());
+			$pageIdStatement = $this->getPageIdStatement($tableName, $tableAlias, $querySettings->getStoragePageIds());
+		}
+
+		if ($sysLanguageStatement !== '' && $pageIdStatement !== '') {
+			$whereClause = $sysLanguageStatement . ' AND ' . $pageIdStatement;
+		} elseif ($sysLanguageStatement !== '') {
+			$whereClause = $sysLanguageStatement;
+		} else {
+			$whereClause = $pageIdStatement;
 		}
+
+		return $whereClause;
 	}
 
 	/**
@@ -526,10 +602,10 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 	 *
 	 * @param QuerySettingsInterface $querySettings
 	 * @param string $tableName The database table name
-	 * @param array &$sql The query parts
-	 * @return void
+	 * @param string $tableAlias
+	 * @return string
 	 */
-	protected function addVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableName, array &$sql) {
+	protected function getVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableName, $tableAlias) {
 		$statement = '';
 		if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
 			$ignoreEnableFields = $querySettings->getIgnoreEnableFields();
@@ -542,10 +618,11 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 				$statement .= $this->getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted);
 			}
 			if (!empty($statement)) {
+				$statement = $this->replaceTableNameWithAlias($statement, $tableName, $tableAlias);
 				$statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
-				$sql['additionalWhereClause'][] = $statement;
 			}
 		}
+		return $statement;
 	}
 
 	/**
@@ -598,15 +675,16 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 	 * Builds the language field statement
 	 *
 	 * @param string $tableName The database table name
-	 * @param array &$sql The query parts
+	 * @param string $tableAlias The table alias used in the query.
 	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
-	 * @return void
+	 * @return string
 	 */
-	protected function addSysLanguageStatement($tableName, array &$sql, $querySettings) {
+	protected function getSysLanguageStatement($tableName, $tableAlias, $querySettings) {
+		$sysLanguageStatement = '';
 		if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
 			if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
 				// Select all entries for the current language
-				$additionalWhereClause = $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . (int)$querySettings->getLanguageUid() . ',-1)';
+				$additionalWhereClause = $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . (int)$querySettings->getLanguageUid() . ',-1)';
 				// If any language is set -> get those entries which are not translated yet
 				// They will be removed by \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay if not matching overlay mode
 				if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
@@ -615,17 +693,17 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 
 					$mode = $querySettings->getLanguageMode();
 					if ($mode === 'strict') {
-						$additionalWhereClause = $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=-1' .
-							' OR (' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' = ' . (int)$querySettings->getLanguageUid() .
-							' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '=0' .
-							') OR (' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
-							' AND ' . $tableName . '.uid IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
+						$additionalWhereClause = $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=-1' .
+							' OR (' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' = ' . (int)$querySettings->getLanguageUid() .
+							' AND ' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '=0' .
+							') OR (' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
+							' AND ' . $tableAlias . '.uid IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
 							' FROM ' . $tableName .
 							' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
 							' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=' . (int)$querySettings->getLanguageUid();
 					} else {
-						$additionalWhereClause .= ' OR (' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
-							' AND ' . $tableName . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
+						$additionalWhereClause .= ' OR (' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
+							' AND ' . $tableAlias . '.uid NOT IN (SELECT ' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
 							' FROM ' . $tableName .
 							' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
 							' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=' . (int)$querySettings->getLanguageUid();
@@ -637,21 +715,23 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 					}
 					$additionalWhereClause .= '))';
 				}
-				$sql['additionalWhereClause'][] = '(' . $additionalWhereClause . ')';
+				$sysLanguageStatement = '(' . $additionalWhereClause . ')';
 			}
 		}
+		return $sysLanguageStatement;
 	}
 
 	/**
 	 * Builds the page ID checking statement
 	 *
 	 * @param string $tableName The database table name
-	 * @param array &$sql The query parts
+	 * @param string $tableAlias The table alias used in the query.
 	 * @param array $storagePageIds list of storage page ids
 	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InconsistentQuerySettingsException
-	 * @return void
+	 * @return string
 	 */
-	protected function addPageIdStatement($tableName, array &$sql, array $storagePageIds) {
+	protected function getPageIdStatement($tableName, $tableAlias, array $storagePageIds) {
+		$pageIdStatement = '';
 		$tableColumns = $this->tableColumnCache->get($tableName);
 		if ($tableColumns === FALSE) {
 			$tableColumns = $this->databaseHandle->admin_get_fields($tableName);
@@ -661,15 +741,16 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 			$rootLevel = (int)$GLOBALS['TCA'][$tableName]['ctrl']['rootLevel'];
 			if ($rootLevel) {
 				if ($rootLevel === 1) {
-					$sql['additionalWhereClause'][] = $tableName . '.pid = 0';
+					$pageIdStatement = $tableAlias . '.pid = 0';
 				}
 			} else {
 				if (empty($storagePageIds)) {
 					throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InconsistentQuerySettingsException('Missing storage page ids.', 1365779762);
 				}
-				$sql['additionalWhereClause'][] = $tableName . '.pid IN (' . implode(', ', $storagePageIds) . ')';
+				$pageIdStatement = $tableAlias . '.pid IN (' . implode(', ', $storagePageIds) . ')';
 			}
 		}
+		return $pageIdStatement;
 	}
 
 	/**
@@ -695,35 +776,72 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 			$sql['fields'][$leftTableName] = $rightTableName . '.*';
 		}
 		$this->addRecordTypeConstraint($rightClassName, $sql);
+		$leftTableName = $this->getUniqueAlias($sql, $leftTableName);
 		$sql['tables'][$leftTableName] = $leftTableName;
+		$rightTableName = $this->getUniqueAlias($sql, $rightTableName);
 		$sql['unions'][$rightTableName] = 'LEFT JOIN ' . $rightTableName;
 		$joinCondition = $join->getJoinCondition();
 		if ($joinCondition instanceof Qom\EquiJoinCondition) {
 			$column1Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty1Name(), $leftClassName);
 			$column2Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty2Name(), $rightClassName);
-			$sql['unions'][$rightTableName] .= ' ON ' . $joinCondition->getSelector1Name() . '.' . $column1Name . ' = ' . $joinCondition->getSelector2Name() . '.' . $column2Name;
+			$sql['unions'][$rightTableName] .= ' ON ' . $leftTableName . '.' . $column1Name . ' = ' . $rightTableName . '.' . $column2Name;
 		}
 		if ($rightSource instanceof Qom\JoinInterface) {
 			$this->parseJoin($rightSource, $sql);
 		}
 	}
 
+	/**
+	 * Generates a unique alias for the given table and the given property path.
+	 * The property path will be mapped to the generated alias in the tablePropertyMap.
+	 *
+	 * @param array $sql The SQL satement parts, will be filled with the tableAliasMap.
+	 * @param string $tableName The name of the table for which the alias should be generated.
+	 * @param string $fullPropertyPath The full property path that is related to the given table.
+	 * @return string The generated table alias.
+	 */
+	protected function getUniqueAlias(array &$sql, $tableName, $fullPropertyPath = NULL) {
+
+		if (isset($fullPropertyPath) && isset($this->tablePropertyMap[$fullPropertyPath])) {
+			return $this->tablePropertyMap[$fullPropertyPath];
+		}
+
+		$alias = $tableName;
+		$i = 0;
+		while (isset($sql['tableAliasMap'][$alias])) {
+			$alias = $tableName . $i;
+			$i++;
+		}
+
+		$sql['tableAliasMap'][$alias] = $tableName;
+
+		if (isset($fullPropertyPath)) {
+			$this->tablePropertyMap[$fullPropertyPath] = $alias;
+		}
+
+		return $alias;
+	}
+
 	/**
 	 * adds a union statement to the query, mostly for tables referenced in the where condition.
+	 * The property for which the union statement is generated will be appended.
 	 *
-	 * @param string &$className
-	 * @param string &$tableName
-	 * @param array &$propertyPath
-	 * @param array &$sql
+	 * @param string &$className The name of the parent class, will be set to the child class after processing.
+	 * @param string &$tableName The name of the parent table, will be set to the table alias that is used in the union statement.
+	 * @param array &$propertyPath The remaining property path, will be cut of by one part during the process.
+	 * @param array &$sql The SQL statement parts, will be filled with the union statements.
+	 * @param string $fullPropertyPath The full path the the current property, will be used to make table names unique.
 	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception
 	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidRelationConfigurationException
 	 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\MissingColumnMapException
 	 */
-	protected function addUnionStatement(&$className, &$tableName, &$propertyPath, array &$sql) {
+	protected function addUnionStatement(&$className, &$tableName, &$propertyPath, array &$sql, &$fullPropertyPath) {
+
 		$explodedPropertyPath = explode('.', $propertyPath, 2);
 		$propertyName = $explodedPropertyPath[0];
 		$columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
-		$tableName = $this->dataMapper->convertClassNameToTableName($className);
+		$realTableName = $this->dataMapper->convertClassNameToTableName($className);
+		$tableName = isset($this->tablePropertyMap[$fullPropertyPath]) ? $this->tablePropertyMap[$fullPropertyPath] : $realTableName;
 		$columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
 
 		if ($columnMap === NULL) {
@@ -737,33 +855,64 @@ class Typo3DbQueryParser implements \TYPO3\CMS\Core\SingletonInterface {
 			throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidRelationConfigurationException('The relation information for property "' . $propertyName . '" of class "' . $className . '" is missing.', 1353170925);
 		}
 
+		$fullPropertyPath .= ($fullPropertyPath === '') ? $propertyName : '.' . $propertyName;
+		$childTableAlias = $this->getUniqueAlias($sql, $childTableName, $fullPropertyPath);
+
+		// If there is already exists a union with the current identifier we do not need to build it again and exit early.
+		if (isset($sql['unions'][$childTableAlias])) {
+			$propertyPath = $explodedPropertyPath[1];
+			$tableName = $childTableAlias;
+			$className = $this->dataMapper->getType($className, $propertyName);
+			return;
+		}
+
 		if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_ONE) {
 			if (isset($parentKeyFieldName)) {
-				$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
+				$sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $tableName . '.uid=' . $childTableAlias . '.' . $parentKeyFieldName;
 			} else {
-				$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $columnName . '=' . $childTableName . '.uid';
+				$sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $tableName . '.' . $columnName . '=' . $childTableAlias . '.uid';
 			}
-			$className = $this->dataMapper->getType($className, $propertyName);
+			$sql['unions'][$childTableAlias] .= $this->getAdditionalMatchFieldsStatement($columnMap, $childTableName, $childTableAlias, $realTableName);
 		} elseif ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY) {
 			if (isset($parentKeyFieldName)) {
-				$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
+				$sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $tableName . '.uid=' . $childTableAlias . '.' . $parentKeyFieldName;
 			} else {
-				$onStatement = '(FIND_IN_SET(' . $childTableName . '.uid, ' . $tableName . '.' . $columnName . '))';
-				$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $onStatement;
+				$onStatement = '(FIND_IN_SET(' . $childTableAlias . '.uid, ' . $tableName . '.' . $columnName . '))';
+				$sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $onStatement;
 			}
-			$className = $this->dataMapper->getType($className, $propertyName);
+			$sql['unions'][$childTableAlias] .= $this->getAdditionalMatchFieldsStatement($columnMap, $childTableName, $childTableAlias, $realTableName);
 		} elseif ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
 			$relationTableName = $columnMap->getRelationTableName();
-			$sql['unions'][$relationTableName] = 'LEFT JOIN ' . $relationTableName . ' ON ' . $tableName . '.uid=' . $relationTableName . '.' . $columnMap->getParentKeyFieldName();
-			$sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $relationTableName . '.' . $columnMap->getChildKeyFieldName() . '=' . $childTableName . '.uid';
-			$className = $this->dataMapper->getType($className, $propertyName);
+			$relationTableAlias = $relationTableAlias = $this->getUniqueAlias($sql, $relationTableName, $fullPropertyPath . '_mm');
+			$sql['unions'][$relationTableAlias] = 'LEFT JOIN ' . $relationTableName . ' AS ' . $relationTableAlias . ' ON ' . $tableName . '.uid=' . $relationTableAlias . '.' . $columnMap->getParentKeyFieldName();
+			$sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $relationTableAlias . '.' . $columnMap->getChildKeyFieldName() . '=' . $childTableAlias . '.uid';
+			$sql['unions'][$childTableAlias] .= $this->getAdditionalMatchFieldsStatement($columnMap, $relationTableName, $relationTableAlias, $realTableName);
 		} else {
 			throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception('Could not determine type of relation.', 1252502725);
 		}
 		// @todo check if there is another solution for this
 		$sql['keywords']['distinct'] = 'DISTINCT';
 		$propertyPath = $explodedPropertyPath[1];
-		$tableName = $childTableName;
+		$tableName = $childTableAlias;
+		$className = $this->dataMapper->getType($className, $propertyName);
+	}
+
+	/**
+	 * If the table name does not match the table alias all occurrences of
+	 * "tableName." are replaced with "tableAlias." in the given SQL statement.
+	 *
+	 * @param string $statement The SQL statement in which the values are replaced.
+	 * @param string $tableName The table name that is replaced.
+	 * @param string $tableAlias The table alias that replaced the table name.
+	 * @return string The modified SQL statement.
+	 */
+	protected function replaceTableNameWithAlias($statement, $tableName, $tableAlias) {
+
+		if ($tableAlias !== $tableName) {
+			$statement = str_replace($tableName . '.', $tableAlias . '.', $statement);
+		}
+
+		return $statement;
 	}
 
 	/**
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/Person.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/Person.php
index 71f39ec29745..cfd68fe6f627 100644
--- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/Person.php
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Model/Person.php
@@ -34,6 +34,16 @@ class Person extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
 	 */
 	protected $email = '';
 
+	/**
+	 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\ExtbaseTeam\BlogExample\Domain\Model\Tag>
+	 */
+	protected $tags = NULL;
+
+	/**
+	 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\ExtbaseTeam\BlogExample\Domain\Model\Tag>
+	 */
+	protected $tagsSpecial = NULL;
+
 	/**
 	 * Constructs a new Person
 	 *
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Repository/PostRepository.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Repository/PostRepository.php
index 17ae40f68303..d43f58a1127f 100644
--- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Repository/PostRepository.php
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Domain/Repository/PostRepository.php
@@ -16,6 +16,8 @@ namespace ExtbaseTeam\BlogExample\Domain\Repository;
 
 /**
  * A repository for blog posts
+ *
+ * @method \ExtbaseTeam\BlogExample\Domain\Model\Post findByUid($uid)
  */
 class PostRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
 
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/Person.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/Person.php
index 0911a0f741df..16f79fbf63a3 100644
--- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/Person.php
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/Person.php
@@ -43,10 +43,50 @@ $TCA['tx_blogexample_domain_model_person'] = array(
 				'eval' => 'trim, required',
 				'max' => 256
 			)
-		)
+		),
+		'tags' => array(
+			'exclude' => 1,
+			'label' => 'LLL:EXT:blog_example/Resources/Private/Language/locallang_db.xml:tx_blogexample_domain_model_person.tags',
+			'config' => array(
+				'type' => 'inline',
+				'foreign_table' => 'tx_blogexample_domain_model_tag',
+				'MM' => 'tx_blogexample_domain_model_tag_mm',
+				'foreign_table_field' => 'tablenames',
+				'foreign_match_fields' => array(
+					'fieldname' => 'tags'
+				),
+				'maxitems' => 9999,
+				'appearance' => array(
+					'useCombination' => 1,
+					'useSortable' => 1,
+					'collapseAll' => 1,
+					'expandSingle' => 1,
+				)
+			)
+		),
+		'tags_special' => array(
+			'exclude' => 1,
+			'label' => 'LLL:EXT:blog_example/Resources/Private/Language/locallang_db.xml:tx_blogexample_domain_model_person.tags_special',
+			'config' => array(
+				'type' => 'inline',
+				'foreign_table' => 'tx_blogexample_domain_model_tag',
+				'MM' => 'tx_blogexample_domain_model_tag_mm',
+				'foreign_table_field' => 'tablenames',
+				'foreign_match_fields' => array(
+					'fieldname' => 'tags_special'
+				),
+				'maxitems' => 9999,
+				'appearance' => array(
+					'useCombination' => 1,
+					'useSortable' => 1,
+					'collapseAll' => 1,
+					'expandSingle' => 1,
+				)
+			)
+		),
 	),
 	'types' => array(
-		'1' => array('showitem' => 'firstname, lastname, email, avatar')
+		'1' => array('showitem' => 'firstname, lastname, email, avatar, tags, tags_special')
 	),
 	'palettes' => array(
 		'1' => array('showitem' => '')
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Resources/Private/Language/locallang_db.xml b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Resources/Private/Language/locallang_db.xml
index 01899a0d7528..d3d400a02576 100644
--- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Resources/Private/Language/locallang_db.xml
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Resources/Private/Language/locallang_db.xml
@@ -28,6 +28,8 @@
 			<label index="tx_blogexample_domain_model_person.lastname">Lastname</label>
 			<label index="tx_blogexample_domain_model_person.email">E-Mail</label>
 			<label index="tx_blogexample_domain_model_person.avatar">Avatar</label>
+			<label index="tx_blogexample_domain_model_person.tags">Tags</label>
+			<label index="tx_blogexample_domain_model_person.tags_special">Special tags</label>
 			<label index="tx_blogexample_domain_model_comment">Comment</label>
 			<label index="tx_blogexample_domain_model_comment.date">Date</label>
 			<label index="tx_blogexample_domain_model_comment.author">Author</label>
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql
index 10aa5bfdb9f5..67ef7cf09ae7 100644
--- a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql
@@ -35,7 +35,7 @@ CREATE TABLE tx_blogexample_domain_model_blog (
 
 	PRIMARY KEY (uid),
 	KEY parent (pid),
-	KEY t3ver_oid (t3ver_oid,t3ver_wsid),
+	KEY t3ver_oid (t3ver_oid,t3ver_wsid)
 );
 
 #
@@ -78,7 +78,7 @@ CREATE TABLE tx_blogexample_domain_model_post (
 
 	PRIMARY KEY (uid),
 	KEY parent (pid),
-	KEY t3ver_oid (t3ver_oid,t3ver_wsid),
+	KEY t3ver_oid (t3ver_oid,t3ver_wsid)
 );
 
 #
@@ -101,7 +101,7 @@ CREATE TABLE tx_blogexample_domain_model_comment (
 	hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
 
 	PRIMARY KEY (uid),
-	KEY parent (pid),
+	KEY parent (pid)
 );
 
 #
@@ -114,6 +114,8 @@ CREATE TABLE tx_blogexample_domain_model_person (
 	firstname varchar(255) DEFAULT '' NOT NULL,
 	lastname varchar(255) DEFAULT '' NOT NULL,
 	email varchar(255) DEFAULT '' NOT NULL,
+	tags int(11) unsigned DEFAULT '0' NOT NULL,
+	tags_special int(11) unsigned DEFAULT '0' NOT NULL,
 
 	tstamp int(11) unsigned DEFAULT '0' NOT NULL,
 	crdate int(11) unsigned DEFAULT '0' NOT NULL,
@@ -133,7 +135,7 @@ CREATE TABLE tx_blogexample_domain_model_person (
 
 	PRIMARY KEY (uid),
 	KEY parent (pid),
-	KEY t3ver_oid (t3ver_oid,t3ver_wsid),
+	KEY t3ver_oid (t3ver_oid,t3ver_wsid)
 );
 
 #
@@ -154,7 +156,22 @@ CREATE TABLE tx_blogexample_domain_model_tag (
 	sys_language_uid int(11) DEFAULT '0' NOT NULL,
 
 	PRIMARY KEY (uid),
-	KEY parent (pid),
+	KEY parent (pid)
+);
+
+#
+# Table structure for table 'tx_blogexample_domain_model_tag_mm'
+#
+CREATE TABLE tx_blogexample_domain_model_tag_mm (
+	uid_local int(11) unsigned DEFAULT '0' NOT NULL,
+	uid_foreign int(11) unsigned DEFAULT '0' NOT NULL,
+	tablenames varchar(255) DEFAULT '' NOT NULL,
+	fieldname varchar(255) DEFAULT '' NOT NULL,
+	sorting int(11) unsigned DEFAULT '0' NOT NULL,
+	sorting_foreign int(11) unsigned DEFAULT '0' NOT NULL,
+
+	KEY uid_local (uid_local),
+	KEY uid_foreign (uid_foreign)
 );
 
 #
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/CountTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/CountTest.php
index a359a83081b5..325d873833c4 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/CountTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/CountTest.php
@@ -21,7 +21,7 @@ class CountTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	/**
 	 * @var int number of all records
 	 */
-	protected $numberOfRecordsInFixture = 11;
+	protected $numberOfRecordsInFixture = 14;
 
 	/**
 	 * @var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
@@ -48,6 +48,11 @@ class CountTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 */
 	protected $blogRepository;
 
+	/**
+	 * @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository
+	 */
+	protected $postRepository;
+
 	/**
 	 * Sets up this test suite.
 	 */
@@ -57,8 +62,11 @@ class CountTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/core/Tests/Functional/Fixtures/pages.xml');
 		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs.xml');
 		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/posts.xml');
+		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-post-mm.xml');
 		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags.xml');
+		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags-mm.xml');
 		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-tag-mm.xml');
+		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/persons.xml');
 
 		$this->objectManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
 		$this->persistentManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class);
@@ -147,6 +155,21 @@ class CountTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->assertSame(10, $query->count());
 	}
 
+	/**
+	 * Test if count works with subproperties in subselects that use the same table as the repository.
+	 *
+	 * @test
+	 */
+	public function subpropertyJoinSameTableCountTest() {
+		$query = $this->postRepository->createQuery();
+
+		$query->matching(
+			$query->equals('relatedPosts.title', 'Post2')
+		);
+
+		$this->assertSame(1, $query->count());
+	}
+
 	/**
 	 * Test if count works with subproperties in multiple left join.
 	 *
@@ -165,4 +188,36 @@ class CountTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->assertSame(10, $query->count());
 	}
 
+	/**
+	 * @test
+	 */
+	public function queryWithAndConditionsToTheSameTableReturnExpectedCount() {
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PersonRepository $personRepository */
+		$personRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PersonRepository::class);
+		$query = $personRepository->createQuery();
+		$query->matching(
+			$query->logicalAnd(
+				$query->equals('tags.name', 'TagForAuthor1'),
+				$query->equals('tagsSpecial.name', 'SpecialTagForAuthor1')
+			)
+		);
+		$this->assertSame(1, $query->count());
+	}
+
+	/**
+	 * @test
+	 */
+	public function queryWithOrConditionsToTheSameTableReturnExpectedCount() {
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PersonRepository $personRepository */
+		$personRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PersonRepository::class);
+		$query = $personRepository->createQuery();
+		$query->matching(
+			$query->logicalOr(
+				$query->equals('tags.name', 'TagForAuthor1'),
+				$query->equals('tagsSpecial.name', 'SpecialTagForAuthor1')
+			)
+		);
+		$this->assertSame(3, $query->count());
+	}
+
 }
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs.xml
index 1da71af92564..f002eca62b36 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs.xml
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs.xml
@@ -20,4 +20,35 @@
 		<deleted>0</deleted>
 		<posts>1</posts>
 	</tx_blogexample_domain_model_blog>
+	<tx_blogexample_domain_model_blog>
+		<uid>3</uid>
+		<pid>0</pid>
+		<title>Blog3</title>
+		<description>Blog3 Description</description>
+		<logo></logo>
+		<l18n_diffsource></l18n_diffsource>
+		<deleted>0</deleted>
+		<posts>1</posts>
+	</tx_blogexample_domain_model_blog>
+	<tx_blogexample_domain_model_blog>
+		<uid>4</uid>
+		<pid>0</pid>
+		<title>Blog4Hidden</title>
+		<description>Blog4 Description</description>
+		<logo></logo>
+		<l18n_diffsource></l18n_diffsource>
+		<hidden>1</hidden>
+		<deleted>0</deleted>
+		<posts>1</posts>
+	</tx_blogexample_domain_model_blog>
+	<tx_blogexample_domain_model_blog>
+		<uid>5</uid>
+		<pid>0</pid>
+		<title>Blog5Deleted</title>
+		<description>Blog5 Description</description>
+		<logo></logo>
+		<l18n_diffsource></l18n_diffsource>
+		<deleted>1</deleted>
+		<posts>1</posts>
+	</tx_blogexample_domain_model_blog>
 </dataset>
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/persons.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/persons.xml
new file mode 100644
index 000000000000..f140363391bf
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/persons.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dataset>
+	<tx_blogexample_domain_model_person>
+		<uid>1</uid>
+		<pid>0</pid>
+		<firstname>Author</firstname>
+		<lastname>With tag and special tag</lastname>
+		<tags>1</tags>
+		<tags_special>1</tags_special>
+	</tx_blogexample_domain_model_person>
+	<tx_blogexample_domain_model_person>
+		<uid>2</uid>
+		<pid>0</pid>
+		<firstname>Author</firstname>
+		<lastname>With tag</lastname>
+		<tags>1</tags>
+		<tags_special>0</tags_special>
+	</tx_blogexample_domain_model_person>
+	<tx_blogexample_domain_model_person>
+		<uid>3</uid>
+		<pid>0</pid>
+		<firstname>Author</firstname>
+		<lastname>With special tag</lastname>
+		<tags>0</tags>
+		<tags_special>1</tags_special>
+	</tx_blogexample_domain_model_person>
+</dataset>
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-post-mm.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-post-mm.xml
new file mode 100644
index 000000000000..7eeccfbf009f
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-post-mm.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dataset>
+	<!-- Blog post 2 is related to blog post 1 -->
+	<tx_blogexample_post_post_mm>
+		<uid_local>2</uid_local>
+		<uid_foreign>1</uid_foreign>
+		<sorting>1</sorting>
+		<sorting_foreign>1</sorting_foreign>
+	</tx_blogexample_post_post_mm>
+</dataset>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-tag-mm.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-tag-mm.xml
index 128edfe36f19..222a5904d006 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-tag-mm.xml
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-tag-mm.xml
@@ -123,4 +123,16 @@
 		<sorting>1</sorting>
 		<sorting_foreign>10</sorting_foreign>
 	</tx_blogexample_post_tag_mm>
+	<tx_blogexample_post_tag_mm>
+		<uid_local>12</uid_local>
+		<uid_foreign>12</uid_foreign>
+		<sorting>1</sorting>
+		<sorting_foreign>10</sorting_foreign>
+	</tx_blogexample_post_tag_mm>
+	<tx_blogexample_post_tag_mm>
+		<uid_local>13</uid_local>
+		<uid_foreign>12</uid_foreign>
+		<sorting>1</sorting>
+		<sorting_foreign>10</sorting_foreign>
+	</tx_blogexample_post_tag_mm>
 </dataset>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/posts.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/posts.xml
index 598f423521e6..4676719f4ea2 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/posts.xml
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/posts.xml
@@ -12,6 +12,7 @@
 		<l18n_diffsource></l18n_diffsource>
 		<sorting>1</sorting>
 		<deleted>0</deleted>
+		<related_posts>1</related_posts>
 	</tx_blogexample_domain_model_post>
 	<tx_blogexample_domain_model_post>
 		<uid>2</uid>
@@ -133,4 +134,72 @@
 		<sorting>11</sorting>
 		<deleted>0</deleted>
 	</tx_blogexample_domain_model_post>
+	<tx_blogexample_domain_model_post>
+		<uid>12</uid>
+		<pid>0</pid>
+		<blog>3</blog>
+		<author>0</author>
+		<tags>1</tags>
+		<categories>0</categories>
+		<title>post with tag</title>
+		<content>Lorem ipsum...</content>
+		<l18n_diffsource></l18n_diffsource>
+		<sorting>1</sorting>
+		<deleted>0</deleted>
+		<hidden>0</hidden>
+	</tx_blogexample_domain_model_post>
+	<tx_blogexample_domain_model_post>
+		<uid>13</uid>
+		<pid>0</pid>
+		<blog>3</blog>
+		<author>1</author>
+		<tags>0</tags>
+		<categories>0</categories>
+		<title>post with tagged author</title>
+		<content>Lorem ipsum...</content>
+		<l18n_diffsource></l18n_diffsource>
+		<sorting>2</sorting>
+		<deleted>0</deleted>
+		<hidden>0</hidden>
+	</tx_blogexample_domain_model_post>
+	<tx_blogexample_domain_model_post>
+		<uid>14</uid>
+		<pid>0</pid>
+		<blog>3</blog>
+		<author>1</author>
+		<tags>1</tags>
+		<categories>0</categories>
+		<title>post with tag and tagged author</title>
+		<content>Lorem ipsum...</content>
+		<l18n_diffsource></l18n_diffsource>
+		<sorting>2</sorting>
+		<deleted>0</deleted>
+		<hidden>0</hidden>
+	</tx_blogexample_domain_model_post>
+	<tx_blogexample_domain_model_post>
+		<uid>20</uid>
+		<pid>0</pid>
+		<blog>3</blog>
+		<tags>0</tags>
+		<categories>0</categories>
+		<title>post20 hidden</title>
+		<content>Lorem ipsum...</content>
+		<l18n_diffsource></l18n_diffsource>
+		<sorting>11</sorting>
+		<deleted>0</deleted>
+		<hidden>1</hidden>
+	</tx_blogexample_domain_model_post>
+	<tx_blogexample_domain_model_post>
+		<uid>30</uid>
+		<pid>0</pid>
+		<blog>3</blog>
+		<tags>0</tags>
+		<categories>0</categories>
+		<title>post30 deleted</title>
+		<content>Lorem ipsum...</content>
+		<l18n_diffsource></l18n_diffsource>
+		<sorting>11</sorting>
+		<deleted>1</deleted>
+		<hidden>0</hidden>
+	</tx_blogexample_domain_model_post>
 </dataset>
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags-mm.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags-mm.xml
new file mode 100644
index 000000000000..560562c6c83d
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags-mm.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dataset>
+	<tx_blogexample_domain_model_tag_mm>
+		<uid_local>1</uid_local>
+		<uid_foreign>11</uid_foreign>
+		<tablenames>tx_blogexample_domain_model_person</tablenames>
+		<fieldname>tags</fieldname>
+		<sorting>1</sorting>
+		<sorting_foreign>1</sorting_foreign>
+	</tx_blogexample_domain_model_tag_mm>
+	<tx_blogexample_domain_model_tag_mm>
+		<uid_local>1</uid_local>
+		<uid_foreign>13</uid_foreign>
+		<tablenames>tx_blogexample_domain_model_person</tablenames>
+		<fieldname>tags_special</fieldname>
+		<sorting>1</sorting>
+		<sorting_foreign>1</sorting_foreign>
+	</tx_blogexample_domain_model_tag_mm>
+	<tx_blogexample_domain_model_tag_mm>
+		<uid_local>2</uid_local>
+		<uid_foreign>11</uid_foreign>
+		<tablenames>tx_blogexample_domain_model_person</tablenames>
+		<fieldname>tags</fieldname>
+		<sorting>1</sorting>
+		<sorting_foreign>1</sorting_foreign>
+	</tx_blogexample_domain_model_tag_mm>
+	<tx_blogexample_domain_model_tag_mm>
+		<uid_local>3</uid_local>
+		<uid_foreign>13</uid_foreign>
+		<tablenames>tx_blogexample_domain_model_person</tablenames>
+		<fieldname>tags_special</fieldname>
+		<sorting>1</sorting>
+		<sorting_foreign>1</sorting_foreign>
+	</tx_blogexample_domain_model_tag_mm>
+</dataset>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags.xml
index 71ef96979ad2..e1a2b8c0cd38 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags.xml
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags.xml
@@ -70,4 +70,25 @@
 		<name>Tag10</name>
 		<deleted>0</deleted>
 	</tx_blogexample_domain_model_tag>
+	<tx_blogexample_domain_model_tag>
+		<uid>11</uid>
+		<pid>0</pid>
+		<posts>0</posts>
+		<name>TagForAuthor1</name>
+		<deleted>0</deleted>
+	</tx_blogexample_domain_model_tag>
+	<tx_blogexample_domain_model_tag>
+		<uid>12</uid>
+		<pid>0</pid>
+		<posts>0</posts>
+		<name>Tag12</name>
+		<deleted>0</deleted>
+	</tx_blogexample_domain_model_tag>
+	<tx_blogexample_domain_model_tag>
+		<uid>13</uid>
+		<pid>0</pid>
+		<posts>0</posts>
+		<name>SpecialTagForAuthor1</name>
+		<deleted>0</deleted>
+	</tx_blogexample_domain_model_tag>
 </dataset>
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/InTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/InTest.php
index bc5eb4a72b87..5ba238c56ff2 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/InTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/InTest.php
@@ -156,7 +156,9 @@ class InTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 * @test
 	 */
 	public function inConditionWorksWithQueryResult() {
-		$queryResult = $this->blogRepository->findAll();
+		$query = $this->blogRepository->createQuery();
+		$query->matching($query->in('uid', array(1,2)));
+		$queryResult = $query->execute();
 
 		$inQuery = $this->postRepository->createQuery();
 
@@ -172,7 +174,9 @@ class InTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 * @test
 	 */
 	public function inConditionWorksWithQueryResultOnSecondCall() {
-		$queryResult = $this->blogRepository->findAll();
+		$query = $this->blogRepository->createQuery();
+		$query->matching($query->in('uid', array(1,2)));
+		$queryResult = $query->execute();
 
 		$inQuery = $this->postRepository->createQuery();
 
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/QueryParserTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/QueryParserTest.php
index b2c2f688e278..f6ccb93f3973 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/QueryParserTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/QueryParserTest.php
@@ -38,12 +38,23 @@ class QueryParserTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 */
 	protected $objectManager;
 
+	/**
+	 * @var \ExtbaseTeam\BlogExample\Domain\Repository\BlogRepository
+	 */
+	protected $blogRepository;
+
 	/**
 	 * Sets up this test suite.
 	 */
 	protected function setUp() {
 		parent::setUp();
 
+		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags.xml');
+		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/tags-mm.xml');
+		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/persons.xml');
+		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/posts.xml');
+		$this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/post-tag-mm.xml');
+
 		$this->objectManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
 		$this->queryParser = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class);
 		$this->blogRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\BlogRepository::class);
@@ -119,4 +130,42 @@ class QueryParserTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->assertNotSame($hashWithCaseSensitiveFalse, $hashWithCaseSensitiveTrue);
 	}
 
+	/**
+	 * @test
+	 */
+	public function queryWithMultipleRelationsToIdenticalTablesReturnsExpectedResultForOrQuery() {
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
+		$postRepository = $this->objectManager->get('ExtbaseTeam\\BlogExample\\Domain\\Repository\\PostRepository');
+		$query = $postRepository->createQuery();
+		$query->matching(
+			$query->logicalAnd(
+				$query->equals('blog', 3),
+				$query->logicalOr(
+					$query->equals('tags.name', 'Tag12'),
+					$query->equals('author.tags.name', 'TagForAuthor1')
+				)
+			)
+		);
+		$result = $query->execute()->toArray();
+		$this->assertEquals(3, count($result));
+	}
+
+	/**
+	 * @test
+	 */
+	public function queryWithMultipleRelationsToIdenticalTablesReturnsExpectedResultForAndQuery() {
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
+		$postRepository = $this->objectManager->get('ExtbaseTeam\\BlogExample\\Domain\\Repository\\PostRepository');
+		$query = $postRepository->createQuery();
+		$query->matching(
+			$query->logicalAnd(
+				$query->equals('blog', 3),
+				$query->equals('tags.name', 'Tag12'),
+				$query->equals('author.tags.name', 'TagForAuthor1')
+			)
+		);
+		$result = $query->execute()->toArray();
+		$this->assertEquals(1, count($result));
+	}
+
 }
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/RelationTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/RelationTest.php
index a23e5ca2000a..5982ef89b864 100644
--- a/typo3/sysext/extbase/Tests/Functional/Persistence/RelationTest.php
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/RelationTest.php
@@ -19,11 +19,6 @@ use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
 
 class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 
-	/**
-	 * @var int number of all records
-	 */
-	protected $numberOfRecordsInFixture = 11;
-
 	/**
 	 * @var \ExtbaseTeam\BlogExample\Domain\Model\Blog
 	 */
@@ -70,10 +65,10 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 * @test
 	 */
 	public function attachPostToBlogAtTheEnd() {
-		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post');
-		$this->assertSame($this->numberOfRecordsInFixture, $countPosts);
+		$countPostsOriginal = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'blog =' . $this->blog->getUid());
 
 		$newPostTitle = 'sdufhisdhuf';
+		/** @var \ExtbaseTeam\BlogExample\Domain\Model\Post $newPost */
 		$newPost = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Model\Post::class);
 		$newPost->setBlog($this->blog);
 		$newPost->setTitle($newPostTitle);
@@ -82,12 +77,12 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->blog->addPost($newPost);
 		$this->updateAndPersistBlog();
 
-		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post');
-		$this->assertSame(($this->numberOfRecordsInFixture + 1), $countPosts);
+		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'blog =' . $this->blog->getUid());
+		$this->assertSame(($countPostsOriginal + 1), $countPosts);
 
 		$post = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('title,sorting', 'tx_blogexample_domain_model_post', 'blog =' . $this->blog->getUid(), '', 'sorting DESC');
 		$this->assertSame($newPostTitle, $post['title']);
-		$this->assertSame((string)($this->numberOfRecordsInFixture), $post['sorting']);
+		$this->assertSame((string)($countPostsOriginal + 1), $post['sorting']);
 	}
 
 	/**
@@ -96,8 +91,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 * @test
 	 */
 	public function removeLastPostFromBlog() {
-		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post');
-		$this->assertSame($this->numberOfRecordsInFixture, $countPosts);
+		$countPostsOriginal = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'deleted=0');
 
 		$post = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('sorting', 'tx_blogexample_domain_model_post', 'blog =' . $this->blog->getUid(), '', 'sorting DESC');
 		$this->assertEquals(10, $post['sorting']);
@@ -112,7 +106,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->updateAndPersistBlog();
 
 		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'deleted=0');
-		$this->assertEquals(($this->numberOfRecordsInFixture - 1), $countPosts);
+		$this->assertEquals(($countPostsOriginal - 1), $countPosts);
 
 		$post = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', 'tx_blogexample_domain_model_post', 'uid =' . $latestPost->getUid() . ' AND deleted=0');
 		$this->assertSame(NULL, $post['uid']);
@@ -128,8 +122,10 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 * @test
 	 */
 	public function addPostToBlogInTheMiddle() {
-		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post');
-		$this->assertSame($this->numberOfRecordsInFixture, $countPosts);
+		$countPostsOriginal = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'deleted=0');
+
+		/** @var \ExtbaseTeam\BlogExample\Domain\Model\Post $newPost */
+		$newPost = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Model\Post::class);
 
 		$posts = clone $this->blog->getPosts();
 		$this->blog->getPosts()->removeAll($posts);
@@ -138,7 +134,6 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		foreach ($posts as $post) {
 			$this->blog->addPost($post);
 			if ($counter == 5) {
-				$newPost = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Model\Post::class);
 				$newPost->setBlog($this->blog);
 				$newPost->setTitle($newPostTitle);
 				$newPost->setContent('Bla Bla Bla');
@@ -148,8 +143,8 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		}
 		$this->updateAndPersistBlog();
 
-		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post',  'deleted=0');
-		$this->assertSame(($this->numberOfRecordsInFixture + 1), $countPosts);
+		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'deleted=0');
+		$this->assertSame(($countPostsOriginal + 1), $countPosts);
 
 		//last post
 		$post = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('title,sorting', 'tx_blogexample_domain_model_post', 'blog =' . $this->blog->getUid(), '', 'sorting DESC');
@@ -157,7 +152,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->assertSame('11', $post['sorting']);
 
 		// check sorting of the post added in the middle
-		$post = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('title,sorting', 'tx_blogexample_domain_model_post', 'uid=' . ($this->numberOfRecordsInFixture + 1));
+		$post = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('title,sorting', 'tx_blogexample_domain_model_post', 'uid=' . $newPost->getUid());
 		$this->assertSame($newPostTitle, $post['title']);
 		$this->assertSame('6', $post['sorting']);
 	}
@@ -168,8 +163,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 * @test
 	 */
 	public function removeMiddlePostFromBlog() {
-		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post');
-		$this->assertSame($this->numberOfRecordsInFixture, $countPosts);
+		$countPostsOriginal = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'deleted=0');
 
 		$posts = clone $this->blog->getPosts();
 		$counter = 1;
@@ -182,7 +176,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->updateAndPersistBlog();
 
 		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'deleted=0');
-		$this->assertSame(($this->numberOfRecordsInFixture - 1), $countPosts);
+		$this->assertSame(($countPostsOriginal - 1), $countPosts);
 
 		$post = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('title,sorting', 'tx_blogexample_domain_model_post', 'blog =' . $this->blog->getUid(), '', 'sorting DESC');
 		$this->assertSame('Post10', $post['title']);
@@ -195,8 +189,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 * @test
 	 */
 	public function movePostFromEndToTheMiddle() {
-		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post');
-		$this->assertSame($this->numberOfRecordsInFixture, $countPosts);
+		$countPostsOriginal = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'deleted=0');
 
 		$posts = clone $this->blog->getPosts();
 		$postsArray = $posts->toArray();
@@ -218,7 +211,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->updateAndPersistBlog();
 
 		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_post', 'deleted=0');
-		$this->assertSame($this->numberOfRecordsInFixture, $countPosts);
+		$this->assertSame($countPostsOriginal, $countPosts);
 
 		$post = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('title,sorting', 'tx_blogexample_domain_model_post', 'blog =' . $this->blog->getUid(), '', 'sorting DESC');
 		$this->assertSame('Post9', $post['title']);
@@ -235,12 +228,14 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 * @test
 	 */
 	public function attachTagToPostAtTheEnd() {
-		$count = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_tag');
-		$this->assertSame(10, $count);
+		$countOriginal = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_tag');
 
 		$newTagTitle = 'sdufhisdhuf';
-		$newTag = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Model\Tag::class, $newTagTitle);
 
+		/** @var \ExtbaseTeam\BlogExample\Domain\Model\Tag $newTag */
+		$newTag = $this->objectManager->get('ExtbaseTeam\\BlogExample\\Domain\\Model\\Tag', $newTagTitle);
+
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
 		$postRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PostRepository::class);
 		$post = $postRepository->findByUid(1);
 		$post->addTag($newTag);
@@ -249,22 +244,21 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->persistentManager->persistAll();
 
 		$count = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_tag');
-		$this->assertSame(11, $count);
+		$this->assertSame(($countOriginal + 1), $count);
 
 		$tag = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid_foreign', 'tx_blogexample_post_tag_mm', 'uid_local =' . $post->getUid(), '', 'sorting DESC');
-		$this->assertSame('11', $tag['uid_foreign']);
+		$this->assertSame($newTag->getUid(), (int)$tag['uid_foreign']);
 	}
 
-
 	/**
 	 * Tests removing object from the end of sorted M:M relation (Post:Tag)
 	 *
 	 * @test
 	 */
-	public  function removeLastTagFromPost() {
-		$count = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_tag');
-		$this->assertSame(10, $count);
+	public function removeLastTagFromPost() {
+		$countOriginal = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_tag', 'deleted=0');
 
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
 		$postRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PostRepository::class);
 		$post = $postRepository->findByUid(1);
 		$tags = $post->getTags();
@@ -278,8 +272,8 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$postRepository->update($post);
 		$this->persistentManager->persistAll();
 
-		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_tag', 'deleted=0' );
-		$this->assertEquals(10, $countPosts);
+		$countPosts = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_domain_model_tag', 'deleted=0');
+		$this->assertEquals($countOriginal, $countPosts);
 
 		$tag = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid_foreign', 'tx_blogexample_post_tag_mm', 'uid_local =' . $post->getUid(), '', 'sorting DESC');
 		$this->assertSame('9', $tag['uid_foreign']);
@@ -293,20 +287,22 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	 *
 	 * @test
 	 */
-	public  function addTagToPostInTheMiddle() {
-		$countTags = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_post_tag_mm', 'uid_local=1');
-		$this->assertSame(10, $countTags);
+	public function addTagToPostInTheMiddle() {
+		$countTagsOriginal = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_post_tag_mm', 'uid_local=1');
 
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
 		$postRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PostRepository::class);
 		$post = $postRepository->findByUid(1);
 		$tags = clone $post->getTags();
 		$post->setTags(new ObjectStorage());
 
+		/** @var \ExtbaseTeam\BlogExample\Domain\Model\Tag $newTag */
+		$newTag = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Model\Tag::class, 'INSERTED TAG at position 6 : ' . strftime(''));
+
 		$counter = 1;
 		foreach ($tags as $tag) {
 			$post->addTag($tag);
 			if ($counter == 5) {
-				$newTag = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Model\Tag::class, 'INSERTED TAG at position 6 : ' . strftime(''));
 				$post->addTag($newTag);
 			}
 			$counter++;
@@ -316,16 +312,15 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$this->persistentManager->persistAll();
 
 		$countTags = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_post_tag_mm', 'uid_local=1');
-		$this->assertSame(11, $countTags);
+		$this->assertSame(($countTagsOriginal + 1), $countTags);
 
 		$tag = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid_foreign', 'tx_blogexample_post_tag_mm', 'uid_local =' . $post->getUid(), '', 'sorting DESC');
 		$this->assertSame('10', $tag['uid_foreign']);
 
 		$tag = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid_foreign', 'tx_blogexample_post_tag_mm', 'uid_local =' . $post->getUid() . ' AND sorting=6');
-		$this->assertSame('11', $tag['uid_foreign']);
+		$this->assertSame($newTag->getUid(), (int)$tag['uid_foreign']);
 	}
 
-
 	/**
 	 * Tests removing object from the middle of the sorted M:M relation (Post:Tag)
 	 *
@@ -335,6 +330,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$countTags = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_post_tag_mm', 'uid_local=1');
 		$this->assertSame(10, $countTags);
 
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
 		$postRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PostRepository::class);
 		$post = $postRepository->findByUid(1);
 		$tags = clone $post->getTags();
@@ -369,6 +365,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$countTags = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'tx_blogexample_post_tag_mm', 'uid_local=1');
 		$this->assertSame(10, $countTags);
 
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
 		$postRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PostRepository::class);
 		$post = $postRepository->findByUid(1);
 		$tags = clone $post->getTags();
@@ -413,6 +410,7 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 	public function timestampFieldIsUpdatedOnPostSave() {
 		$rawPost = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'tx_blogexample_domain_model_post', 'uid=1');
 
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
 		$postRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PostRepository::class);
 		$post = $postRepository->findByUid(1);
 		$post->setTitle("newTitle");
@@ -471,9 +469,11 @@ class RelationTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
 		$countCategories = $this->getDatabaseConnection()->exec_SELECTcountRows('*', 'sys_category_record_mm', 'uid_foreign=1 AND tablenames="tx_blogexample_domain_model_post" AND fieldname="categories"');
 		$this->assertSame(3, $countCategories);
 
+		/** @var \ExtbaseTeam\BlogExample\Domain\Repository\PostRepository $postRepository */
 		$postRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\PostRepository::class);
 		$post = $postRepository->findByUid(1);
 
+		/** @var \TYPO3\CMS\Extbase\Domain\Model\Category $newCategory */
 		$newCategory = $this->objectManager->get(\TYPO3\CMS\Extbase\Domain\Model\Category::class);
 		$newCategory->setTitle('New Category');
 
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbQueryParserTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbQueryParserTest.php
index 635f4f3ce543..cccd303c50bd 100644
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbQueryParserTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Storage/Typo3DbQueryParserTest.php
@@ -25,10 +25,9 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		);
 		/** @var \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings|\PHPUnit_Framework_MockObject_MockObject $querySettings */
 		$querySettings = $this->getMock(\TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings::class);
-		$sql = array();
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
-		$mockTypo3DbQueryParser->_callRef('addSysLanguageStatement', $table, $sql, $querySettings);
-		$expectedSql = array('additionalWhereClause' => array('(' . $table . '.sys_language_uid IN (0,-1))'));
+		$sql = $mockTypo3DbQueryParser->_callRef('getSysLanguageStatement', $table, $table, $querySettings);
+		$expectedSql = '(' . $table . '.sys_language_uid IN (0,-1))';
 		$this->assertSame($expectedSql, $sql);
 	}
 
@@ -40,13 +39,12 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		$GLOBALS['TCA'][$table]['ctrl'] = array(
 			'languageField' => 'sys_language_uid'
 		);
-		$sql = array();
 		/** @var \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings|\PHPUnit_Framework_MockObject_MockObject $querySettings */
 		$querySettings = $this->getMock(\TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings::class, array('dummy'));
 		$querySettings->setLanguageUid('1');
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
-		$mockTypo3DbQueryParser->_callRef('addSysLanguageStatement', $table, $sql, $querySettings);
-		$result = array('additionalWhereClause' => array('(' . $table . '.sys_language_uid IN (1,-1))'));
+		$sql = $mockTypo3DbQueryParser->_callRef('getSysLanguageStatement', $table, $table, $querySettings);
+		$result = '(' . $table . '.sys_language_uid IN (1,-1))';
 		$this->assertSame($result, $sql);
 	}
 
@@ -58,11 +56,10 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		$GLOBALS['TCA'][$table]['ctrl'] = array(
 			'languageField' => 'sys_language_uid'
 		);
-		$sql = array();
 		$querySettings = new \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings();
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
-		$mockTypo3DbQueryParser->_callRef('addSysLanguageStatement', $table, $sql, $querySettings);
-		$expectedSql = array('additionalWhereClause' => array('(' . $table . '.sys_language_uid IN (0,-1))'));
+		$sql = $mockTypo3DbQueryParser->_callRef('getSysLanguageStatement', $table, $table, $querySettings);
+		$expectedSql = '(' . $table . '.sys_language_uid IN (0,-1))';
 		$this->assertSame($expectedSql, $sql);
 	}
 
@@ -75,12 +72,11 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 			'languageField' => 'sys_language_uid',
 			'delete' => 'deleted'
 		);
-		$sql = array();
 		$querySettings = new \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings();
 		$querySettings->setLanguageUid(0);
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
-		$mockTypo3DbQueryParser->_callRef('addSysLanguageStatement', $table, $sql, $querySettings);
-		$expectedSql = array('additionalWhereClause' => array('(' . $table . '.sys_language_uid IN (0,-1))'));
+		$sql = $mockTypo3DbQueryParser->_callRef('getSysLanguageStatement', $table, $table, $querySettings);
+		$expectedSql = '(' . $table . '.sys_language_uid IN (0,-1))';
 		$this->assertSame($expectedSql, $sql);
 	}
 
@@ -92,12 +88,11 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		$GLOBALS['TCA'][$table]['ctrl'] = array(
 			'languageField' => 'sys_language_uid'
 		);
-		$sql = array();
 		$querySettings = new \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings();
 		$querySettings->setLanguageUid(2);
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
-		$mockTypo3DbQueryParser->_callRef('addSysLanguageStatement', $table, $sql, $querySettings);
-		$expectedSql = array('additionalWhereClause' => array('(' . $table . '.sys_language_uid IN (2,-1))'));
+		$sql = $mockTypo3DbQueryParser->_callRef('getSysLanguageStatement', $table, $table, $querySettings);
+		$expectedSql = '(' . $table . '.sys_language_uid IN (2,-1))';
 		$this->assertSame($expectedSql, $sql);
 	}
 
@@ -110,12 +105,11 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 			'languageField' => 'sys_language_uid',
 			'transOrigPointerField' => 'l10n_parent'
 		);
-		$sql = array();
 		$querySettings = new \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings();
 		$querySettings->setLanguageUid(2);
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
-		$mockTypo3DbQueryParser->_callRef('addSysLanguageStatement', $table, $sql, $querySettings);
-		$expectedSql = array('additionalWhereClause' => array('(' . $table . '.sys_language_uid IN (2,-1) OR (' . $table . '.sys_language_uid=0 AND ' . $table . '.uid NOT IN (SELECT ' . $table . '.l10n_parent FROM ' . $table . ' WHERE ' . $table . '.l10n_parent>0 AND ' . $table . '.sys_language_uid=2)))'));
+		$sql = $mockTypo3DbQueryParser->_callRef('getSysLanguageStatement', $table, $table, $querySettings);
+		$expectedSql = '(' . $table . '.sys_language_uid IN (2,-1) OR (' . $table . '.sys_language_uid=0 AND ' . $table . '.uid NOT IN (SELECT ' . $table . '.l10n_parent FROM ' . $table . ' WHERE ' . $table . '.l10n_parent>0 AND ' . $table . '.sys_language_uid=2)))';
 		$this->assertSame($expectedSql, $sql);
 	}
 
@@ -129,19 +123,16 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 			'transOrigPointerField' => 'l10n_parent',
 			'delete' => 'deleted'
 		);
-		$sql = array();
 		$querySettings = new \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings();
 		$querySettings->setLanguageUid(2);
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
-		$mockTypo3DbQueryParser->_callRef('addSysLanguageStatement', $table, $sql, $querySettings);
-		$expectedSql = array('additionalWhereClause' => array(
-			'(' . $table . '.sys_language_uid IN (2,-1)' .
+		$sql= $mockTypo3DbQueryParser->_callRef('getSysLanguageStatement', $table, $table, $querySettings);
+		$expectedSql = '(' . $table . '.sys_language_uid IN (2,-1)' .
 				' OR (' . $table . '.sys_language_uid=0 AND ' . $table . '.uid NOT IN (' .
 				'SELECT ' . $table . '.l10n_parent FROM ' . $table .
 				' WHERE ' . $table . '.l10n_parent>0 AND ' .
 				$table . '.sys_language_uid=2 AND ' .
-				$table . '.deleted=0)))')
-		);
+				$table . '.deleted=0)))';
 		$this->assertSame($expectedSql, $sql);
 	}
 
@@ -149,26 +140,22 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 	 * @test
 	 */
 	public function addSysLanguageStatementWorksInBackendContextWithSubselectionTakesDeleteStatementIntoAccountIfNecessary() {
-		$table = $this->getUniqueId('tx_coretest_table');
 		$table = 'tt_content';
 		$GLOBALS['TCA'][$table]['ctrl'] = array(
 			'languageField' => 'sys_language_uid',
 			'transOrigPointerField' => 'l10n_parent',
 			'delete' => 'deleted'
 		);
-		$sql = array();
 		$querySettings = new \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings();
 		$querySettings->setLanguageUid(2);
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
-		$mockTypo3DbQueryParser->_callRef('addSysLanguageStatement', $table, $sql, $querySettings);
-		$expectedSql = array('additionalWhereClause' => array(
-			'(' . $table . '.sys_language_uid IN (2,-1)' .
+		$sql = $mockTypo3DbQueryParser->_callRef('getSysLanguageStatement', $table, $table, $querySettings);
+		$expectedSql = '(' . $table . '.sys_language_uid IN (2,-1)' .
 				' OR (' . $table . '.sys_language_uid=0 AND ' . $table . '.uid NOT IN (' .
 				'SELECT ' . $table . '.l10n_parent FROM ' . $table .
 				' WHERE ' . $table . '.l10n_parent>0 AND ' .
 				$table . '.sys_language_uid=2 AND ' .
-				$table . '.deleted=0)))')
-		);
+				$table . '.deleted=0)))';
 		$this->assertSame($expectedSql, $sql);
 	}
 
@@ -230,14 +217,14 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
 	public function providerForVisibilityConstraintStatement() {
 		return array(
-			'in be: include all' => array('BE', TRUE, array(), TRUE, NULL),
-			'in be: ignore enable fields but do not include deleted' => array('BE', TRUE, array(), FALSE, array('tx_foo_table.deleted_column=0')),
-			'in be: respect enable fields but include deleted' => array('BE', FALSE, array(), TRUE, array('tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789)')),
-			'in be: respect enable fields and do not include deleted' => array('BE', FALSE, array(), FALSE, array('tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789) AND tx_foo_table.deleted_column=0')),
-			'in fe: include all' => array('FE', TRUE, array(), TRUE, NULL),
-			'in fe: ignore enable fields but do not include deleted' => array('FE', TRUE, array(), FALSE, array('tx_foo_table.deleted_column=0')),
-			'in fe: ignore only starttime and do not include deleted' => array('FE', TRUE, array('starttime'), FALSE, array('tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0')),
-			'in fe: respect enable fields and do not include deleted' => array('FE', FALSE, array(), FALSE, array('tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0 AND tx_foo_table.starttime_column<=123456789'))
+			'in be: include all' => array('BE', TRUE, array(), TRUE, ''),
+			'in be: ignore enable fields but do not include deleted' => array('BE', TRUE, array(), FALSE, 'tx_foo_table.deleted_column=0'),
+			'in be: respect enable fields but include deleted' => array('BE', FALSE, array(), TRUE, 'tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789)'),
+			'in be: respect enable fields and do not include deleted' => array('BE', FALSE, array(), FALSE, 'tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789) AND tx_foo_table.deleted_column=0'),
+			'in fe: include all' => array('FE', TRUE, array(), TRUE, ''),
+			'in fe: ignore enable fields but do not include deleted' => array('FE', TRUE, array(), FALSE, 'tx_foo_table.deleted_column=0'),
+			'in fe: ignore only starttime and do not include deleted' => array('FE', TRUE, array('starttime'), FALSE, 'tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0'),
+			'in fe: respect enable fields and do not include deleted' => array('FE', FALSE, array(), FALSE, 'tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0 AND tx_foo_table.starttime_column<=123456789')
 		);
 	}
 
@@ -261,7 +248,6 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		$mockQuerySettings->expects($this->once())->method('getIgnoreEnableFields')->will($this->returnValue($ignoreEnableFields));
 		$mockQuerySettings->expects($this->once())->method('getEnableFieldsToBeIgnored')->will($this->returnValue($enableFieldsToBeIgnored));
 		$mockQuerySettings->expects($this->once())->method('getIncludeDeleted')->will($this->returnValue($deletedValue));
-		$sql = array();
 
 		/** @var $mockEnvironmentService \TYPO3\CMS\Extbase\Service\EnvironmentService | \PHPUnit_Framework_MockObject_MockObject */
 		$mockEnvironmentService = $this->getMock(\TYPO3\CMS\Extbase\Service\EnvironmentService::class, array('isEnvironmentInFrontendMode'));
@@ -269,17 +255,17 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
 		$mockTypo3DbQueryParser->_set('environmentService', $mockEnvironmentService);
-		$mockTypo3DbQueryParser->_callRef('addVisibilityConstraintStatement', $mockQuerySettings, $tableName, $sql);
-		$this->assertSame($expectedSql, $sql['additionalWhereClause']);
+		$resultSql = $mockTypo3DbQueryParser->_callRef('getVisibilityConstraintStatement', $mockQuerySettings, $tableName, $tableName);
+		$this->assertSame($expectedSql, $resultSql);
 		unset($GLOBALS['TCA'][$tableName]);
 	}
 
 	public function providerForRespectEnableFields() {
 		return array(
-			'in be: respectEnableFields=false' => array('BE', FALSE, NULL),
-			'in be: respectEnableFields=true' => array('BE', TRUE, array('tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789) AND tx_foo_table.deleted_column=0')),
-			'in FE: respectEnableFields=false' => array('FE', FALSE, NULL),
-			'in FE: respectEnableFields=true' => array('FE', TRUE, array('tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0 AND tx_foo_table.starttime_column<=123456789'))
+			'in be: respectEnableFields=false' => array('BE', FALSE, ''),
+			'in be: respectEnableFields=true' => array('BE', TRUE, 'tx_foo_table.disabled_column=0 AND (tx_foo_table.starttime_column<=123456789) AND tx_foo_table.deleted_column=0'),
+			'in FE: respectEnableFields=false' => array('FE', FALSE, ''),
+			'in FE: respectEnableFields=true' => array('FE', TRUE, 'tx_foo_table.deleted_column=0 AND tx_foo_table.disabled_column=0 AND tx_foo_table.starttime_column<=123456789')
 		);
 	}
 
@@ -303,7 +289,6 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		$mockQuerySettings = $this->getMock(\TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings::class, array('dummy'), array(), '', FALSE);
 		$mockQuerySettings->setIgnoreEnableFields(!$respectEnableFields);
 		$mockQuerySettings->setIncludeDeleted(!$respectEnableFields);
-		$sql = array();
 
 		/** @var $mockEnvironmentService \TYPO3\CMS\Extbase\Service\EnvironmentService | \PHPUnit_Framework_MockObject_MockObject */
 		$mockEnvironmentService = $this->getMock(\TYPO3\CMS\Extbase\Service\EnvironmentService::class, array('isEnvironmentInFrontendMode'));
@@ -311,8 +296,8 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
 		$mockTypo3DbQueryParser->_set('environmentService', $mockEnvironmentService);
-		$mockTypo3DbQueryParser->_callRef('addVisibilityConstraintStatement', $mockQuerySettings, $tableName, $sql);
-		$this->assertSame($expectedSql, $sql['additionalWhereClause']);
+		$actualSql = $mockTypo3DbQueryParser->_callRef('getVisibilityConstraintStatement', $mockQuerySettings, $tableName, $tableName);
+		$this->assertSame($expectedSql, $actualSql);
 		unset($GLOBALS['TCA'][$tableName]);
 	}
 
@@ -332,7 +317,6 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		$mockQuerySettings->expects($this->once())->method('getIgnoreEnableFields')->will($this->returnValue(FALSE));
 		$mockQuerySettings->expects($this->once())->method('getEnableFieldsToBeIgnored')->will($this->returnValue(array()));
 		$mockQuerySettings->expects($this->once())->method('getIncludeDeleted')->will($this->returnValue(TRUE));
-		$sql = array();
 
 		/** @var $mockEnvironmentService \TYPO3\CMS\Extbase\Service\EnvironmentService | \PHPUnit_Framework_MockObject_MockObject */
 		$mockEnvironmentService = $this->getMock(\TYPO3\CMS\Extbase\Service\EnvironmentService::class, array('isEnvironmentInFrontendMode'));
@@ -340,7 +324,7 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
 		$mockTypo3DbQueryParser->_set('environmentService', $mockEnvironmentService);
-		$mockTypo3DbQueryParser->_callRef('addVisibilityConstraintStatement', $mockQuerySettings, $tableName, $sql);
+		$mockTypo3DbQueryParser->_callRef('getVisibilityConstraintStatement', $mockQuerySettings, $tableName, $tableName);
 		unset($GLOBALS['TCA'][$tableName]);
 	}
 	/**
@@ -352,17 +336,17 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 			'set Pid to zero if rootLevel = 1' => array(
 				'1',
 				$table,
-				array('additionalWhereClause' => array($table . '.pid = 0'))
+				$table . '.pid = 0'
 			),
 			'set Pid to given Pids if rootLevel = 0' => array(
 				'0',
 				$table,
-				array('additionalWhereClause' => array($table . '.pid IN (42, 27)'))
+				$table . '.pid IN (42, 27)'
 			),
 			'set no statement if rootLevel = -1' => array(
 				'-1',
 				$table,
-				array()
+				''
 			)
 		);
 	}
@@ -376,13 +360,12 @@ class Typo3DbQueryParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		$GLOBALS['TCA'][$table]['ctrl'] = array(
 			'rootLevel' => $rootLevel
 		);
-		$sql = array();
 		$storagePageIds = array(42,27);
 		$mockTypo3DbQueryParser = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser::class, array('dummy'), array(), '', FALSE);
 		$mockFrontendVariableCache = $this->getMock(\TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class, array(), array(), '', FALSE);
 		$mockTypo3DbQueryParser->_set('tableColumnCache', $mockFrontendVariableCache);
 		$mockFrontendVariableCache->expects($this->once())->method('get')->will($this->returnValue(array('pid' => '42')));
-		$mockTypo3DbQueryParser->_callRef('addPageIdStatement', $table, $sql, $storagePageIds);
+		$sql = $mockTypo3DbQueryParser->_callRef('getPageIdStatement', $table, $table, $storagePageIds);
 
 		$this->assertSame($expectedSql, $sql);
 	}
-- 
GitLab