N°1888 - Filter search with another search

This commit is contained in:
Eric
2019-10-23 12:56:02 +02:00
parent 6f6b654dba
commit 5642552f9a
6 changed files with 687 additions and 283 deletions

View File

@@ -873,6 +873,15 @@ class DBObjectSearch extends DBSearch
return $res;
}
/**
* @param \DBObjectSearch $oFilter
* @param string $sExtKeyAttCode
* @param array $aClassAliases
* @param array $aAliasTranslation
* @param int $iOperatorCode
*
* @throws \CoreException
*/
protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
{
// Find the node on which the new tree must be attached (most of the time it is "this")
@@ -883,6 +892,7 @@ class DBObjectSearch extends DBSearch
{
foreach ($oReceivingFilter->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode] as $oExisting)
{
/** @var DBObjectSearch $oExisting */
if ($oExisting->GetClass() == $oFilter->GetClass())
{
$oExisting->MergeWith_InNamespace($oFilter, $oExisting->m_aClasses, $aAliasTranslation);
@@ -953,7 +963,7 @@ class DBObjectSearch extends DBSearch
$this->RecomputeClassList($this->m_aClasses);
}
protected function AddCondition_ReferencedBy_InNameSpace(DBSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
protected function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
{
$sForeignClass = $oFilter->GetClass();
@@ -980,10 +990,115 @@ class DBObjectSearch extends DBSearch
}
}
/**
* Filter this search with another search.
* Initial search is unmodified.
* The difference with Intersect, is that an alias can be provided,
* the filtered class does not need to be the first joined class.
*
* @param string $sClassAlias class being filtered
* @param DBSearch $oFilter Filter to apply
*
* @return DBSearch The filtered search
* @throws \CoreException
*/
public function Filter($sClassAlias, DBSearch $oFilter)
{
// If the conditions are the correct ones for Intersect
if (($this->GetFirstJoinedClassAlias() == $sClassAlias))
{
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");
}
$oFilteredSearch->AddConditionExpression($oFilterExpression);
return $oFilteredSearch;
}
/**
* Filter "in place" the search (filtered part is replaced in the initial search)
*
* @param DBObjectSearch $oSearch Search to filter, modified with the given filter
* @param string $sClassAlias class to filter
* @param \DBSearch $oFilter Filter to apply
*
* @return \Expression|false
* @throws \CoreException
*/
private static function FilterSubClass(DBObjectSearch &$oSearch, $sClassAlias, DBSearch $oFilter, $aRootClasses)
{
if (($oSearch->GetFirstJoinedClassAlias() == $sClassAlias))
{
$oSearch->ResetCondition();
$oSearch = $oSearch->IntersectSubClass($oFilter, $aRootClasses);
return $oSearch->GetCriteria();
}
/** @var Expression $oFilterExpression */
// Search in the filter tree where is the correct DBSearch
foreach ($oSearch->m_aPointingTo as $sExtKey => $aPointingTo)
{
foreach ($aPointingTo as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $index => $oExtFilter)
{
$oFilterExpression = self::FilterSubClass($oExtFilter, $sClassAlias, $oFilter, $aRootClasses);
if ($oFilterExpression !== false)
{
$oSearch->m_aPointingTo[$sExtKey][$iOperatorCode][$index] = $oExtFilter;
return $oFilterExpression;
}
}
}
}
foreach($oSearch->m_aReferencedBy as $sForeignClass => $aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $index => $oForeignFilter)
{
$oFilterExpression = self::FilterSubClass($oForeignFilter, $sClassAlias, $oFilter, $aRootClasses);
if ($oFilterExpression !== false)
{
$oSearch->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode][$iOperatorCode][$index] = $oForeignFilter;
return $oFilterExpression;
}
}
}
}
}
return false;
}
/**
* @inheritDoc
* @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)
{
@@ -999,15 +1114,12 @@ class DBObjectSearch extends DBSearch
foreach ($aFilters as $oRightFilter)
{
// Limitation: the queried class must be the first declared class
if ($this->GetFirstJoinedClassAlias() != $this->GetClassAlias())
{
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$this->GetClass()} AS {$this->GetClassAlias()}) is not the first joined class ({$this->GetFirstJoinedClass()} AS {$this->GetFirstJoinedClassAlias()})");
}
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();
@@ -1017,14 +1129,14 @@ class DBObjectSearch extends DBSearch
$oLeftFilter->AllowAllData();
}
if ($oLeftFilter->GetClass() != $oRightFilter->GetClass())
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass())
{
if (MetaModel::IsParentClass($oLeftFilter->GetClass(), $oRightFilter->GetClass()))
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass()))
{
// Specialize $oLeftFilter
$oLeftFilter->ChangeClass($oRightFilter->GetClass());
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
}
elseif (MetaModel::IsParentClass($oRightFilter->GetClass(), $oLeftFilter->GetClass()))
elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass()))
{
// Specialize $oRightFilter
$oRightFilter->ChangeClass($oLeftFilter->GetClass());
@@ -1036,7 +1148,7 @@ class DBObjectSearch extends DBSearch
}
$aAliasTranslation = array();
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $oLeftFilter->m_aClasses, $aAliasTranslation);
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
$aSearches[] = $oLeftFilter;
}
@@ -1051,15 +1163,22 @@ class DBObjectSearch extends DBSearch
}
}
/**
* @param DBObjectSearch $oFilter
* @param array $aClassAliases
* @param array $aAliasTranslation
*
* @throws CoreException
*/
protected function MergeWith_InNamespace($oFilter, &$aClassAliases, &$aAliasTranslation)
{
if ($this->GetClass() != $oFilter->GetClass())
if ($this->GetFirstJoinedClass() != $oFilter->GetClass())
{
throw new CoreException("Attempting to merge a filter of class '{$this->GetClass()}' with a filter of class '{$oFilter->GetClass()}'");
throw new CoreException("Attempting to merge a filter of class '{$this->GetFirstJoinedClass()}' with a filter of class '{$oFilter->GetClass()}'");
}
// Translate search condition into our aliasing scheme
$aAliasTranslation[$oFilter->GetClassAlias()]['*'] = $this->GetClassAlias();
$aAliasTranslation[$oFilter->GetClassAlias()]['*'] = $this->GetFirstJoinedClassAlias();
foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
{

View File

@@ -433,11 +433,27 @@ abstract class DBSearch
*/
abstract public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null);
/**
/**
* Filter this search with another search.
* Initial search is unmodified.
* The difference with Intersect, is that an alias can be provided,
* the filtered class does not need to be the first joined class,
* it can be any class of the search.
*
* @param string $sClassAlias class being filtered
* @param DBSearch $oFilter Filter to apply
*
* @return DBSearch The filtered search
* @throws \CoreException
*/
abstract public function Filter($sClassAlias, DBSearch $oFilter);
/**
* Filter the result
*
* The filter is performed by returning only the values in common with the given $oFilter
* The impact on the resulting query performance/viability can be significant.
* Only the first joined class can be filtered.
*
* @internal
*
@@ -1087,7 +1103,7 @@ abstract class DBSearch
$oSearch = $this;
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
{
foreach ($this->GetSelectedClasses() as $sClass)
foreach ($this->GetSelectedClasses() as $sClassAlias => $sClass)
{
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false)
@@ -1098,7 +1114,7 @@ abstract class DBSearch
if (is_object($oVisibleObjects))
{
$oVisibleObjects->AllowAllData();
$oSearch = $this->Intersect($oVisibleObjects);
$oSearch = $this->Filter($sClassAlias, $oVisibleObjects);
/** @var DBSearch $oSearch */
$oSearch->SetDataFiltered();
}

View File

@@ -383,6 +383,16 @@ class DBUnionSearch extends DBSearch
}
}
public function Filter($sClassAlias, DBSearch $oFilter)
{
$aSearches = array();
foreach ($this->aSearches as $oSearch)
{
$aSearches[] = $oSearch->Filter($sClassAlias, $oFilter);
}
return new DBUnionSearch($aSearches);
}
public function Intersect(DBSearch $oFilter)
{
$aSearches = array();

View File

@@ -955,7 +955,7 @@ class DBObjectSearch extends DBSearch
$this->RecomputeClassList($this->m_aClasses);
}
protected function AddCondition_ReferencedBy_InNameSpace(DBSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
protected function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
{
$sForeignClass = $oFilter->GetClass();
@@ -982,10 +982,115 @@ class DBObjectSearch extends DBSearch
}
}
/**
* Filter this search with another search.
* Initial search is unmodified.
* The difference with Intersect, is that an alias can be provided,
* the filtered class does not need to be the first joined class.
*
* @param string $sClassAlias class being filtered
* @param DBSearch $oFilter Filter to apply
*
* @return DBSearch The filtered search
* @throws \CoreException
*/
public function Filter($sClassAlias, DBSearch $oFilter)
{
// If the conditions are the correct ones for Intersect
if (($this->GetFirstJoinedClassAlias() == $sClassAlias))
{
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");
}
$oFilteredSearch->AddConditionExpression($oFilterExpression);
return $oFilteredSearch;
}
/**
* Filter "in place" the search (filtered part is replaced in the initial search)
*
* @param DBObjectSearch $oSearch Search to filter, modified with the given filter
* @param string $sClassAlias class to filter
* @param \DBSearch $oFilter Filter to apply
*
* @return \Expression|false
* @throws \CoreException
*/
private static function FilterSubClass(DBObjectSearch &$oSearch, $sClassAlias, DBSearch $oFilter, $aRootClasses)
{
if (($oSearch->GetFirstJoinedClassAlias() == $sClassAlias))
{
$oSearch->ResetCondition();
$oSearch = $oSearch->IntersectSubClass($oFilter, $aRootClasses);
return $oSearch->GetCriteria();
}
/** @var Expression $oFilterExpression */
// Search in the filter tree where is the correct DBSearch
foreach ($oSearch->m_aPointingTo as $sExtKey => $aPointingTo)
{
foreach ($aPointingTo as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $index => $oExtFilter)
{
$oFilterExpression = self::FilterSubClass($oExtFilter, $sClassAlias, $oFilter, $aRootClasses);
if ($oFilterExpression !== false)
{
$oSearch->m_aPointingTo[$sExtKey][$iOperatorCode][$index] = $oExtFilter;
return $oFilterExpression;
}
}
}
}
foreach($oSearch->m_aReferencedBy as $sForeignClass => $aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $index => $oForeignFilter)
{
$oFilterExpression = self::FilterSubClass($oForeignFilter, $sClassAlias, $oFilter, $aRootClasses);
if ($oFilterExpression !== false)
{
$oSearch->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode][$iOperatorCode][$index] = $oForeignFilter;
return $oFilterExpression;
}
}
}
}
}
return false;
}
/**
* @inheritDoc
* @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)
{
@@ -1001,15 +1106,12 @@ class DBObjectSearch extends DBSearch
foreach ($aFilters as $oRightFilter)
{
// Limitation: the queried class must be the first declared class
if ($this->GetFirstJoinedClassAlias() != $this->GetClassAlias())
{
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$this->GetClass()} AS {$this->GetClassAlias()}) is not the first joined class ({$this->GetFirstJoinedClass()} AS {$this->GetFirstJoinedClassAlias()})");
}
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();
@@ -1019,14 +1121,14 @@ class DBObjectSearch extends DBSearch
$oLeftFilter->AllowAllData();
}
if ($oLeftFilter->GetClass() != $oRightFilter->GetClass())
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass())
{
if (MetaModel::IsParentClass($oLeftFilter->GetClass(), $oRightFilter->GetClass()))
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass()))
{
// Specialize $oLeftFilter
$oLeftFilter->ChangeClass($oRightFilter->GetClass());
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
}
elseif (MetaModel::IsParentClass($oRightFilter->GetClass(), $oLeftFilter->GetClass()))
elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass()))
{
// Specialize $oRightFilter
$oRightFilter->ChangeClass($oLeftFilter->GetClass());
@@ -1038,7 +1140,7 @@ class DBObjectSearch extends DBSearch
}
$aAliasTranslation = array();
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $oLeftFilter->m_aClasses, $aAliasTranslation);
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
$aSearches[] = $oLeftFilter;
}

View File

@@ -0,0 +1,414 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Core;
use CMDBSource;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use DBSearch;
/**
* Class DBSearchIntersectTest
*
* @package Combodo\iTop\Test\UnitTest\Core
*
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
class DBSearchIntersectTest extends ItopDataTestCase
{
const CREATE_TEST_ORG = false;
protected function setUp()
{
parent::setUp();
require_once(APPROOT.'application/startup.inc.php');
}
/**
* @dataProvider FilterProvider
*
* @param $sLeftSelect
* @param $sRightSelect
* @param $sClassAlias
* @param $sResult
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testFilter($sLeftSelect, $sRightSelect, $sClassAlias, $sResult)
{
$oLeftSearch = DBSearch::FromOQL($sLeftSelect);
$oRightSearch = DBSearch::FromOQL($sRightSelect);
$oResultSearch = $oLeftSearch->Filter($sClassAlias, $oRightSearch);
CMDBSource::TestQuery($oResultSearch->MakeSelectQuery());
$this->assertEquals($sResult, $oResultSearch->ToOQL());
}
public function FilterProvider()
{
$aTests = array();
$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",
'alias' => "P",
'result' => "SELECT `L`, `P` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id WHERE (`P`.`org_id` = 3)");
$aTests['Multiple selected classes inverted 1'] = array(
'left' => "SELECT `L`, `P`, `D` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id JOIN PC AS D ON D.location_id = L.id JOIN Person AS P2 ON P.manager_id = P2.id WHERE 1",
'right' => "SELECT Location WHERE org_id = 3",
'alias' => "L",
'result' => "SELECT `L`, `P`, `D` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id JOIN PC AS `D` ON `D`.location_id = `L`.id JOIN Person AS `P2` ON `P`.manager_id = `P2`.id WHERE (`L`.`org_id` = 3)");
$aTests['Multiple selected classes inverted 2'] = array(
'left' => "SELECT `L`, `P`, `D` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id JOIN PC AS D ON D.location_id = L.id JOIN Person AS P2 ON P.manager_id = P2.id WHERE (`L`.`org_id` = 3)",
'right' => "SELECT Person WHERE org_id = 3",
'alias' => "P",
'result' => "SELECT `L`, `P`, `D` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id JOIN PC AS `D` ON `D`.location_id = `L`.id JOIN Person AS `P2` ON `P`.manager_id = `P2`.id WHERE ((`L`.`org_id` = 3) AND (`P`.`org_id` = 3))");
$aTests['Same class'] = array(
'left' => "SELECT Contact WHERE name = 'Christie'",
'right' => "SELECT Contact WHERE org_id = 3",
'alias' => "Contact",
'result' => "SELECT `Contact` FROM Contact AS `Contact` WHERE ((`Contact`.`name` = 'Christie') AND (`Contact`.`org_id` = 3))");
$aTests['Different Alias'] = array(
'left' => "SELECT Contact AS C WHERE C.name = 'Christie'",
'right' => "SELECT Contact AS CC WHERE CC.org_id = 3",
'alias' => "C",
'result' => "SELECT `C` FROM Contact AS `C` WHERE ((`C`.`name` = 'Christie') AND (`C`.`org_id` = 3))");
$aTests['Multiple selected classes'] = array(
'left' => "SELECT `L`, `P` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE 1",
'right' => "SELECT Location WHERE org_id = 3",
'alias' => "L",
'result' => "SELECT `L`, `P` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE (`L`.`org_id` = 3)");
$aTests['Joined classes'] = array(
'left' => "SELECT `L` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE 1",
'right' => "SELECT Person WHERE org_id = 3",
'alias' => "P",
'result' => "SELECT `L` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE (`P`.`org_id` = 3)");
$aTests['Joined filter'] = array(
'left' => "SELECT `P` FROM Person AS `P` WHERE 1",
'right' => "SELECT `P` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id WHERE `L`.org_id = 3",
'alias' => "P",
'result' => "SELECT `P` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id WHERE (`L`.`org_id` = 3)");
$aTests['Joined filter on joined classes'] = array(
'left' => "SELECT `L` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE 1",
'right' => "SELECT Person FROM Person AS Person JOIN Location ON Person.location_id = Location.id WHERE Location.org_id = 3",
'alias' => "P",
'result' => "SELECT `L` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id JOIN Location AS `Location` ON `P`.location_id = `Location`.id WHERE (`Location`.`org_id` = 3)");
$aTests['Alias collision'] = array(
'left' => "SELECT `L` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE 1",
'right' => "SELECT Person FROM Person AS Person JOIN Location AS `L` ON Person.location_id = `L`.id WHERE `L`.org_id = 3",
'alias' => "P",
'result' => "SELECT `L` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id JOIN Location AS `L1` ON `P`.location_id = `L1`.id WHERE (`L1`.`org_id` = 3)");
return $aTests;
}
/**
* @dataProvider IntersectProvider
*
* @param $sLeftSelect
* @param $sRightSelect
* @param $sResult
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testIntersect($sLeftSelect, $sRightSelect, $sResult)
{
$oLeftSearch = DBSearch::FromOQL($sLeftSelect);
$oRightSearch = DBSearch::FromOQL($sRightSelect);
$oResultSearch = $oLeftSearch->Intersect($oRightSearch);
CMDBSource::TestQuery($oResultSearch->MakeSelectQuery());
$this->assertEquals($sResult, $oResultSearch->ToOQL());
}
public function IntersectProvider()
{
$aTests = array();
$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",
'result' => "SELECT `L`, `P` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id WHERE (`P`.`org_id` = 3)");
$aTests['Multiple selected classes inverted 2'] = array(
'left' => "SELECT `L`, `P`, `D` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id JOIN PC AS D ON D.location_id = L.id JOIN Person AS P2 ON P.manager_id = P2.id WHERE (`L`.`org_id` = 3)",
'right' => "SELECT Person WHERE org_id = 3",
'result' => "SELECT `L`, `P`, `D` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id JOIN PC AS `D` ON `D`.location_id = `L`.id JOIN Person AS `P2` ON `P`.manager_id = `P2`.id WHERE ((`L`.`org_id` = 3) AND (`P`.`org_id` = 3))");
$aTests['Same class'] = array(
'left' => "SELECT Contact WHERE name = 'Christie'",
'right' => "SELECT Contact WHERE org_id = 3",
'result' => "SELECT `Contact` FROM Contact AS `Contact` WHERE ((`Contact`.`name` = 'Christie') AND (`Contact`.`org_id` = 3))");
$aTests['Different Alias'] = array(
'left' => "SELECT Contact AS C WHERE C.name = 'Christie'",
'right' => "SELECT Contact AS CC WHERE CC.org_id = 3",
'result' => "SELECT `C` FROM Contact AS `C` WHERE ((`C`.`name` = 'Christie') AND (`C`.`org_id` = 3))");
$aTests['Multiple selected classes'] = array(
'left' => "SELECT `L`, `P` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE 1",
'right' => "SELECT Location WHERE org_id = 3",
'result' => "SELECT `L`, `P` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE (`L`.`org_id` = 3)");
$aTests['Alias collision'] = array(
'left' => "SELECT `P` FROM Person AS `P` WHERE 1",
'right' => "SELECT `Person` FROM Person AS `Person` JOIN Person AS `P` ON `P`.manager_id = `Person`.id WHERE `P`.org_id = 3",
'result' => "SELECT `P` FROM Person AS `P` JOIN Person AS `P1` ON `P1`.manager_id = `P`.id WHERE (`P1`.`org_id` = 3)");
return $aTests;
}
/**
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testIntersectNotOptimizedPointingTo()
{
$sBaseQuery = "SELECT l FROM lnkContactToFunctionalCI AS l JOIN Contact AS c ON l.contact_id = c.id";
$sOQL = "SELECT l FROM lnkContactToFunctionalCI AS l JOIN Person AS p ON l.contact_id = p.id";
$sResult = "SELECT `l` FROM lnkContactToFunctionalCI AS `l` JOIN Contact AS `c` ON `l`.contact_id = `c`.id JOIN Person AS `p` ON `l`.contact_id = `p`.id WHERE 1";
$oSearchA = DBSearch::FromOQL($sBaseQuery);
$oSearchB = DBSearch::FromOQL($sOQL);
$oIntersect = $oSearchA->Intersect($oSearchB);
CMDBSource::TestQuery($oIntersect->MakeSelectQuery());
$this->assertEquals($sResult, $oIntersect->ToOQL());
}
/**
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testIntersectNotOptimizedReferencedBy()
{
$sBaseQuery = "SELECT o FROM Organization AS o JOIN Contact AS c ON c.org_id = o.id WHERE c.id = 1";
$sOQL = "SELECT o FROM Organization AS o JOIN Person AS p ON p.org_id = o.id WHERE p.id = 2";
$sResult = "SELECT `o` FROM Organization AS `o` JOIN Contact AS `c` ON `c`.org_id = `o`.id JOIN Person AS `p` ON `p`.org_id = `o`.id WHERE ((`c`.`id` = 1) AND (`p`.`id` = 2))";
$oSearchA = DBSearch::FromOQL($sBaseQuery);
$oSearchB = DBSearch::FromOQL($sOQL);
$oIntersect = $oSearchA->Intersect($oSearchB);
CMDBSource::TestQuery($oIntersect->MakeSelectQuery());
$this->assertEquals($sResult, $oIntersect->ToOQL());
}
/**
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testIntersectOptimizedReferencedBy()
{
$sBaseQuery = "SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = 'some name'";
$sQueryA = "SELECT r FROM UserRequest AS r JOIN Service AS s ON r.service_id = s.id JOIN Organization AS o ON s.org_id = o.id WHERE r.agent_id = 456 AND s.servicefamily_id = 789 AND o.name = 'right_name'";
$sResult = "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id JOIN UserRequest AS `r` ON `r`.service_id = `s`.id WHERE ((`o`.`name` = 'some name') AND (((`r`.`agent_id` = 456) AND (`s`.`servicefamily_id` = 789)) AND (`o`.`name` = 'right_name')))";
$oSearchA = DBSearch::FromOQL($sQueryA);
$oSearchB = DBSearch::FromOQL($sBaseQuery);
$oSearchB->AddCondition_ReferencedBy($oSearchA, 'service_id');
CMDBSource::TestQuery($oSearchB->MakeSelectQuery());
$this->assertEquals($sResult, $oSearchB->ToOQL());
}
/**
* @throws \CoreException
* @throws \CoreWarning
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testIntersectNotOptimizedAddConditionPointingTo()
{
$sBaseQuery = "SELECT Person FROM Person AS Person";
$sQueryA = "SELECT o FROM Organization AS o JOIN Contact AS c ON c.org_id = o.id";
$sResult = "SELECT `Person` FROM Person AS `Person` JOIN Organization AS `o` ON `Person`.org_id = `o`.id JOIN Contact AS `c` ON `c`.org_id = `o`.id WHERE 1";
$oSearchA = DBSearch::FromOQL($sQueryA);
$oSearchB = DBSearch::FromOQL($sBaseQuery);
$oSearchB->AddCondition_PointingTo($oSearchA, 'org_id');
CMDBSource::TestQuery($oSearchB->MakeSelectQuery());
$this->assertEquals($sResult, $oSearchB->ToOQL());
}
/**
* @throws \CoreException
* @throws \CoreWarning
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testIntersectOptimizedAddConditionPointingTo()
{
$sBaseQuery = "SELECT ur FROM UserRequest AS ur JOIN Person AS p ON ur.agent_id = p.id WHERE p.status != 'terminated'";
$sQueryA = "SELECT o FROM Organization AS o JOIN UserRequest AS r ON r.org_id = o.id JOIN Person AS p ON r.caller_id = p.id WHERE o.name LIKE 'Company' AND r.service_id = 123 AND p.employee_number LIKE '007'";
$sResult = "SELECT `ur` FROM UserRequest AS `ur` JOIN Person AS `p` ON `ur`.agent_id = `p`.id JOIN Organization AS `o` ON `ur`.org_id = `o`.id JOIN Person AS `p11` ON `ur`.caller_id = `p11`.id WHERE ((`p`.`status` != 'terminated') AND (((`o`.`name` LIKE 'Company') AND (`ur`.`service_id` = 123)) AND (`p11`.`employee_number` LIKE '007')))";
$oSearchA = DBSearch::FromOQL($sQueryA);
$oSearchB = DBSearch::FromOQL($sBaseQuery);
$oSearchB->AddCondition_PointingTo($oSearchA, 'org_id');
CMDBSource::TestQuery($oSearchB->MakeSelectQuery());
$this->assertEquals($sResult, $oSearchB->ToOQL());
}
/**
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testIntersectNotOptimizedAddConditionReferencedBy()
{
$sBaseQuery = "SELECT Person FROM Person AS Person";
$sQueryA = "SELECT l FROM lnkContactToFunctionalCI AS l JOIN Contact AS c ON l.contact_id = c.id";
$sResult = "SELECT `Person` FROM Person AS `Person` JOIN lnkContactToFunctionalCI AS `l` ON `l`.contact_id = `Person`.id JOIN Contact AS `c` ON `l`.contact_id = `c`.id WHERE 1";
$oSearchA = DBSearch::FromOQL($sQueryA);
$oSearchB = DBSearch::FromOQL($sBaseQuery);
$oSearchB->AddCondition_ReferencedBy($oSearchA, 'contact_id');
CMDBSource::TestQuery($oSearchA->MakeSelectQuery());
$this->assertEquals($sResult, $oSearchB->ToOQL());
}
/**
* @dataProvider IntersectOptimizationProvider
* @doesNotPerformAssertions
*
* @param string $sOQL
* @param string $sResult
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \OQLException
*/
public function testIntersectOptimization($sBaseQuery, $sOQL, $sResult)
{
$oSearchA = DBSearch::FromOQL($sBaseQuery);
$oSearchB = DBSearch::FromOQL($sOQL);
$oIntersect = $oSearchA->Intersect($oSearchB);
CMDBSource::TestQuery($oIntersect->MakeSelectQuery());
$this->assertEquals($sResult, $oIntersect->ToOQL());
}
public function IntersectOptimizationProvider()
{
$aQueries = array(
'Exact same query' => array(
'Base Query' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Filter OQL' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Result ' => "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id WHERE ((`o`.`name` = 'The World Company') AND (`o`.`name` = 'The World Company'))",
),
'Same query, other aliases' => array(
'Base Query' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Filter OQL' => 'SELECT s2 FROM Service AS s2 JOIN Organization AS o2 ON s2.org_id = o2.id WHERE o2.name = "The World Company"',
'Result ' => "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id WHERE ((`o`.`name` = 'The World Company') AND (`o`.`name` = 'The World Company'))",
),
'Same aliases, different condition' => array(
'Base Query' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Filter OQL' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.parent_id = 0',
'Result ' => "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id WHERE ((`o`.`name` = 'The World Company') AND (`o`.`parent_id` = 0))",
),
'Other aliases, different condition' => array(
'Base Query' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Filter OQL' => 'SELECT s2 FROM Service AS s2 JOIN Organization AS o2 ON s2.org_id = o2.id WHERE o2.parent_id = 0',
'Result ' => "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id WHERE ((`o`.`name` = 'The World Company') AND (`o`.`parent_id` = 0))",
),
'Same aliases, simpler query tree' => array(
'Base Query' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Filter OQL' => 'SELECT s FROM Service AS s WHERE name LIKE "Save the World"',
'Result ' => "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id WHERE ((`o`.`name` = 'The World Company') AND (`s`.`name` LIKE 'Save the World'))",
),
'Other aliases, simpler query tree' => array(
'Base Query' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Filter OQL' => 'SELECT s2 FROM Service AS s2 WHERE name LIKE "Save the World"',
'Result ' => "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id WHERE ((`o`.`name` = 'The World Company') AND (`s`.`name` LIKE 'Save the World'))",
),
'Same aliases, different query tree' => array(
'Base Query' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Filter OQL' => 'SELECT s FROM Service AS s JOIN ServiceFamily AS f ON s.servicefamily_id = f.id WHERE s.org_id = 123 AND f.name = "Care"',
'Result ' => "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id JOIN ServiceFamily AS `f` ON `s`.servicefamily_id = `f`.id WHERE ((`o`.`name` = 'The World Company') AND ((`s`.`org_id` = 123) AND (`f`.`name` = 'Care')))",
),
'Other aliases, different query tree' => array(
'Base Query' => 'SELECT s FROM Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
'Filter OQL' => 'SELECT s2 FROM Service AS s2 JOIN ServiceFamily AS f ON s2.servicefamily_id = f.id WHERE s2.org_id = 123 AND f.name = "Care"',
'Result ' => "SELECT `s` FROM Service AS `s` JOIN Organization AS `o` ON `s`.org_id = `o`.id JOIN ServiceFamily AS `f` ON `s`.servicefamily_id = `f`.id WHERE ((`o`.`name` = 'The World Company') AND ((`s`.`org_id` = 123) AND (`f`.`name` = 'Care')))",
),
'2 - Exact same query' => array(
'Base Query' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Filter OQL' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Service AS `s` ON `s`.org_id = `o`.id WHERE ((`s`.`name` = 'Help') AND (`s`.`name` = 'Help'))",
),
'2 - Same query, other aliases' => array(
'Base Query' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Filter OQL' => 'SELECT o2 FROM Organization AS o2 JOIN Service AS s2 ON s2.org_id = o2.id WHERE s2.name = "Help"',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Service AS `s` ON `s`.org_id = `o`.id WHERE ((`s`.`name` = 'Help') AND (`s`.`name` = 'Help'))",
),
'2 - Same aliases, different condition' => array(
'Base Query' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Filter OQL' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.servicefamily_id = 321',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Service AS `s` ON `s`.org_id = `o`.id WHERE ((`s`.`name` = 'Help') AND (`s`.`servicefamily_id` = 321))",
),
'2 - Other aliases, different condition' => array(
'Base Query' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Filter OQL' => 'SELECT o2 FROM Organization AS o2 JOIN Service AS s2 ON s2.org_id = o2.id WHERE s2.servicefamily_id = 321',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Service AS `s` ON `s`.org_id = `o`.id WHERE ((`s`.`name` = 'Help') AND (`s`.`servicefamily_id` = 321))",
),
'2 - Same aliases, simpler query tree' => array(
'Base Query' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Filter OQL' => 'SELECT o FROM Organization AS o WHERE o.name = "Demo"',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Service AS `s` ON `s`.org_id = `o`.id WHERE ((`s`.`name` = 'Help') AND (`o`.`name` = 'Demo'))",
),
'2 - Other aliases, simpler query tree' => array(
'Base Query' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Filter OQL' => 'SELECT o2 FROM Organization AS o2 WHERE o2.name = "Demo"',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Service AS `s` ON `s`.org_id = `o`.id WHERE ((`s`.`name` = 'Help') AND (`o`.`name` = 'Demo'))",
),
'2 - Same aliases, different query tree' => array(
'Base Query' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Filter OQL' => 'SELECT o FROM Organization AS o JOIN Location AS l ON l.org_id = o.id WHERE l.name = "Paris"',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Service AS `s` ON `s`.org_id = `o`.id JOIN Location AS `l` ON `l`.org_id = `o`.id WHERE ((`s`.`name` = 'Help') AND (`l`.`name` = 'Paris'))",
),
'2 - Other aliases, different query tree' => array(
'Base Query' => 'SELECT o FROM Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
'Filter OQL' => 'SELECT o2 FROM Organization AS o2 JOIN Location AS l ON l.org_id = o2.id WHERE l.name = "Paris"',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Service AS `s` ON `s`.org_id = `o`.id JOIN Location AS `l` ON `l`.org_id = `o`.id WHERE ((`s`.`name` = 'Help') AND (`l`.`name` = 'Paris'))",
),
'Internal query optimizations 1' => array(
'Base Query' => 'SELECT o FROM Organization AS o',
'Filter OQL' => 'SELECT o FROM Organization AS o JOIN Location AS l ON l.org_id = o.id JOIN Organization AS p ON o.parent_id = p.id WHERE l.name = "Paris" AND p.code LIKE "toto"',
'Result ' => "SELECT `o` FROM Organization AS `o` JOIN Organization AS `p` ON `o`.parent_id = `p`.id JOIN Location AS `l` ON `l`.org_id = `o`.id WHERE ((`l`.`name` = 'Paris') AND (`p`.`code` LIKE 'toto'))",
),
'Internal query optimizations 2' => array(
'Base Query' => 'SELECT r FROM UserRequest AS r JOIN Service AS s ON r.service_id = s.id JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "left_name"',
'Filter OQL' => 'SELECT r FROM UserRequest AS r JOIN Service AS s ON r.service_id = s.id JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "right_name"',
'Result ' => "SELECT `r` FROM UserRequest AS `r` JOIN Service AS `s` ON `r`.service_id = `s`.id JOIN Organization AS `o` ON `s`.org_id = `o`.id WHERE ((`o`.`name` = 'left_name') AND (`o`.`name` = 'right_name'))",
),
);
return $aQueries;
}
}

View File

@@ -4712,199 +4712,6 @@ class TestExecActions extends TestBizModel
}
}
class TestIntersectOptimization extends TestBizModel
{
static public function GetName()
{
return 'Internal query optimizations (pointing to)';
}
static public function GetDescription()
{
return 'Clever optimization required for the portal to work fine (expected improvement: query never finishing... to an almost instantaneous query!';
}
protected function DoExecute()
{
$sBaseQuery = 'SELECT Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"';
$aQueries = array(
// Exact same query
'SELECT Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "The World Company"',
// Same query, other aliases
'SELECT Service AS s2 JOIN Organization AS o2 ON s2.org_id = o2.id WHERE o2.name = "The World Company"',
// Same aliases, different condition
'SELECT Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.parent_id = 0',
// Other aliases, different condition
'SELECT Service AS s2 JOIN Organization AS o2 ON s2.org_id = o2.id WHERE o2.parent_id = 0',
// Same aliases, simpler query tree
'SELECT Service AS s WHERE name LIKE "Save the World"',
// Other aliases, simpler query tree
'SELECT Service AS s2 WHERE name LIKE "Save the World"',
// Same aliases, different query tree
'SELECT Service AS s JOIN ServiceFamily AS f ON s.servicefamily_id = f.id WHERE s.org_id = 123 AND f.name = "Care"',
// Other aliases, different query tree
'SELECT Service AS s2 JOIN ServiceFamily AS f ON s2.servicefamily_id = f.id WHERE s2.org_id = 123 AND f.name = "Care"',
);
echo "<h4>Base query: ".htmlentities($sBaseQuery, ENT_QUOTES, 'UTF-8')."</h4>\n";
foreach ($aQueries as $sOQL)
{
echo "<h5>Checking: ".htmlentities($sOQL, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sBaseQuery);
$oSearchB = DBSearch::FromOQL($sOQL);
$oIntersect = $oSearchA->Intersect($oSearchB);
echo "<p>Intersect: ".htmlentities($oIntersect->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oIntersect->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
}
}
}
class TestIntersectOptimization2 extends TestBizModel
{
static public function GetName()
{
return 'Internal query optimizations (referenced by)';
}
static public function GetDescription()
{
return 'Clever optimization required for the portal to work fine (expected improvement: query never finishing... to an almost instantaneous query!';
}
protected function DoExecute()
{
$sBaseQuery = 'SELECT Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"';
$aQueries = array(
// Exact same query
'SELECT Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.name = "Help"',
// Same query, other aliases
'SELECT Organization AS o2 JOIN Service AS s2 ON s2.org_id = o2.id WHERE s2.name = "Help"',
// Same aliases, different condition
'SELECT Organization AS o JOIN Service AS s ON s.org_id = o.id WHERE s.servicefamily_id = 321',
// Other aliases, different condition
'SELECT Organization AS o2 JOIN Service AS s2 ON s2.org_id = o2.id WHERE s2.servicefamily_id = 321',
// Same aliases, simpler query tree
'SELECT Organization AS o WHERE o.name = "Demo"',
// Other aliases, simpler query tree
'SELECT Organization AS o2 WHERE o2.name = "Demo"',
// Same aliases, different query tree
'SELECT Organization AS o JOIN Location AS l ON l.org_id = o.id WHERE l.name = "Paris"',
// Other aliases, different query tree
'SELECT Organization AS o2 JOIN Location AS l ON l.org_id = o2.id WHERE l.name = "Paris"',
);
echo "<h4>Base query: ".htmlentities($sBaseQuery, ENT_QUOTES, 'UTF-8')."</h4>\n";
foreach ($aQueries as $sOQL)
{
echo "<h5>Checking: ".htmlentities($sOQL, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sBaseQuery);
$oSearchB = DBSearch::FromOQL($sOQL);
$oIntersect = $oSearchA->Intersect($oSearchB);
echo "<p>Intersect: ".htmlentities($oIntersect->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oIntersect->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
}
}
}
class TestIntersectOptimization3 extends TestBizModel
{
static public function GetName()
{
return 'Internal query optimizations (mix)';
}
static public function GetDescription()
{
return 'Clever optimization required for the portal to work fine (expected improvement: query never finishing... to an almost instantaneous query!';
}
protected function DoExecute()
{
$aQueries = array(
array(
'SELECT Organization AS o',
'SELECT Organization AS o JOIN Location AS l ON l.org_id = o.id JOIN Organization AS p ON o.parent_id = p.id WHERE l.name = "Paris" AND p.code LIKE "toto"',
),
array(
'SELECT UserRequest AS r JOIN Service AS s ON r.service_id = s.id JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "left_name"',
'SELECT UserRequest AS r JOIN Service AS s ON r.service_id = s.id JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "right_name"',
),
);
echo "<h4>Mixing....</h4>\n";
foreach ($aQueries as $aQ)
{
$sBaseQuery = $aQ[0];
$sOQL = $aQ[1];
echo "<h5>Left: ".htmlentities($sBaseQuery, ENT_QUOTES, 'UTF-8')."</h5>\n";
echo "<h5>Right: ".htmlentities($sOQL, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sBaseQuery);
$oSearchB = DBSearch::FromOQL($sOQL);
$oIntersect = $oSearchA->Intersect($oSearchB);
echo "<p>Intersect: ".htmlentities($oIntersect->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oIntersect->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
}
}
}
class TestIntersectOptimization4 extends TestBizModel
{
static public function GetName()
{
return 'Internal query optimizations (Folding on Join/ReferencedBy)';
}
static public function GetDescription()
{
return 'Clever optimization required for the portal to work fine (expected improvement: query never finishing... to an almost instantaneous query!';
}
protected function DoExecute()
{
echo "<h4>Here we are (conluding a long series of tests)</h4>\n";
$sQueryA = 'SELECT UserRequest AS r JOIN Service AS s ON r.service_id = s.id JOIN Organization AS o ON s.org_id = o.id WHERE r.agent_id = 456 AND s.servicefamily_id = 789 AND o.name = "right_name"';
$sQueryB = 'SELECT Service AS s JOIN Organization AS o ON s.org_id = o.id WHERE o.name = "some name"';
echo "<h5>A: ".htmlentities($sQueryA, ENT_QUOTES, 'UTF-8')."</h5>\n";
echo "<h5>B: ".htmlentities($sQueryB, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sQueryA);
$oSearchB = DBSearch::FromOQL($sQueryB);
$oSearchB->AddCondition_ReferencedBy($oSearchA, 'service_id');
echo "<p>Referenced by...: ".htmlentities($oSearchB->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oSearchB->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
}
}
class TestIntersectOptimization5 extends TestBizModel
{
static public function GetName()
{
return 'Internal query optimizations (Folding on Join/PointingTo)';
}
static public function GetDescription()
{
return 'Clever optimization required for the portal to work fine (expected improvement: query never finishing... to an almost instantaneous query!';
}
protected function DoExecute()
{
echo "<h4>Here we are (concluding a long series of tests)</h4>\n";
$sQueryA = 'SELECT Organization AS o JOIN UserRequest AS r ON r.org_id = o.id JOIN Person AS p ON r.caller_id = p.id WHERE o.name LIKE "Company" AND r.service_id = 123 AND p.employee_number LIKE "007"';
$sQueryB = 'SELECT UserRequest AS ur JOIN Person AS p ON ur.agent_id = p.id WHERE p.status != "terminated"';
echo "<h5>A: ".htmlentities($sQueryA, ENT_QUOTES, 'UTF-8')."</h5>\n";
echo "<h5>B: ".htmlentities($sQueryB, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sQueryA);
$oSearchB = DBSearch::FromOQL($sQueryB);
$oSearchB->AddCondition_PointingTo($oSearchA, 'org_id');
echo "<p>Pointing to...: ".htmlentities($oSearchB->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oSearchB->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
}
}
class TestParsingOptimization extends TestBizModel
{
static public function GetName()
@@ -5036,70 +4843,6 @@ class TestImplicitAlias extends TestBizModel
}
}
class TestIntersectNotOptimized extends TestBizModel
{
static public function GetName()
{
return 'Internal query NOT optimized';
}
static public function GetDescription()
{
return '(N.718) Sometimes, the optimization CANNOT be performed because merging two different classes (same branch) is not implemented';
}
protected function DoExecute()
{
echo "<h4>Intersect NOT optimized on 'pointing to'</h4>\n";
$sBaseQuery = 'SELECT lnkContactToFunctionalCI AS l JOIN Contact AS c ON l.contact_id = c.id';
$sOQL = 'SELECT lnkContactToFunctionalCI AS l JOIN Person AS p ON l.contact_id = p.id';
echo "<h5>Left: ".htmlentities($sBaseQuery, ENT_QUOTES, 'UTF-8')."</h5>\n";
echo "<h5>Right: ".htmlentities($sOQL, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sBaseQuery);
$oSearchB = DBSearch::FromOQL($sOQL);
$oIntersect = $oSearchA->Intersect($oSearchB);
echo "<p>Intersect: ".htmlentities($oIntersect->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oIntersect->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
echo "<h4>Intersect NOT optimized on 'referenced by'</h4>\n";
$sBaseQuery = 'SELECT Organization AS o JOIN Contact AS c ON c.org_id = o.id WHERE c.id = 1';
$sOQL = 'SELECT Organization AS o JOIN Person AS p ON p.org_id = o.id WHERE p.id = 2';
echo "<h5>Left: ".htmlentities($sBaseQuery, ENT_QUOTES, 'UTF-8')."</h5>\n";
echo "<h5>Right: ".htmlentities($sOQL, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sBaseQuery);
$oSearchB = DBSearch::FromOQL($sOQL);
$oIntersect = $oSearchA->Intersect($oSearchB);
echo "<p>Intersect: ".htmlentities($oIntersect->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oIntersect->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
echo "<h4>NOT Folding on AddCondition_PointingTo</h4>\n";
$sQueryA = 'SELECT Organization AS o JOIN Contact AS c ON c.org_id = o.id';
$sQueryB = 'SELECT Person';
echo "<h5>A: ".htmlentities($sQueryA, ENT_QUOTES, 'UTF-8')."</h5>\n";
echo "<h5>B: ".htmlentities($sQueryB, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sQueryA);
$oSearchB = DBSearch::FromOQL($sQueryB);
$oSearchB->AddCondition_PointingTo($oSearchA, 'org_id');
echo "<p>Pointing to...: ".htmlentities($oSearchB->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oSearchB->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
echo "<h4>NOT Folding on AddCondition_ReferencedBy</h4>\n";
$sQueryA = 'SELECT lnkContactToFunctionalCI AS l JOIN Contact AS c ON l.contact_id = c.id';
$sQueryB = 'SELECT Person';
echo "<h5>A: ".htmlentities($sQueryA, ENT_QUOTES, 'UTF-8')."</h5>\n";
echo "<h5>B: ".htmlentities($sQueryB, ENT_QUOTES, 'UTF-8')."</h5>\n";
$oSearchA = DBSearch::FromOQL($sQueryA);
$oSearchB = DBSearch::FromOQL($sQueryB);
$oSearchB->AddCondition_ReferencedBy($oSearchA, 'contact_id');
echo "<p>Referenced by...: ".htmlentities($oSearchA->ToOQL(), ENT_QUOTES, 'UTF-8')."</p>\n";
CMDBSource::TestQuery($oSearchA->MakeSelectQuery());
echo "<p>Successfully tested the SQL query.</p>\n";
}
}
class TestBug609 extends TestBizModel
{
static public function GetName()