mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
N°1161 - Add functions, order by and limits to DBSearch::MakeGroupByQuery()
SVN:trunk[5350]
This commit is contained in:
@@ -1434,7 +1434,7 @@ class DBObjectSearch extends DBSearch
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
{
|
||||
// Hide objects that are not visible to the current user
|
||||
//
|
||||
@@ -1517,7 +1517,15 @@ class DBObjectSearch extends DBSearch
|
||||
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->Render();
|
||||
}
|
||||
}
|
||||
if (!is_null($aSelectExpr))
|
||||
{
|
||||
foreach($aSelectExpr as $sAlias => $oExpr)
|
||||
{
|
||||
$sRawId .= 'se:'.$sAlias.'!'.$oExpr->Render();
|
||||
}
|
||||
}
|
||||
$aContextData['aGroupByExpr'] = $aGroupByExpr;
|
||||
$aContextData['aSelectExpr'] = $aSelectExpr;
|
||||
$sRawId .= $bGetCount;
|
||||
$aContextData['bGetCount'] = $bGetCount;
|
||||
if (is_array($aSelectedClasses))
|
||||
@@ -1542,6 +1550,7 @@ class DBObjectSearch extends DBSearch
|
||||
|
||||
// Query caching
|
||||
//
|
||||
$sOqlAPCCacheId = null;
|
||||
if (self::$m_bQueryCacheEnabled)
|
||||
{
|
||||
// Warning: using directly the query string as the key to the hash array can FAIL if the string
|
||||
@@ -1581,7 +1590,7 @@ class DBObjectSearch extends DBSearch
|
||||
if (!isset($oSQLQuery))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses);
|
||||
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
||||
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
|
||||
|
||||
if (self::$m_bQueryCacheEnabled)
|
||||
@@ -1601,16 +1610,17 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aAttToLoad
|
||||
* @param $bGetCount
|
||||
* @param $aModifierProperties
|
||||
* @param null $aGroupByExpr
|
||||
* @param null $aSelectedClasses
|
||||
* @param array $aAttToLoad
|
||||
* @param bool $bGetCount
|
||||
* @param array $aModifierProperties
|
||||
* @param array $aGroupByExpr
|
||||
* @param array $aSelectedClasses
|
||||
* @param array $aSelectExpr
|
||||
* @return null|SQLObjectQuery
|
||||
*/
|
||||
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
{
|
||||
$oBuild = new QueryBuilderContext($this, $aModifierProperties, $aGroupByExpr, $aSelectedClasses);
|
||||
$oBuild = new QueryBuilderContext($this, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
||||
|
||||
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, $aAttToLoad, array());
|
||||
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
|
||||
@@ -1624,6 +1634,17 @@ class DBObjectSearch extends DBSearch
|
||||
{
|
||||
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
|
||||
}
|
||||
if ($aSelectExpr)
|
||||
{
|
||||
// Get the fields corresponding to the select expressions
|
||||
foreach($oBuild->m_oQBExpressions->GetSelect() as $sAlias => $oExpr)
|
||||
{
|
||||
if (key_exists($sAlias, $aSelectExpr))
|
||||
{
|
||||
$oSQLQuery->AddSelect($sAlias, $oExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aMandatoryTables = null;
|
||||
if (self::$m_bOptimizeQueries)
|
||||
|
||||
@@ -429,8 +429,53 @@ abstract class DBSearch
|
||||
protected static $m_aQueryStructCache = array();
|
||||
|
||||
|
||||
public function MakeGroupByQuery($aArgs, $aGroupByExpr, $bExcludeNullValues = false)
|
||||
/** Generate a Group By SQL request from a search
|
||||
* @param array $aArgs
|
||||
* @param array $aGroupByExpr array('alias' => Expression)
|
||||
* @param bool $bExcludeNullValues
|
||||
* @param array $aSelectExpr array('alias' => Expression) Additional expressions added to the request
|
||||
* @param array $aOrderBy array('alias' => bool) true = ASC false = DESC
|
||||
* @param int $iLimitCount
|
||||
* @param int $iLimitStart
|
||||
* @return string SQL query generated
|
||||
* @throws Exception
|
||||
*/
|
||||
public function MakeGroupByQuery($aArgs, $aGroupByExpr, $bExcludeNullValues = false, $aSelectExpr = array(), $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0)
|
||||
{
|
||||
// Sanity check
|
||||
foreach($aGroupByExpr as $sAlias => $oExpr)
|
||||
{
|
||||
if (!($oExpr instanceof Expression))
|
||||
{
|
||||
throw new CoreException("Wrong parameter for 'Group By' for [$sAlias] (an array('alias' => Expression) is awaited)");
|
||||
}
|
||||
}
|
||||
foreach($aSelectExpr as $sAlias => $oExpr)
|
||||
{
|
||||
if (array_key_exists($sAlias, $aGroupByExpr))
|
||||
{
|
||||
throw new CoreException("Alias collision between 'Group By' and 'Select Expressions' [$sAlias]");
|
||||
}
|
||||
if (!($oExpr instanceof Expression))
|
||||
{
|
||||
throw new CoreException("Wrong parameter for 'Select Expressions' for [$sAlias] (an array('alias' => Expression) is awaited)");
|
||||
}
|
||||
}
|
||||
foreach($aOrderBy as $sAlias => $bAscending)
|
||||
{
|
||||
if (!array_key_exists($sAlias, $aGroupByExpr) && !array_key_exists($sAlias, $aSelectExpr) && ($sAlias != '_itop_count_'))
|
||||
{
|
||||
$aAllowedAliases = array_keys($aSelectExpr);
|
||||
$aAllowedAliases = array_merge($aAllowedAliases, array_keys($aGroupByExpr));
|
||||
$aAllowedAliases[] = '_itop_count_';
|
||||
throw new CoreException("Wrong alias [$sAlias] for 'Order By'. Allowed values are: ", null, implode(", ", $aAllowedAliases));
|
||||
}
|
||||
if (!is_bool($bAscending))
|
||||
{
|
||||
throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value for '$sAlias''");
|
||||
}
|
||||
}
|
||||
|
||||
if ($bExcludeNullValues)
|
||||
{
|
||||
// Null values are not handled (though external keys set to 0 are allowed)
|
||||
@@ -448,15 +493,15 @@ abstract class DBSearch
|
||||
}
|
||||
|
||||
$aAttToLoad = array();
|
||||
$oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr);
|
||||
$oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr, $aSelectExpr);
|
||||
|
||||
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
|
||||
try
|
||||
{
|
||||
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
|
||||
$sRes = $oSQLQuery->RenderGroupBy($aScalarArgs, $bBeautifulSQL);
|
||||
$sRes = $oSQLQuery->RenderGroupBy($aScalarArgs, $bBeautifulSQL, $aOrderBy, $iLimitCount, $iLimitStart);
|
||||
}
|
||||
catch (MissingQueryArgument $e)
|
||||
catch (Exception $e)
|
||||
{
|
||||
// Add some information...
|
||||
$e->addInfo('OQL', $this->ToOQL());
|
||||
@@ -563,9 +608,9 @@ abstract class DBSearch
|
||||
}
|
||||
|
||||
|
||||
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null)
|
||||
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
|
||||
{
|
||||
$oSQLQuery = $this->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr);
|
||||
$oSQLQuery = $this->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr);
|
||||
$oSQLQuery->SetSourceOQL($this->ToOQL());
|
||||
|
||||
// Join to an additional table, if required...
|
||||
@@ -587,7 +632,7 @@ abstract class DBSearch
|
||||
}
|
||||
|
||||
public abstract function GetSQLQueryStructure(
|
||||
$aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null
|
||||
$aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -143,7 +143,7 @@ class DBUnionSearch extends DBSearch
|
||||
|
||||
/**
|
||||
* Limited to the selected classes
|
||||
*/
|
||||
*/
|
||||
public function GetClassName($sAlias)
|
||||
{
|
||||
if (array_key_exists($sAlias, $this->aSelectedClasses))
|
||||
@@ -474,15 +474,17 @@ class DBUnionSearch extends DBSearch
|
||||
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
|
||||
}
|
||||
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
{
|
||||
if (count($this->aSearches) == 1)
|
||||
{
|
||||
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr);
|
||||
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectExpr);
|
||||
}
|
||||
|
||||
$aSQLQueries = array();
|
||||
$aAliases = array_keys($this->aSelectedClasses);
|
||||
$aQueryAttToLoad = null;
|
||||
$aUnionQuerySelectExpr = array();
|
||||
foreach ($this->aSearches as $iSearch => $oSearch)
|
||||
{
|
||||
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
|
||||
@@ -544,7 +546,43 @@ class DBUnionSearch extends DBSearch
|
||||
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
||||
}
|
||||
}
|
||||
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses);
|
||||
|
||||
if (is_null($aSelectExpr))
|
||||
{
|
||||
$aQuerySelectExpr = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aQuerySelectExpr = array();
|
||||
$aTranslationData = array();
|
||||
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
|
||||
foreach($aAliases as $iColumn => $sAlias)
|
||||
{
|
||||
$sQueryAlias = $aQueryColumns[$iColumn];
|
||||
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
|
||||
}
|
||||
foreach($aSelectExpr as $sExpressionAlias => $oExpression)
|
||||
{
|
||||
$oExpression->Browse(function ($oNode) use (&$aQuerySelectExpr, &$aTranslationData)
|
||||
{
|
||||
if ($oNode instanceof FieldExpression)
|
||||
{
|
||||
$sAlias = $oNode->GetParent()."__".$oNode->GetName();
|
||||
if (!key_exists($sAlias, $aQuerySelectExpr))
|
||||
{
|
||||
$aQuerySelectExpr[$sAlias] = $oNode->Translate($aTranslationData, false, false);
|
||||
}
|
||||
$aTranslationData[$oNode->GetParent()][$oNode->GetName()] = new FieldExpression($sAlias);
|
||||
}
|
||||
});
|
||||
// Only done for the first select as aliases are named after the first query
|
||||
if (!array_key_exists($sExpressionAlias, $aUnionQuerySelectExpr))
|
||||
{
|
||||
$aUnionQuerySelectExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses, $aQuerySelectExpr);
|
||||
if (count($aSearchAliases) > 1)
|
||||
{
|
||||
// Necessary to make sure that selected columns will match throughout all the queries
|
||||
@@ -554,7 +592,7 @@ class DBUnionSearch extends DBSearch
|
||||
$aSQLQueries[] = $oSubQuery;
|
||||
}
|
||||
|
||||
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr);
|
||||
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr, $aUnionQuerySelectExpr);
|
||||
//MyHelpers::var_dump_html($oSQLQuery, true);
|
||||
//MyHelpers::var_dump_html($oSQLQuery->RenderSelect(), true);
|
||||
if (self::$m_bDebugQuery) $oSQLQuery->DisplayHtml();
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Expression
|
||||
{
|
||||
return base64_encode($this->Render());
|
||||
}
|
||||
|
||||
|
||||
static public function unserialize($sValue)
|
||||
{
|
||||
return self::FromOQL(base64_decode($sValue));
|
||||
@@ -1373,7 +1373,7 @@ class QueryBuilderExpressions
|
||||
*/
|
||||
protected $m_aClassIds;
|
||||
|
||||
public function __construct(DBObjectSearch $oSearch, $aGroupByExpr = null)
|
||||
public function __construct(DBObjectSearch $oSearch, $aGroupByExpr = null, $aSelectExpr = null)
|
||||
{
|
||||
$this->m_oConditionExpr = $oSearch->GetCriteria();
|
||||
if (!$oSearch->GetShowObsoleteData())
|
||||
@@ -1387,7 +1387,7 @@ class QueryBuilderExpressions
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->m_aSelectExpr = array();
|
||||
$this->m_aSelectExpr = is_null($aSelectExpr) ? array() : $aSelectExpr;
|
||||
$this->m_aGroupByExpr = $aGroupByExpr;
|
||||
$this->m_aJoinFields = array();
|
||||
|
||||
@@ -1448,8 +1448,10 @@ class QueryBuilderExpressions
|
||||
|
||||
/**
|
||||
* Get tables representing the queried objects
|
||||
* Could be further optimized: when the first join is an outer join, then the rest can be omitted
|
||||
*/
|
||||
* Could be further optimized: when the first join is an outer join, then the rest can be omitted
|
||||
* @param array $aTables
|
||||
* @return array
|
||||
*/
|
||||
public function GetMandatoryTables(&$aTables = null)
|
||||
{
|
||||
if (is_null($aTables)) $aTables = array();
|
||||
@@ -1458,6 +1460,8 @@ class QueryBuilderExpressions
|
||||
{
|
||||
$oExpression->CollectUsedParents($aTables);
|
||||
}
|
||||
|
||||
return $aTables;
|
||||
}
|
||||
|
||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||
@@ -1498,7 +1502,6 @@ class QueryBuilderExpressions
|
||||
{
|
||||
$this->m_aJoinFields[$index] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
}
|
||||
|
||||
foreach($this->m_aClassIds as $sClass => $oExpression)
|
||||
{
|
||||
$this->m_aClassIds[$sClass] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||
|
||||
@@ -34,10 +34,10 @@ class QueryBuilderContext
|
||||
|
||||
public $m_oQBExpressions;
|
||||
|
||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
{
|
||||
$this->m_oRootFilter = $oFilter;
|
||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr);
|
||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr, $aSelectExpr);
|
||||
|
||||
$this->m_aClassAliases = $oFilter->GetJoinedClasses();
|
||||
$this->m_aTableAliases = array();
|
||||
|
||||
@@ -293,11 +293,12 @@ class SQLObjectQuery extends SQLQuery
|
||||
* Needed for the unions
|
||||
* @param $aOrderBy
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function RenderOrderByClause($aOrderBy)
|
||||
{
|
||||
$this->PrepareRendering();
|
||||
$sOrderBy = self::ClauseOrderBy($aOrderBy);
|
||||
$sOrderBy = self::ClauseOrderBy($aOrderBy, $this->__aFields);
|
||||
return $sOrderBy;
|
||||
}
|
||||
|
||||
@@ -357,8 +358,8 @@ class SQLObjectQuery extends SQLQuery
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelect = self::ClauseSelect($this->__aFields);
|
||||
$sOrderBy = self::ClauseOrderBy($aOrderBy);
|
||||
$sSelect = self::ClauseSelect($this->__aFields, $sLineSep);
|
||||
$sOrderBy = self::ClauseOrderBy($aOrderBy, $this->__aFields);
|
||||
if (!empty($sOrderBy))
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
|
||||
@@ -381,21 +382,42 @@ class SQLObjectQuery extends SQLQuery
|
||||
/**
|
||||
* @param array $aArgs
|
||||
* @param bool $bBeautifulQuery
|
||||
* @param array $aOrderBy
|
||||
* @param int $iLimitCount
|
||||
* @param int $iLimitStart
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false)
|
||||
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false, $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0)
|
||||
{
|
||||
$this->m_bBeautifulQuery = $bBeautifulQuery;
|
||||
$sLineSep = $this->m_bBeautifulQuery ? "\n" : '';
|
||||
$sIndent = $this->m_bBeautifulQuery ? " " : null;
|
||||
|
||||
$this->PrepareRendering();
|
||||
|
||||
$sSelect = self::ClauseSelect($this->__aFields);
|
||||
$sFrom = self::ClauseFrom($this->__aFrom, $sIndent);
|
||||
$sWhere = self::ClauseWhere($this->m_oConditionExpr, $aArgs);
|
||||
$sGroupBy = self::ClauseGroupBy($this->__aGroupBy);
|
||||
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep GROUP BY $sGroupBy";
|
||||
$sOrderBy = self::ClauseOrderBy($aOrderBy, $this->__aFields);
|
||||
if (!empty($sGroupBy))
|
||||
{
|
||||
$sGroupBy = "GROUP BY $sGroupBy$sLineSep";
|
||||
}
|
||||
if (!empty($sOrderBy))
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
|
||||
}
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep $sGroupBy $sOrderBy$sLineSep $sLimit";
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,18 +69,18 @@ abstract class SQLQuery
|
||||
abstract public function RenderDelete($aArgs = array());
|
||||
abstract public function RenderUpdate($aArgs = array());
|
||||
abstract public function RenderSelect($aOrderBy = array(), $aArgs = array(), $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulQuery = false);
|
||||
abstract public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false);
|
||||
abstract public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false, $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0);
|
||||
|
||||
abstract public function OptimizeJoins($aUsedTables, $bTopCall = true);
|
||||
|
||||
protected static function ClauseSelect($aFields)
|
||||
protected static function ClauseSelect($aFields, $sLineSep = '')
|
||||
{
|
||||
$aSelect = array();
|
||||
foreach ($aFields as $sFieldAlias => $sSQLExpr)
|
||||
{
|
||||
$aSelect[] = "$sSQLExpr AS $sFieldAlias";
|
||||
}
|
||||
$sSelect = implode(', ', $aSelect);
|
||||
$sSelect = implode(",$sLineSep ", $aSelect);
|
||||
return $sSelect;
|
||||
}
|
||||
|
||||
@@ -181,7 +181,13 @@ abstract class SQLQuery
|
||||
}
|
||||
}
|
||||
|
||||
protected static function ClauseOrderBy($aOrderBy)
|
||||
/**
|
||||
* @param array $aOrderBy
|
||||
* @param array $aExistingFields
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
*/
|
||||
protected static function ClauseOrderBy($aOrderBy, $aExistingFields)
|
||||
{
|
||||
$aOrderBySpec = array();
|
||||
foreach($aOrderBy as $sFieldAlias => $bAscending)
|
||||
|
||||
@@ -38,8 +38,9 @@ class SQLUnionQuery extends SQLQuery
|
||||
{
|
||||
protected $aQueries;
|
||||
protected $aGroupBy;
|
||||
protected $aSelectExpr;
|
||||
|
||||
public function __construct($aQueries, $aGroupBy)
|
||||
public function __construct($aQueries, $aGroupBy, $aSelectExpr = array())
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
@@ -49,6 +50,7 @@ class SQLUnionQuery extends SQLQuery
|
||||
$this->aQueries[] = $oSQLQuery->DeepClone();
|
||||
}
|
||||
$this->aGroupBy = $aGroupBy;
|
||||
$this->aSelectExpr = $aSelectExpr;
|
||||
}
|
||||
|
||||
public function DisplayHtml()
|
||||
@@ -129,7 +131,17 @@ class SQLUnionQuery extends SQLQuery
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false)
|
||||
|
||||
/**
|
||||
* @param array $aArgs
|
||||
* @param bool $bBeautifulQuery
|
||||
* @param array $aOrderBy
|
||||
* @param int $iLimitCount
|
||||
* @param int $iLimitStart
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false, $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0)
|
||||
{
|
||||
$this->m_bBeautifulQuery = $bBeautifulQuery;
|
||||
$sLineSep = $this->m_bBeautifulQuery ? "\n" : '';
|
||||
@@ -143,15 +155,41 @@ class SQLUnionQuery extends SQLQuery
|
||||
$sSelects = '('.implode(")$sLineSep UNION$sLineSep(", $aSelects).')';
|
||||
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
|
||||
|
||||
$aAliases = array();
|
||||
$aSelectAliases = array();
|
||||
$aGroupAliases = array();
|
||||
foreach ($this->aGroupBy as $sGroupAlias => $trash)
|
||||
{
|
||||
$aAliases[] = "`$sGroupAlias`";
|
||||
$aSelectAliases[$sGroupAlias] = "`$sGroupAlias`";
|
||||
$aGroupAliases[] = "`$sGroupAlias`";
|
||||
}
|
||||
foreach($this->aSelectExpr as $sSelectAlias => $oExpr)
|
||||
{
|
||||
$aSelectAliases[$sSelectAlias] = $oExpr->Render()." AS `$sSelectAlias`";
|
||||
}
|
||||
$sSelect = implode(', ', $aAliases);
|
||||
$sGroupBy = implode(', ', $aAliases);
|
||||
|
||||
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep GROUP BY $sGroupBy";
|
||||
$sSelect = implode(",$sLineSep ", $aSelectAliases);
|
||||
$sGroupBy = implode(', ', $aGroupAliases);
|
||||
|
||||
$sOrderBy = self::ClauseOrderBy($aOrderBy, $aSelectAliases);
|
||||
if (!empty($sGroupBy))
|
||||
{
|
||||
$sGroupBy = "GROUP BY $sGroupBy$sLineSep";
|
||||
}
|
||||
if (!empty($sOrderBy))
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
|
||||
}
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
|
||||
|
||||
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep $sGroupBy $sOrderBy$sLineSep $sLimit";
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
|
||||
343
test/GroupByAndFunctions.php
Normal file
343
test/GroupByAndFunctions.php
Normal file
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
require_once ('../approot.inc.php');
|
||||
require_once(APPROOT.'application/application.inc.php');
|
||||
require_once(APPROOT.'application/itopwebpage.class.inc.php');
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
require_once(APPROOT.'application/loginwebpage.class.inc.php');
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Main program
|
||||
//
|
||||
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed
|
||||
|
||||
|
||||
$sSubmit = utils::ReadParam('submit', '', false, 'raw_data');
|
||||
if ($sSubmit != 'Reset')
|
||||
{
|
||||
$sOQL = utils::ReadParam('OQL_Request', '', false, 'raw_data');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOQL = '';
|
||||
}
|
||||
$bError = false;
|
||||
$oP = new iTopWebPage('Database inconsistencies');
|
||||
$oP->set_base(utils::GetAbsoluteUrlAppRoot().'test/');
|
||||
$oP->set_title('Grouping with functions');
|
||||
$oP->add('<div style="padding: 15px;"><h2>Grouping with functions</h2>');
|
||||
$oP->add('<div style="padding: 15px; background: #ddd;">');
|
||||
try
|
||||
{
|
||||
if (!empty($sOQL))
|
||||
{
|
||||
// Getting class attributes
|
||||
$oSearch = DBSearch::FromOQL($sOQL);
|
||||
$aSearches = $oSearch->GetSearches();
|
||||
if ($oSearch instanceof DBUnionSearch)
|
||||
{
|
||||
$sClass = $aSearches[0]->GetClassAlias();
|
||||
$sRealClass = $aSearches[0]->GetClass();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClass = $oSearch->GetClassAlias();
|
||||
$sRealClass = $oSearch->GetClass();
|
||||
}
|
||||
|
||||
$sGroupBy1 = utils::ReadParam('groupby_1', '');
|
||||
$sGroupBy2 = utils::ReadParam('groupby_2', '');
|
||||
$sOrderBy1 = utils::ReadParam('orderby_1', '');
|
||||
$sOrderBy2 = utils::ReadParam('orderby_2', '');
|
||||
|
||||
$sAttributesOptions1 = '';
|
||||
$sAttributesOptions2 = '';
|
||||
$sAttributesOptions3 = '';
|
||||
$sAttributesOptions4 = '';
|
||||
|
||||
foreach(array('_itop_sum_', '_itop_avg_', '_itop_min_', '_itop_max_', '_itop_count_', 'group1', 'group2') as $sAttCode)
|
||||
{
|
||||
$sAttributesOptions3 .= '<option value="'.$sAttCode.'" '.($sOrderBy1 == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
$sAttributesOptions4 .= '<option value="'.$sAttCode.'" '.($sOrderBy2 == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
}
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs($sRealClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
// Skip this attribute if not defined in this table
|
||||
if ($oSearch instanceof DBUnionSearch)
|
||||
{
|
||||
foreach($aSearches as $oSubQuery)
|
||||
{
|
||||
$sSubClass = $oSubQuery->GetClass();
|
||||
if (!MetaModel::IsValidAttCode($sSubClass, $sAttCode))
|
||||
{
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sAttributesOptions1 .= '<option value="'.$sAttCode.'" '.($sGroupBy1 == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
$sAttributesOptions2 .= '<option value="'.$sAttCode.'" '.($sGroupBy2 == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
}
|
||||
|
||||
$iLimit = intval(utils::ReadParam('top', '0'));
|
||||
|
||||
$sInvOrder1 = utils::ReadParam('desc1', '');
|
||||
$sCheck1 = ($sInvOrder1 == 'on' ? 'checked' : '');
|
||||
|
||||
$sInvOrder2 = utils::ReadParam('desc2', '');
|
||||
$sCheck2 = ($sInvOrder2 == 'on' ? 'checked' : '');
|
||||
|
||||
$sFuncField = utils::ReadParam('funcfield', '');
|
||||
|
||||
$sFuncFieldOption = '';
|
||||
foreach(MetaModel::ListAttributeDefs($sRealClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
// Skip this attribute if not defined in this table
|
||||
if ($oSearch instanceof DBUnionSearch)
|
||||
{
|
||||
foreach($aSearches as $oSubQuery)
|
||||
{
|
||||
$sSubClass = $oSubQuery->GetClass();
|
||||
if (!MetaModel::IsValidAttCode($sSubClass, $sAttCode))
|
||||
{
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (get_class($oAttDef))
|
||||
{
|
||||
case 'Integer':
|
||||
case 'AttributeDecimal':
|
||||
case 'AttributeDuration':
|
||||
case 'AttributeSubItem':
|
||||
case 'AttributePercentage':
|
||||
$sFuncFieldOption .= '<option value="'.$sAttCode.'" '.($sFuncField == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oP->p('<div class="header_message message_error">'.$e->getMessage().'</div>');
|
||||
$bError = true;
|
||||
}
|
||||
$oP->add("<div><form>");
|
||||
$oP->add("<input type=\"submit\" name=\"submit\" value=\"Reset\">\n");
|
||||
$oP->add("</form></div>");
|
||||
|
||||
$oP->add("<form>");
|
||||
|
||||
$oP->add(
|
||||
<<<EOF
|
||||
<div>
|
||||
<label>Search OQL:</label>
|
||||
<div>
|
||||
<textarea id='OQL_Request' name='OQL_Request' cols='60' rows='5'>$sOQL</textarea>
|
||||
</div>
|
||||
</div>
|
||||
EOF
|
||||
);
|
||||
|
||||
if (!empty($sOQL) && !$bError)
|
||||
{
|
||||
$oP->add(
|
||||
<<<EOF
|
||||
<div>
|
||||
<label>Group by:</label>
|
||||
<div>
|
||||
<select id="groupby_1" name="groupby_1">
|
||||
$sAttributesOptions1
|
||||
</select>
|
||||
<select id="groupby_2" name="groupby_2">
|
||||
<option></option>
|
||||
$sAttributesOptions2
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Order by:</label>
|
||||
<div>
|
||||
<select id="orderby_1" name="orderby_1">$sAttributesOptions3</select>
|
||||
<label>Inv order</label><input type="checkbox" name="desc1" $sCheck1/>
|
||||
</div>
|
||||
<div>
|
||||
<select id="orderby_2" name="orderby_2">
|
||||
<option></option>
|
||||
$sAttributesOptions4
|
||||
</select>
|
||||
<label>Inv order</label><input type="checkbox" name="desc2" $sCheck2/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Functions on:</label>
|
||||
<div>
|
||||
<select id="funcfield" name="funcfield">$sFuncFieldOption</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Top:</label>
|
||||
<div><input type="text" id="top" name="top" value="$iLimit"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
$oP->add("<input type=\"submit\" name=\"submit\" value=\"Search\">\n");
|
||||
|
||||
$oP->add("</form>");
|
||||
|
||||
$sSQL = '';
|
||||
|
||||
|
||||
if (empty($sOQL) || empty($sGroupBy1))
|
||||
{
|
||||
$oP->output();
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
$iLimitStart = 0;
|
||||
$aOrderBy = array();
|
||||
if (!empty($sOrderBy1))
|
||||
{
|
||||
$aOrderBy[$sOrderBy1] = ($sInvOrder1 != 'on');
|
||||
}
|
||||
if (!empty($sOrderBy2))
|
||||
{
|
||||
$aOrderBy[$sOrderBy2] = ($sInvOrder2 != 'on');
|
||||
}
|
||||
|
||||
$aGroupBy = array();
|
||||
$oExpr1 = Expression::FromOQL($sClass.'.'.$sGroupBy1);
|
||||
$aGroupBy["group1"] = $oExpr1;
|
||||
|
||||
if (!empty($sGroupBy2))
|
||||
{
|
||||
$oExpr2 = Expression::FromOQL($sClass.'.'.$sGroupBy2);
|
||||
$aGroupBy["group2"] = $oExpr2;
|
||||
}
|
||||
|
||||
$aArgs = array();
|
||||
|
||||
if (empty($sFuncField))
|
||||
{
|
||||
$aFunctions = array();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oTimeExpr = Expression::FromOQL($sClass.'.'.$sFuncField);
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
}
|
||||
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy, $iLimit, $iLimitStart);
|
||||
|
||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||
|
||||
// Display results
|
||||
if (!empty($aRes))
|
||||
{
|
||||
$oP->add('<div>');
|
||||
$oP->add('<table class="listResults">');
|
||||
$aLine = $aRes[0];
|
||||
$aCols = array();
|
||||
$oP->add('<tr>');
|
||||
foreach(array_keys($aLine) as $item)
|
||||
{
|
||||
if (!is_numeric($item))
|
||||
{
|
||||
$aCols[] = $item;
|
||||
$oP->add("<th>$item</th>");
|
||||
}
|
||||
}
|
||||
$oP->add('</tr>');
|
||||
|
||||
foreach($aRes as $aLine)
|
||||
{
|
||||
$oP->add('<tr>');
|
||||
foreach($aCols as $sCol)
|
||||
{
|
||||
$oP->add("<td>".$aLine[$sCol]."</td>");
|
||||
}
|
||||
$oP->add('</tr>');
|
||||
}
|
||||
|
||||
$oP->add('</table>');
|
||||
$oP->add('</div>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<p>No Result</p>\n");
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oP->p('<div class="header_message message_error">'.$e->getMessage().'</div>');
|
||||
$bError = true;
|
||||
}
|
||||
|
||||
$oP->add("<div class=\"header_message message_info\">$sSQL</div>\n");
|
||||
|
||||
$oP->output();
|
||||
|
||||
return;
|
||||
|
||||
/*
|
||||
echo "<pre>";
|
||||
$aClassSelection = MetaModel::GetClasses();
|
||||
foreach($aClassSelection as $sClass)
|
||||
{
|
||||
if (!MetaModel::HasTable($sClass))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
// Skip this attribute if not defined in this table
|
||||
if (!MetaModel::IsAttributeOrigin($sClass, $sAttCode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (get_class($oAttDef))
|
||||
{
|
||||
case 'Integer':
|
||||
case 'AttributeDecimal':
|
||||
case 'AttributeDuration':
|
||||
case 'AttributeSubItem':
|
||||
case 'AttributePercentage':
|
||||
echo "$sClass:$sAttCode = ".get_class($oAttDef)."\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
483
test/core/DBSearchTest.php
Normal file
483
test/core/DBSearchTest.php
Normal file
@@ -0,0 +1,483 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Eric
|
||||
* Date: 06/02/2018
|
||||
* Time: 09:58
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use DBSearch;
|
||||
use Exception;
|
||||
use Expression;
|
||||
use FunctionExpression;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests of the DBSearch class.
|
||||
* <ul>
|
||||
* <li>MakeGroupByQuery</li>
|
||||
* </ul>
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class DBSearchTest extends ItopDataTestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider UReqProvider
|
||||
* @param $iOrgNb
|
||||
* @param $iPersonNb
|
||||
* @param $aReq
|
||||
* @param $iLimit
|
||||
* @param $aCountRes
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testMakeGroupByQuery($iOrgNb, $iPersonNb, $aReq, $iLimit, $aCountRes)
|
||||
{
|
||||
$sOrgs = $this->init_db($iOrgNb, $iPersonNb, $aReq);
|
||||
|
||||
$oSearch = DBSearch::FromOQL("SELECT UserRequest WHERE org_id IN ($sOrgs)");
|
||||
$this->assertNotNull($oSearch);
|
||||
$oExpr1 = Expression::FromOQL('UserRequest.org_id');
|
||||
|
||||
// Alias => Expression
|
||||
$aGroupBy = array('org_id' => $oExpr1);
|
||||
|
||||
$oTimeExpr = Expression::FromOQL('UserRequest.time_spent');
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array('_itop_sum_' => true, '_itop_count_' => true);
|
||||
|
||||
$aArgs = array();
|
||||
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy, $iLimit);
|
||||
$this->debug($sSQL);
|
||||
|
||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||
$this->debug($aRes);
|
||||
|
||||
$this->assertEquals(count($aCountRes), count($aRes));
|
||||
for ($i = 0; $i < count($aCountRes); $i++)
|
||||
{
|
||||
$this->assertEquals($aCountRes[$i], $aRes[$i]['_itop_count_']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function UReqProvider()
|
||||
{
|
||||
return array(
|
||||
"1 line" => array(1, 1, array(array(1, 0, 0)), 0, array('1')),
|
||||
"2 same lines" => array(1, 1, array(array(1, 0, 0), array(1, 0, 0)), 0, array('2')),
|
||||
"2 diff lines" => array(2, 2, array(array(1, 0, 0), array(1, 1, 1)), 0, array('1', '1')),
|
||||
"4 lines" => array(2, 2, array(array(1, 0, 0), array(1, 1, 1), array(1, 0, 0), array(1, 1, 1)), 0, array('2', '2')),
|
||||
"5 lines" => array(2, 2, array(array(1, 0, 0), array(1, 0, 0), array(1, 1, 1), array(1, 0, 0), array(1, 1, 1)), 0, array('2', '3')),
|
||||
"6 lines" => array(2, 4, array(array(1, 0, 0), array(1, 1, 3), array(1, 1, 1), array(1, 1, 3), array(1, 0, 2), array(1, 1, 1)), 0, array('2', '4')),
|
||||
"6 lines limit" => array(2, 4, array(array(1, 0, 0), array(1, 1, 3), array(1, 1, 1), array(1, 1, 1), array(1, 0, 0), array(1, 1, 1)), 1, array('2')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iOrgNb Number of Organization to create
|
||||
* @param int $iPersonNb Number of Person to create
|
||||
* @param array $aReq UserRequests to create: array(array([time_spent value], [org index], [person index]))
|
||||
* @return string organization list for select
|
||||
* @throws Exception
|
||||
*/
|
||||
private function init_db($iOrgNb, $iPersonNb, $aReq)
|
||||
{
|
||||
$aOrgIds = array();
|
||||
$sOrgs = '';
|
||||
for($i = 0; $i < $iOrgNb; $i++)
|
||||
{
|
||||
$oObj = $this->CreateOrganization('UnitTest_Org'.$i);
|
||||
$sKey = $oObj->GetKey();
|
||||
$aOrgIds[] = $sKey;
|
||||
if ($i > 0)
|
||||
{
|
||||
$sOrgs .= ",";
|
||||
}
|
||||
$sOrgs .= $sKey;
|
||||
}
|
||||
$this->assertEquals($iOrgNb, count($aOrgIds));
|
||||
|
||||
$aPersonIds = array();
|
||||
for($i = 0; $i < $iPersonNb; $i++)
|
||||
{
|
||||
$oObj = $this->CreatePerson($i, $aOrgIds[$i % $iOrgNb]);
|
||||
$aPersonIds[] = $oObj->GetKey();
|
||||
}
|
||||
$this->assertEquals($iPersonNb, count($aPersonIds));
|
||||
|
||||
$i = 0;
|
||||
foreach($aReq as $aParams)
|
||||
{
|
||||
$oObj = $this->CreateUserRequest($i, $aParams[0], $aOrgIds[$aParams[1]], $aPersonIds[$aParams[2]]);
|
||||
$this->assertNotNull($oObj);
|
||||
$i++;
|
||||
}
|
||||
return $sOrgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testGroupByUnion()
|
||||
{
|
||||
$oServer = $this->CreateServer(1);
|
||||
|
||||
$this->CreatePhysicalInterface(1, 1000, $oServer->GetKey());
|
||||
$this->CreateFiberChannelInterface(1, 1000, $oServer->GetKey());
|
||||
|
||||
|
||||
$oSearch = DBSearch::FromOQL("SELECT FiberChannelInterface AS FCI WHERE FCI.name = '1' UNION SELECT PhysicalInterface AS PHI WHERE PHI.name = '1'");
|
||||
$this->assertNotNull($oSearch);
|
||||
$oExpr1 = Expression::FromOQL('FCI.name');
|
||||
|
||||
// Alias => Expression (first select reference)
|
||||
$aGroupBy = array('group1' => $oExpr1);
|
||||
|
||||
$oTimeExpr = Expression::FromOQL('FCI.speed');
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array('group1' => true, '_itop_count_' => true);
|
||||
|
||||
$aArgs = array();
|
||||
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
$this->debug($sSQL);
|
||||
|
||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||
$this->debug($aRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testOrderBy_1()
|
||||
{
|
||||
|
||||
$oSearch = DBSearch::FromOQL("SELECT FiberChannelInterface");
|
||||
$this->assertNotNull($oSearch);
|
||||
|
||||
// Alias => Expression (first select reference)
|
||||
$oExpr1 = Expression::FromOQL('FiberChannelInterface.name');
|
||||
$aGroupBy = array('group1' => $oExpr1);
|
||||
|
||||
$oTimeExpr = Expression::FromOQL('FiberChannelInterface.speed');
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
$aArgs = array();
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array(
|
||||
'group1' => true,
|
||||
'_itop_sum_' => true,
|
||||
'_itop_avg_' => true,
|
||||
'_itop_min_' => true,
|
||||
'_itop_max_' => true);
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
$this->assertNotEmpty($sSQL);
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array('nothing_good' => true);
|
||||
$this->expectException("CoreException");
|
||||
$oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
|
||||
$this->assertTrue(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testSanity_1()
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL("SELECT FiberChannelInterface AS FCI WHERE FCI.name = '1' UNION SELECT PhysicalInterface AS PHI WHERE PHI.name = '1'");
|
||||
$this->assertNotNull($oSearch);
|
||||
$oExpr1 = Expression::FromOQL('FCI.name');
|
||||
|
||||
// Alias => Expression (first select reference)
|
||||
$aGroupBy = array('group1' => $oExpr1);
|
||||
|
||||
$oTimeExpr = Expression::FromOQL('FCI.speed');
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'group1' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
$aArgs = array();
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array(
|
||||
'group1' => true,
|
||||
'_itop_sum_' => true,
|
||||
'_itop_avg_' => true,
|
||||
'_itop_min_' => true,
|
||||
'_itop_max_' => true);
|
||||
|
||||
$this->expectException("CoreException");
|
||||
$oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
|
||||
$this->assertTrue(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testSanity_2()
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL("SELECT FiberChannelInterface AS FCI WHERE FCI.name = '1' UNION SELECT PhysicalInterface AS PHI WHERE PHI.name = '1'");
|
||||
$this->assertNotNull($oSearch);
|
||||
|
||||
// Alias => Expression (first select reference)
|
||||
$oExpr1 = Expression::FromOQL('FCI.name');
|
||||
$aGroupBy = array('group1' => $oExpr1);
|
||||
|
||||
$oTimeExpr = Expression::FromOQL('FCI.speed');
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
$aArgs = array();
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array(
|
||||
'group1' => true,
|
||||
'_itop_sum_' => true,
|
||||
'_itop_avg_' => true,
|
||||
'_itop_min_' => true,
|
||||
'_itop_max_' => true);
|
||||
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
$this->assertNotEmpty($sSQL);
|
||||
|
||||
$aGroupBy = array('group1' => 'FCI.name');
|
||||
$this->expectException("CoreException");
|
||||
$oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
|
||||
$this->assertTrue(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testSanity_3()
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL("SELECT FiberChannelInterface AS FCI WHERE FCI.name = '1' UNION SELECT PhysicalInterface AS PHI WHERE PHI.name = '1'");
|
||||
$this->assertNotNull($oSearch);
|
||||
|
||||
// Alias => Expression (first select reference)
|
||||
$oExpr1 = Expression::FromOQL('FCI.name');
|
||||
$aGroupBy = array('group1' => $oExpr1);
|
||||
|
||||
$oTimeExpr = Expression::FromOQL('FCI.speed');
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
$aArgs = array();
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array(
|
||||
'group1' => true,
|
||||
'_itop_sum_' => true,
|
||||
'_itop_avg_' => true,
|
||||
'_itop_min_' => true,
|
||||
'_itop_max_' => true);
|
||||
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
$this->assertNotEmpty($sSQL);
|
||||
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => 'SumExpr',
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
|
||||
$this->expectException("CoreException");
|
||||
$oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
|
||||
$this->assertTrue(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testSanity_4()
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL("SELECT FiberChannelInterface AS FCI WHERE FCI.name = '1' UNION SELECT PhysicalInterface AS PHI WHERE PHI.name = '1'");
|
||||
$this->assertNotNull($oSearch);
|
||||
|
||||
// Alias => Expression (first select reference)
|
||||
$oExpr1 = Expression::FromOQL('FCI.name');
|
||||
$aGroupBy = array('group1' => $oExpr1);
|
||||
|
||||
$oTimeExpr = Expression::FromOQL('FCI.speed');
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
$aArgs = array();
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array(
|
||||
'group1' => true,
|
||||
'_itop_sum_' => true,
|
||||
'_itop_avg_' => true,
|
||||
'_itop_min_' => true,
|
||||
'_itop_max_' => true);
|
||||
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
$this->assertNotEmpty($sSQL);
|
||||
|
||||
$aOrderBy = array(
|
||||
'group1' => true,
|
||||
'_itop_sum_' => true,
|
||||
'_itop_avg_' => 'ASC',
|
||||
'_itop_min_' => true,
|
||||
'_itop_max_' => true);
|
||||
|
||||
$this->expectException("CoreException");
|
||||
$oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
|
||||
$this->assertTrue(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testSanity_5()
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL("SELECT FiberChannelInterface AS FCI WHERE FCI.name = '1' UNION SELECT PhysicalInterface AS PHI WHERE PHI.name = '1'");
|
||||
$this->assertNotNull($oSearch);
|
||||
|
||||
// Alias => Expression (first select reference)
|
||||
$oExpr1 = Expression::FromOQL('FCI.name');
|
||||
$aGroupBy = array('group1' => $oExpr1);
|
||||
|
||||
$oTimeExpr = Expression::FromOQL('FCI.speed');
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
$aArgs = array();
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array(
|
||||
'group1' => true,
|
||||
'_itop_sum_' => true,
|
||||
'_itop_avg_' => true,
|
||||
'_itop_min_' => true,
|
||||
'_itop_max_' => true);
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
$this->assertNotEmpty($sSQL);
|
||||
|
||||
// Alias => Order
|
||||
$aOrderBy = array('nothing_good' => true);
|
||||
$this->expectException("CoreException");
|
||||
$oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy);
|
||||
|
||||
$this->assertTrue(false);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user