mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 02:28:44 +02:00
N°2272 - OQL performance (OQL class tree optimizer)
This commit is contained in:
@@ -42,24 +42,24 @@ class OQLClassNode
|
||||
}
|
||||
|
||||
|
||||
public function AddInnerJoin($oOQLClassNode, $sLeftField, $sRightField)
|
||||
public function AddInnerJoin($oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true)
|
||||
{
|
||||
$this->AddJoin(OQLJoin::JOIN_INNER, $oOQLClassNode, $sLeftField, $sRightField);
|
||||
$this->AddJoin(OQLJoin::JOIN_INNER, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound);
|
||||
}
|
||||
|
||||
public function AddLeftJoin($oOQLClassNode, $sLeftField, $sRightField)
|
||||
public function AddLeftJoin($oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true)
|
||||
{
|
||||
$this->AddJoin(OQLJoin::JOIN_LEFT, $oOQLClassNode, $sLeftField, $sRightField);
|
||||
$this->AddJoin(OQLJoin::JOIN_LEFT, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound);
|
||||
}
|
||||
|
||||
public function AddInnerJoinTree($oOQLClassNode, $sLeftField, $sRightField, $iOperatorCode = TREE_OPERATOR_BELOW, $bInvertOnClause = false)
|
||||
public function AddInnerJoinTree($oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true, $iOperatorCode = TREE_OPERATOR_BELOW, $bInvertOnClause = false)
|
||||
{
|
||||
$this->AddJoin(OQLJoin::JOIN_INNER_TREE, $oOQLClassNode, $sLeftField, $sRightField, $iOperatorCode, $bInvertOnClause);
|
||||
$this->AddJoin(OQLJoin::JOIN_INNER_TREE, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound, $iOperatorCode, $bInvertOnClause);
|
||||
}
|
||||
|
||||
private function AddJoin($sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $sTreeOperator = null, $bInvertOnClause = false)
|
||||
private function AddJoin($sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true, $sTreeOperator = null, $bInvertOnClause = false)
|
||||
{
|
||||
$oOQLJoin = new OQLJoin($sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $sTreeOperator, $bInvertOnClause);
|
||||
$oOQLJoin = new OQLJoin($sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound, $sTreeOperator, $bInvertOnClause);
|
||||
$this->aJoins[] = $oOQLJoin;
|
||||
return $oOQLJoin;
|
||||
}
|
||||
@@ -110,6 +110,11 @@ class OQLClassNode
|
||||
return $this->aJoins;
|
||||
}
|
||||
|
||||
public function RemoveJoin($index)
|
||||
{
|
||||
unset($this->aJoins[$index]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OQLJoin
|
||||
@@ -121,6 +126,8 @@ class OQLJoin
|
||||
private $sJoinType;
|
||||
/** @var \OQLClassNode */
|
||||
private $oOQLClassNode;
|
||||
|
||||
private $bOutbound;
|
||||
private $sLeftField;
|
||||
private $sRightField;
|
||||
private $sTreeOperator;
|
||||
@@ -133,10 +140,11 @@ class OQLJoin
|
||||
* @param $oOQLClassNode
|
||||
* @param $sLeftField
|
||||
* @param $sRightField
|
||||
* @param bool $bOutbound
|
||||
* @param string $sTreeOperator
|
||||
* @param bool $bInvertOnClause
|
||||
*/
|
||||
public function __construct($sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $sTreeOperator = null, $bInvertOnClause = false)
|
||||
public function __construct($sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true, $sTreeOperator = null, $bInvertOnClause = false)
|
||||
{
|
||||
$this->sJoinType = $sJoinType;
|
||||
$this->oOQLClassNode = $oOQLClassNode;
|
||||
@@ -144,6 +152,7 @@ class OQLJoin
|
||||
$this->sRightField = $sRightField;
|
||||
$this->sTreeOperator = $sTreeOperator;
|
||||
$this->bInvertOnClause = $bInvertOnClause;
|
||||
$this->bOutbound = $bOutbound;
|
||||
}
|
||||
|
||||
public function RenderDebug($sClassAlias, $sPrefix = " ")
|
||||
@@ -161,4 +170,20 @@ class OQLJoin
|
||||
return $sOQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \OQLClassNode
|
||||
*/
|
||||
public function GetOOQLClassNode()
|
||||
{
|
||||
return $this->oOQLClassNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function IsOutbound()
|
||||
{
|
||||
return $this->bOutbound;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -275,11 +275,11 @@ class OQLClassTreeBuilder
|
||||
|
||||
if ($oKeyAttDef->IsNullAllowed())
|
||||
{
|
||||
$this->oOQLClassNode->AddLeftJoin($oSelectExtKey, $sKeyAttCode, $sExternalKeyField);
|
||||
$this->oOQLClassNode->AddLeftJoin($oSelectExtKey, $sKeyAttCode, $sExternalKeyField, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->oOQLClassNode->AddInnerJoin($oSelectExtKey, $sKeyAttCode, $sExternalKeyField);
|
||||
$this->oOQLClassNode->AddInnerJoin($oSelectExtKey, $sKeyAttCode, $sExternalKeyField, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,7 +295,7 @@ class OQLClassTreeBuilder
|
||||
//$sExternalKeyTable = $oJoinExpr->GetParent();
|
||||
$sExternalKeyField = $oJoinExpr->GetName();
|
||||
|
||||
$this->oOQLClassNode->AddInnerJoinTree($oSelectExtKey, $sKeyAttCode, $sExternalKeyField, $iOperatorCode);
|
||||
$this->oOQLClassNode->AddInnerJoinTree($oSelectExtKey, $sKeyAttCode, $sExternalKeyField, true, $iOperatorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,12 +343,12 @@ class OQLClassTreeBuilder
|
||||
|
||||
if ($iOperatorCode == TREE_OPERATOR_EQUALS)
|
||||
{
|
||||
$this->oOQLClassNode->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn);
|
||||
$this->oOQLClassNode->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hierarchical key
|
||||
$this->oOQLClassNode->AddInnerJoinTree($oSelectForeign, $sKeyField, $sForeignKeyColumn, $iOperatorCode, true);
|
||||
$this->oOQLClassNode->AddInnerJoinTree($oSelectForeign, $sKeyField, $sForeignKeyColumn, false, $iOperatorCode, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,7 +371,7 @@ class OQLClassTreeBuilder
|
||||
$oSubClassFilter = new DBObjectSearch($sSubClass, $sSubClassAlias);
|
||||
$oOQLClassTreeBuilder = new OQLClassTreeBuilder($oSubClassFilter, $this->oBuild);
|
||||
$oSelectFN = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
||||
$this->oOQLClassNode->AddLeftJoin($oSelectFN, $sKeyField, MetaModel::DBGetKey($sSubClass));
|
||||
$this->oOQLClassNode->AddLeftJoin($oSelectFN, $sKeyField, MetaModel::DBGetKey($sSubClass), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
core/oqlclasstreeoptimizer.class.inc.php
Normal file
68
core/oqlclasstreeoptimizer.class.inc.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class OQLClassTreeOptimizer
|
||||
{
|
||||
/** @var OQLClassNode */
|
||||
private $oOQLClassNode;
|
||||
/** @var QueryBuilderContext */
|
||||
private $oBuild;
|
||||
|
||||
/**
|
||||
* OQLClassTreeOptimizer constructor.
|
||||
*
|
||||
* @param OQLClassNode $oOQLClassNode
|
||||
* @param QueryBuilderContext $oBuild
|
||||
*/
|
||||
public function __construct($oOQLClassNode, $oBuild)
|
||||
{
|
||||
$this->oOQLClassNode = $oOQLClassNode;
|
||||
$this->oBuild = $oBuild;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune the unnecessary joins
|
||||
*/
|
||||
public function OptimizeClassTree()
|
||||
{
|
||||
$this->PruneJoins($this->oOQLClassNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OQLClassNode $oCurrentClassNode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function PruneJoins($oCurrentClassNode)
|
||||
{
|
||||
$aExpectedAttributes = $this->oBuild->m_oQBExpressions->GetUnresolvedFields($oCurrentClassNode->GetClassAlias());
|
||||
$bCanBeRemoved = empty($aExpectedAttributes);
|
||||
|
||||
foreach ($oCurrentClassNode->GetJoins() as $index => $oJoin)
|
||||
{
|
||||
if ($this->PruneJoins($oJoin->GetOOQLClassNode()))
|
||||
{
|
||||
if ($oJoin->IsOutbound())
|
||||
{
|
||||
// The join is not used, remove from tree
|
||||
$oCurrentClassNode->RemoveJoin($index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inbound joins cannot be removed
|
||||
$bCanBeRemoved = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This join is used, so the current node cannot be removed
|
||||
$bCanBeRemoved = false;
|
||||
}
|
||||
}
|
||||
return $bCanBeRemoved;
|
||||
}
|
||||
}
|
||||
@@ -53,11 +53,6 @@ class SQLObjectQueryBuilder
|
||||
}
|
||||
}
|
||||
|
||||
$oBuild = new QueryBuilderContext($this->oDBObjetSearch, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr, $aAttToLoad);
|
||||
$oOQLClassTreeBuilder = new OQLClassTreeBuilder($this->oDBObjetSearch, $oBuild);
|
||||
$oOQLQuery = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
||||
|
||||
|
||||
$oBuild = new QueryBuilderContext($this->oDBObjetSearch, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr, $aAttToLoad);
|
||||
$oSQLQuery = $this->MakeSQLObjectQueryRoot($oBuild, $aAttToLoad, array(), $aGroupByExpr, $aSelectExpr);
|
||||
|
||||
@@ -109,6 +104,15 @@ class SQLObjectQueryBuilder
|
||||
*/
|
||||
private function MakeSQLObjectQueryRoot($oBuild, $aAttToLoad = null, $aValues = array(), $aGroupByExpr = null, $aSelectExpr = null)
|
||||
{
|
||||
// $oOQLClassTreeBuilder = new OQLClassTreeBuilder($this->oDBObjetSearch, $oBuild);
|
||||
// $oOQLClassNode = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
||||
//
|
||||
// if ($this->bOptimizeQueries)
|
||||
// {
|
||||
// $oOQLClassTreeOptimizer = new OQLClassTreeOptimizer($oOQLClassNode, $oBuild);
|
||||
// $oOQLClassTreeOptimizer->OptimizeClassTree();
|
||||
// }
|
||||
|
||||
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, $aAttToLoad, $aValues);
|
||||
|
||||
/**
|
||||
@@ -696,5 +700,18 @@ class SQLObjectQueryBuilder
|
||||
return $oSelectBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \QueryBuilderContext $oBuild
|
||||
* @param \OQLClassNode $oOQLClassNode
|
||||
* @param array $aValues
|
||||
* @return \SQLObjectQuery
|
||||
*/
|
||||
// private function MakeSQLObjectQueryNew($oBuild, $oOQLClassNode, $aValues)
|
||||
// {
|
||||
// $oSQLQuery = new SQLObjectQuery($sTable, $sTableAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
|
||||
//
|
||||
// return $oSQLQuery;
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
@@ -284,6 +284,7 @@ return array(
|
||||
'NiceWebPage' => $baseDir . '/application/nicewebpage.class.inc.php',
|
||||
'OQLClassNode' => $baseDir . '/core/oqlclassnode.class.inc.php',
|
||||
'OQLClassTreeBuilder' => $baseDir . '/core/oqlclasstreebuilder.class.inc.php',
|
||||
'OQLClassTreeOptimizer' => $baseDir . '/core/oqlclasstreeoptimizer.class.inc.php',
|
||||
'OQLException' => $baseDir . '/core/oql/oqlexception.class.inc.php',
|
||||
'OQLJoin' => $baseDir . '/core/oqlclassnode.class.inc.php',
|
||||
'OQLLexer' => $baseDir . '/core/oql/oql-lexer.php',
|
||||
|
||||
@@ -505,6 +505,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'NiceWebPage' => __DIR__ . '/../..' . '/application/nicewebpage.class.inc.php',
|
||||
'OQLClassNode' => __DIR__ . '/../..' . '/core/oqlclassnode.class.inc.php',
|
||||
'OQLClassTreeBuilder' => __DIR__ . '/../..' . '/core/oqlclasstreebuilder.class.inc.php',
|
||||
'OQLClassTreeOptimizer' => __DIR__ . '/../..' . '/core/oqlclasstreeoptimizer.class.inc.php',
|
||||
'OQLException' => __DIR__ . '/../..' . '/core/oql/oqlexception.class.inc.php',
|
||||
'OQLJoin' => __DIR__ . '/../..' . '/core/oqlclassnode.class.inc.php',
|
||||
'OQLLexer' => __DIR__ . '/../..' . '/core/oql/oql-lexer.php',
|
||||
|
||||
Reference in New Issue
Block a user