N°2272 - OQL performance (OQL class tree optimizer)

This commit is contained in:
Eric
2019-09-17 14:38:32 +02:00
parent 93a736e42a
commit 6073be25de
6 changed files with 132 additions and 20 deletions

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View 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;
}
}

View File

@@ -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;
// }
}

View File

@@ -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',

View File

@@ -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',