From 21d37fb2374b28f6b9c3f9acd1103e28c3366bfe Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Wed, 5 Apr 2023 17:07:23 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B06085=20-=20UNION=20is=20not=20supported?= =?UTF-8?q?=20in=20UserRightsProfile::GetSelectFilter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/dbobjectsearch.class.php | 142 ++++++++++-------- .../unitary-tests/core/DBUnionSearchTest.php | 66 ++++++++ 2 files changed, 142 insertions(+), 66 deletions(-) diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 8be53e24b..2a9fa16b3 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -1103,22 +1103,39 @@ class DBObjectSearch extends DBSearch public function Filter($sClassAlias, DBSearch $oFilter) { // If the conditions are the correct ones for Intersect - if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(),$this->GetFirstJoinedClass())) - { + if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(), $this->GetFirstJoinedClass())) { return $this->Intersect($oFilter); } - /** @var \DBObjectSearch $oFilteredSearch */ - $oFilteredSearch = $this->DeepClone(); - $oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oFilter, $this->m_aClasses); - if ($oFilterExpression === false) - { - throw new CoreException("Limitation: cannot filter search"); + if ($oFilter instanceof DBUnionSearch) { + $aFilters = $oFilter->GetSearches(); + } else { + $aFilters = [$oFilter]; } - $oFilteredSearch->AddConditionExpression($oFilterExpression); + $aSearches = []; + foreach ($aFilters as $oRightFilter) { + /** @var \DBObjectSearch $oFilteredSearch */ + $oFilteredSearch = $this->DeepClone(); + $oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oRightFilter, $this->m_aClasses); + if ($oFilterExpression === false) { + throw new CoreException("Limitation: cannot filter search"); + } - return $oFilteredSearch; + $oFilteredSearch->AddConditionExpression($oFilterExpression); + $aSearches[] = $oFilteredSearch; + } + + if (count($aSearches) == 0) { + throw new CoreException('Filtering '.$this->ToOQL().' by '.$oFilter->ToOQL().' failed'); + } + + if (count($aSearches) == 1) { + // return a DBObjectSearch + return $aSearches[0]; + } + + return new DBUnionSearch($aSearches); } /** @@ -1184,22 +1201,10 @@ class DBObjectSearch extends DBSearch * @throws \CoreException */ public function Intersect(DBSearch $oFilter) - { - return $this->IntersectSubClass($oFilter, $this->m_aClasses); - } - - /** - * @param \DBSearch $oFilter - * @param array $aRootClasses classes of the root search (for aliases) - * - * @return \DBUnionSearch|mixed - * @throws \CoreException - */ - protected function IntersectSubClass(DBSearch $oFilter, $aRootClasses) { if ($oFilter instanceof DBUnionSearch) { - // Develop! + // Develop! $aFilters = $oFilter->GetSearches(); } else @@ -1210,56 +1215,61 @@ class DBObjectSearch extends DBSearch $aSearches = array(); foreach ($aFilters as $oRightFilter) { - // Limitation: the queried class must be the first declared class - if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias()) - { - throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})"); - } - - /** @var \DBObjectSearch $oLeftFilter */ - $oLeftFilter = $this->DeepClone(); - $oRightFilter = $oRightFilter->DeepClone(); - - $bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed()); - if ($bAllowAllData) - { - $oLeftFilter->AllowAllData(); - } - - if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass()) - { - if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass())) - { - // Specialize $oLeftFilter - $oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias()); - } - elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass())) - { - // Specialize $oRightFilter - $oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass()); - } - else - { - throw new CoreException("Attempting to merge a filter of class '{$oLeftFilter->GetClass()}' with a filter of class '{$oRightFilter->GetClass()}'"); - } - } - - $aAliasTranslation = array(); - $oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation); - $oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation); - $oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation); - $oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation); - $aSearches[] = $oLeftFilter; + $aSearches[] = $this->IntersectSubClass($oRightFilter, $this->m_aClasses); } + if (count($aSearches) == 1) { // return a DBObjectSearch return $aSearches[0]; } - else - { - return new DBUnionSearch($aSearches); + + return new DBUnionSearch($aSearches); + } + + /** + * @param \DBObjectSearch $oRightFilter + * @param array $aRootClasses classes of the root search (for aliases) + * + * @return \DBObjectSearch + * @throws \CoreException + */ + protected function IntersectSubClass(DBObjectSearch $oRightFilter, array $aRootClasses): DBObjectSearch + { + // Limitation: the queried class must be the first declared class + if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias()) { + throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})"); } + + /** @var \DBObjectSearch $oLeftFilter */ + $oLeftFilter = $this->DeepClone(); + /** @var DBObjectSearch $oRightFilter */ + $oRightFilter = $oRightFilter->DeepClone(); + + $bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed()); + if ($bAllowAllData) { + $oLeftFilter->AllowAllData(); + } + + if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass()) { + if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass())) { + // Specialize $oLeftFilter + $oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias()); + } elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass())) { + // Specialize $oRightFilter + $oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass()); + } else { + throw new CoreException("Attempting to merge a filter of class '{$oLeftFilter->GetClass()}' with a filter of class '{$oRightFilter->GetClass()}'"); + } + } + + $aAliasTranslation = array(); + $oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation); + $oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation); + $oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation); + $oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation); + + return $oLeftFilter; } /** diff --git a/tests/php-unit-tests/unitary-tests/core/DBUnionSearchTest.php b/tests/php-unit-tests/unitary-tests/core/DBUnionSearchTest.php index e69de29bb..7934623e7 100644 --- a/tests/php-unit-tests/unitary-tests/core/DBUnionSearchTest.php +++ b/tests/php-unit-tests/unitary-tests/core/DBUnionSearchTest.php @@ -0,0 +1,66 @@ +AllowAllData(); + $oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects); + $this->InvokeNonPublicMethod(get_class($oSearch), 'SetDataFiltered', $oSearch, []); + + $this->debug($oSearch->ToOQL()); + + $oSet = new DBObjectSet($oSearch); + $oSet->Count(); + + $this->assertTrue(true); + } + + /** + * Ignored test (provokes PHP Error) + * + * @return void + * @throws \CoreException + * @throws \MissingQueryArgument + * @throws \MySQLException + * @throws \MySQLHasGoneAwayException + * @throws \OQLException + */ + public function testFilterOnSecondSelectedClass() + { + $sSourceOQL = 'SELECT P1, L1 FROM Person AS P1 JOIN Location AS L1 ON P1.location_id = L1.id WHERE L1.id = 1'; + $oSearch = DBSearch::FromOQL($sSourceOQL); + + $sFilterOQL = 'SELECT Location AS L2 WHERE L2.id = 2 UNION SELECT Location AS L3 WHERE L3.id = 3'; + $oVisibleObjects = DBSearch::FromOQL($sFilterOQL); + $sClassAlias = 'L1'; + $oVisibleObjects->AllowAllData(); + $oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects); + + $oSearch->ToOQL(); + + $oSet = new DBObjectSet($oSearch); + $oSet->CountWithLimit(1); + + $this->assertTrue(true); + } +}