Ver Fonte

N.1065 When joining, reverse leaf-root order : now it's root first !
* decrease the amount of joins on obsolescence use cases
* should also improve other uses cases as most of the time we believe the attribute linked is in the root class !
* the root table join is done using expressions instead of OQL for perf reasons
* a where clause on finalclass is also added to avoid problems if the leaf table join is not used (would be removed during query optimization phase)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4983 a333f486-631f-4898-b8df-5754b55c2be0

pgoiffon há 7 anos atrás
pai
commit
a82a7f1c3a
1 ficheiros alterados com 31 adições e 22 exclusões
  1. 31 22
      core/dbobjectsearch.class.php

+ 31 - 22
core/dbobjectsearch.class.php

@@ -1776,37 +1776,46 @@ class DBObjectSearch extends DBSearch
 			}
 		}
 
-		// First query built upon on the leaf (ie current) class
+		// First query built from the root, adding all tables including the leaf
+		//   Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
+		//   most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
 		//
-		self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()");
-		if (MetaModel::HasTable($sClass))
+		$oSelectBase = null;
+		$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
+		$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
+		foreach($aClassHierarchy as $sSomeClass)
 		{
-			$oSelectBase = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues);
-		}
-		else
-		{
-			$oSelectBase = null;
-
-			// As the join will not filter on the expected classes, we have to specify it explicitely
-			$sExpectedClasses = implode("', '", MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
-			$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
-			$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
-		}
-
-		// Then we join the queries of the eventual parent classes (compound model)
-		foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
-		{
-			if (!MetaModel::HasTable($sParentClass)) continue;
+			if (!MetaModel::HasTable($sSomeClass))
+			{
+				continue;
+			}
 
-			self::DbgTrace("Parent class: $sParentClass... let's call MakeSQLObjectQuerySingleTable()");
-			$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sParentClass, $aExtKeys, $aValues);
+			self::DbgTrace("Adding join from root to leaf: $sSomeClass... let's call MakeSQLObjectQuerySingleTable()");
+			$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSomeClass, $aExtKeys, $aValues);
 			if (is_null($oSelectBase))
 			{
 				$oSelectBase = $oSelectParentTable;
+				if (!$bIsClassStandaloneClass && (MetaModel::IsRootClass($sSomeClass)))
+				{
+					// As we're linking to root class first, we're adding a where clause on the finalClass attribute :
+					//      COALESCE($sRootClassFinalClass IN ('$sExpectedClasses'), 1)
+					// If we don't, the child classes can be removed in the query optimisation phase, including the leaf that was queried
+					// So we still need to filter records to only those corresponding to the child classes !
+					// The coalesce is mandatory if we have a polymorphic query (left join)
+					$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
+					$sFinalClassSqlColumnName = MetaModel::DBGetClassField($sSomeClass);
+					$oClassExpr = new FieldExpression($sFinalClassSqlColumnName, $oSelectBase->GetTableAlias());
+					$oInExpression = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
+					$oTrueExpression = new TrueExpression();
+					$aCoalesceAttr = array($oInExpression, $oTrueExpression);
+					$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
+
+					$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
+				}
 			}
 			else
 			{
-				$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sParentClass));
+				$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
 			}
 		}