diff --git a/core/oqlclassnode.class.inc.php b/core/oqlclassnode.class.inc.php index e0dc788dc..ff34c6a08 100644 --- a/core/oqlclassnode.class.inc.php +++ b/core/oqlclassnode.class.inc.php @@ -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; + } + } \ No newline at end of file diff --git a/core/oqlclasstreebuilder.class.inc.php b/core/oqlclasstreebuilder.class.inc.php index 49d554073..3197a25f8 100644 --- a/core/oqlclasstreebuilder.class.inc.php +++ b/core/oqlclasstreebuilder.class.inc.php @@ -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); } } } \ No newline at end of file diff --git a/core/oqlclasstreeoptimizer.class.inc.php b/core/oqlclasstreeoptimizer.class.inc.php new file mode 100644 index 000000000..a9358602a --- /dev/null +++ b/core/oqlclasstreeoptimizer.class.inc.php @@ -0,0 +1,68 @@ +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; + } +} \ No newline at end of file diff --git a/core/sqlobjectquerybuilder.class.inc.php b/core/sqlobjectquerybuilder.class.inc.php index c826ad4d4..eda311639 100644 --- a/core/sqlobjectquerybuilder.class.inc.php +++ b/core/sqlobjectquerybuilder.class.inc.php @@ -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; +// } + } \ No newline at end of file diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 10cf89214..ea593d232 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -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', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index c8fed3b55..92997ecc9 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.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',