From 58da108e855ee4dfca52330cb4ac771899782967 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 4 Dec 2019 13:58:56 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B01213=20-=20Allow=20NOT=20IN=20SELECT=20i?= =?UTF-8?q?n=20OQL=20syntax?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/dbobjectsearch.class.php | 19 +++++++++++--- core/oqlclasstreebuilder.class.inc.php | 10 ++------ test/OQL/OQLToSQLTest.php | 1 + test/core/DBSearchIntersectTest.php | 15 ++++++++++- test/core/OQLTest.php | 35 ++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 12 deletions(-) diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index ed8406fa7..e72f19031 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -699,16 +699,27 @@ class DBObjectSearch extends DBSearch } } - public function RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation = array()) + /** + * Rename aliases of nested queries to avoid duplicates with main query + * + * @param array $aClassAliases array of alias => class for the main query + * @param array $aAliasTranslation translation table of main query to apply to nested queries + */ + public function RenameNestedQueriesAliasesInNameSpace($aClassAliases, $aAliasTranslation = array()) { - // Recurse in nested queries - $this->GetCriteria()->Browse(function($oNode) use ($aClassAliases, $aAliasTranslation) { + $this->GetCriteria()->Browse(function ($oNode) use ($aClassAliases, $aAliasTranslation) { if ($oNode instanceof NestedQueryExpression) { $oNestedQuery = $oNode->GetNestedQuery(); $oNestedQuery->RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation); } }); + } + + public function RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation = array()) + { + // Recurse in nested queries + $this->RenameNestedQueriesAliasesInNameSpace($aClassAliases, $aAliasTranslation); $this->AddToNameSpace($aClassAliases, $aAliasTranslation); $this->TranslateConditions($aAliasTranslation, false, false); } @@ -1168,7 +1179,9 @@ class DBObjectSearch extends DBSearch } $aAliasTranslation = array(); + $oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation); $oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation); + $oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation); $oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation); $aSearches[] = $oLeftFilter; } diff --git a/core/oqlclasstreebuilder.class.inc.php b/core/oqlclasstreebuilder.class.inc.php index 63b7ca5ac..c75f61f7d 100644 --- a/core/oqlclasstreebuilder.class.inc.php +++ b/core/oqlclasstreebuilder.class.inc.php @@ -382,13 +382,7 @@ class OQLClassTreeBuilder */ private function TranslateNestedRequests() { - $this->oDBObjetSearch->GetCriteria()->Browse(function($oNode) { - if ($oNode instanceof NestedQueryExpression) - { - $oNestedQuery = $oNode->GetNestedQuery(); - $aClassAliases = $this->oDBObjetSearch->GetJoinedClasses(); - $oNestedQuery->RenameAliasesInNameSpace($aClassAliases); - } - }); + $aClassAliases = $this->oDBObjetSearch->GetJoinedClasses(); + $this->oDBObjetSearch->RenameNestedQueriesAliasesInNameSpace($aClassAliases); } } diff --git a/test/OQL/OQLToSQLTest.php b/test/OQL/OQLToSQLTest.php index caa23da54..d12b03f39 100644 --- a/test/OQL/OQLToSQLTest.php +++ b/test/OQL/OQLToSQLTest.php @@ -495,6 +495,7 @@ class OQLToSQLTest extends ItopDataTestCase "SELECT t 143" => array("SELECT `t` FROM TriggerOnObjectUpdate AS `t` WHERE (`t`.`target_class` IN ('appUserPreferences'))", array('t.friendlyname' => true), $aArgs), ); + $aData["SELECT UNION 1"] = array("SELECT `User` FROM User AS `User` WHERE 1 UNION SELECT `User` FROM User AS `User` WHERE (`User`.`id` = 3)", array(), array(), null, array(), 0, 0); $aData["SELECT 1"] = array("SELECT `UserInternal` FROM UserInternal AS `UserInternal` WHERE ((`UserInternal`.`login` = 'admin') AND (`UserInternal`.`status` = 'enabled'))", unserialize('a:1:{s:12:"friendlyname";b:1;}'), array(), null, array(), 0, 0); $aData["SELECT 2"] = array("SELECT `Contact` FROM Contact AS `Contact` WHERE (`Contact`.`id` = '987654321')", array(), array(), null, array(), 0, 0); $aData["SELECT 3"] = array("SELECT `Organization` FROM Organization AS `Organization` WHERE 1", array(), array(), null, array(), 4, 0); diff --git a/test/core/DBSearchIntersectTest.php b/test/core/DBSearchIntersectTest.php index cd9f64356..cd11f97ba 100644 --- a/test/core/DBSearchIntersectTest.php +++ b/test/core/DBSearchIntersectTest.php @@ -17,6 +17,8 @@ use DBSearch; */ class DBSearchIntersectTest extends ItopDataTestCase { + const USE_TRANSACTION = false; + protected function setUp() { parent::setUp(); @@ -132,14 +134,25 @@ class DBSearchIntersectTest extends ItopDataTestCase $oRightSearch = DBSearch::FromOQL($sRightSelect); $oResultSearch = $oLeftSearch->Intersect($oRightSearch); + $sOQLResult = $oResultSearch->ToOQL(); CMDBSource::TestQuery($oResultSearch->MakeSelectQuery()); - $this->assertEquals($sResult, $oResultSearch->ToOQL()); + $this->assertEquals($sResult, $sOQLResult); } public function IntersectProvider() { $aTests = array(); + $aTests['Nested selects 2'] = array( + 'left' => "SELECT `U` FROM UserRequest AS `U` WHERE U.agent_id = 3", + 'right' => "SELECT `UserRequest` FROM UserRequest AS `UserRequest` JOIN Person AS `P` ON `UserRequest`.agent_id = `P`.id JOIN Organization AS `Organization` ON `P`.org_id = `Organization`.id WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` WHERE (`Organization`.`id` = `UserRequest`.`org_id`)))", + 'result' => "SELECT `U` FROM UserRequest AS `U` JOIN Person AS `P` ON `U`.agent_id = `P`.id JOIN Organization AS `Organization` ON `P`.org_id = `Organization`.id WHERE ((`U`.`agent_id` = 3) AND (`U`.`org_id` IN (SELECT `Organization1` FROM Organization AS `Organization1` WHERE (`Organization1`.`id` = `U`.`org_id`))))"); + + $aTests['Nested selects'] = array( + 'left' => "SELECT `UserRequest` FROM UserRequest AS `UserRequest` JOIN Person AS `P` ON `UserRequest`.agent_id = `P`.id JOIN Organization AS `Organization` ON `P`.org_id = `Organization`.id WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` WHERE (`Organization`.`id` = `UserRequest`.`org_id`)))", + 'right' => "SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE UserRequest.agent_id = 3", + 'result' => "SELECT `UserRequest` FROM UserRequest AS `UserRequest` JOIN Person AS `P` ON `UserRequest`.agent_id = `P`.id JOIN Organization AS `Organization` ON `P`.org_id = `Organization`.id WHERE ((`UserRequest`.`org_id` IN (SELECT `Organization1` FROM Organization AS `Organization1` WHERE (`Organization1`.`id` = `UserRequest`.`org_id`))) AND (`UserRequest`.`agent_id` = 3))"); + $aTests['Multiple selected classes inverted'] = array( 'left' => "SELECT `L`, `P` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id WHERE 1", 'right' => "SELECT Person WHERE org_id = 3", diff --git a/test/core/OQLTest.php b/test/core/OQLTest.php index 027ffc776..511faa319 100644 --- a/test/core/OQLTest.php +++ b/test/core/OQLTest.php @@ -10,6 +10,7 @@ namespace Combodo\iTop\Test\UnitTest\Core; +use CMDBSource; use Combodo\iTop\Test\UnitTest\ItopDataTestCase; use DBObjectSearch; use DBSearch; @@ -390,4 +391,38 @@ class OQLTest extends ItopDataTestCase ); } + /** + * @dataProvider MakeSelectQueryProvider + * @param $sOQL + * @param $sExpectedExceptionClass + */ + public function testMakeSelectQuery($sOQL, $sExpectedExceptionClass = '') + { + $sExceptionClass = ''; + try + { + $oSearch = DBSearch::FromOQL($sOQL); + CMDBSource::TestQuery($oSearch->MakeSelectQuery()); + } + catch (Exception $e) + { + $sExceptionClass = get_class($e); + } + + static::assertEquals($sExpectedExceptionClass, $sExceptionClass); + } + + public function MakeSelectQueryProvider() + { + return array( + array("SELECT `UserRequest` FROM UserRequest AS `UserRequest` JOIN Person AS `P` ON `UserRequest`.agent_id = `P`.id JOIN Organization AS `Organization` ON `P`.org_id = `Organization`.id WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` WHERE (`Organization`.`id` = `Toto`.`org_id`)))", 'OqlNormalizeException'), + array("SELECT `UserRequest` FROM UserRequest AS `UserRequest` JOIN Person AS `P` ON `UserRequest`.agent_id = `P`.id JOIN Organization AS `Organization` ON `P`.org_id = `Organization`.id WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` WHERE (`Organization`.`id` = `UserRequest`.`org_id`)))"), + array("SELECT `U` FROM User AS `U` JOIN Person AS `P` ON `U`.contactid = `P`.id WHERE ((`U`.`status` = 'enabled') AND (`U`.`id` NOT IN (SELECT `U` FROM User AS `U` JOIN Person AS `P` ON `U`.contactid = `P`.id JOIN URP_UserOrg AS `L` ON `L`.userid = `U`.id WHERE ((`U`.`status` = 'enabled') AND (`L`.`allowed_org_id` = `P`.`org_id`)) UNION SELECT `U` FROM User AS `U` WHERE ((`U`.`status` = 'enabled') AND (`U`.`id` NOT IN (SELECT `U` FROM User AS `U` JOIN URP_UserOrg AS `L` ON `L`.userid = `U`.id WHERE (`U`.`status` = 'enabled')))))))"), + array("SELECT `Ur` FROM UserRequest AS `Ur` WHERE (`Ur`.`id` NOT IN (SELECT `Ur` FROM UserRequest AS `Ur` JOIN lnkFunctionalCIToTicket AS `lnk` ON `lnk`.ticket_id = `Ur`.id WHERE 1))"), + array("SELECT `T` FROM Ticket AS `T` WHERE ((`T`.`finalclass` IN ('userrequest', 'change')) AND (`T`.`id` NOT IN (SELECT `Ur` FROM UserRequest AS `Ur` JOIN lnkFunctionalCIToTicket AS `lnk` ON `lnk`.ticket_id = `Ur`.id WHERE 1 UNION SELECT `C` FROM Change AS `C` JOIN lnkFunctionalCIToTicket AS `lnk` ON `lnk`.ticket_id = `C`.id WHERE 1)))"), + array("SELECT `PhysicalDevice` FROM PhysicalDevice AS `PhysicalDevice` WHERE ((`PhysicalDevice`.`status` = 'production') AND (`PhysicalDevice`.`id` NOT IN (SELECT `p` FROM PhysicalDevice AS `p` JOIN lnkFunctionalCIToProviderContract AS `l` ON `l`.functionalci_id = `p`.id WHERE 1)))"), + array("SELECT `U` FROM User AS `U` JOIN Person AS `P` ON `U`.contactid = `P`.id WHERE ((`U`.`status` = 'enabled') AND (`U`.`id` NOT IN (SELECT `U` FROM User AS `U` JOIN Person AS `P` ON `U`.contactid = `P`.id JOIN URP_UserOrg AS `L` ON `L`.userid = `U`.id WHERE ((`U1`.`status` = 'enabled') AND (`L`.`allowed_org_id` = `P`.`org_id`)) UNION SELECT `U` FROM User AS `U` WHERE ((`U`.`status` = 'enabled') AND (`U`.`id` NOT IN (SELECT `U` FROM User AS `U` JOIN URP_UserOrg AS `L` ON `L`.userid = `U`.id WHERE (`U`.`status` = 'enabled')))))))", "OqlNormalizeException"), + ); + } + }