mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
OQL: Fixed a number of bugs, and implemented new features
- bug: a JOIN b on b.extkey = a.id - bug: operators precedence (still a shift-reduce conflict with JOINS) - changed pkey into id (preserved the compatibility for DBObjectSearch::AddCondition() - allow implicit class name in WHERE condition - bug: wrong report on typo error - suggest an alternative in case of typo error SVN:code[12]
This commit is contained in:
@@ -94,6 +94,113 @@ class MyHelpers
|
||||
echo "\n</pre>\n";
|
||||
}
|
||||
|
||||
public static function var_dump_string($var)
|
||||
{
|
||||
ob_start();
|
||||
print_r($var);
|
||||
$sRet = ob_get_clean();
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
protected static function first_diff_line($s1, $s2)
|
||||
{
|
||||
$aLines1 = explode("\n", $s1);
|
||||
$aLines2 = explode("\n", $s2);
|
||||
for ($i = 0 ; $i < min(count($aLines1), count($aLines2)) ; $i++)
|
||||
{
|
||||
if ($aLines1[$i] != $aLines2[$i]) return $i;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function highlight_line($sMultiline, $iLine, $sHighlightStart = '<b>', $sHightlightEnd = '</b>')
|
||||
{
|
||||
$aLines = explode("\n", $sMultiline);
|
||||
$aLines[$iLine] = $sHighlightStart.$aLines[$iLine].$sHightlightEnd;
|
||||
return implode("\n", $aLines);
|
||||
}
|
||||
|
||||
protected static function first_diff($s1, $s2)
|
||||
{
|
||||
// do not work fine with multiline strings
|
||||
$iLen1 = strlen($s1);
|
||||
$iLen2 = strlen($s2);
|
||||
for ($i = 0 ; $i < min($iLen1, $iLen2) ; $i++)
|
||||
{
|
||||
if ($s1[$i] !== $s2[$i]) return $i;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function last_diff($s1, $s2)
|
||||
{
|
||||
// do not work fine with multiline strings
|
||||
$iLen1 = strlen($s1);
|
||||
$iLen2 = strlen($s2);
|
||||
for ($i = 0 ; $i < min(strlen($s1), strlen($s2)) ; $i++)
|
||||
{
|
||||
if ($s1[$iLen1 - $i - 1] !== $s2[$iLen2 - $i - 1]) return array($iLen1 - $i, $iLen2 - $i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function text_cmp_html($sText1, $sText2, $sHighlight)
|
||||
{
|
||||
$iDiffPos = self::first_diff_line($sText1, $sText2);
|
||||
$sDisp1 = self::highlight_line($sText1, $iDiffPos, '<div style="'.$sHighlight.'">', '</div>');
|
||||
$sDisp2 = self::highlight_line($sText2, $iDiffPos, '<div style="'.$sHighlight.'">', '</div>');
|
||||
echo "<table style=\"valign=top;\">\n";
|
||||
echo "<tr>\n";
|
||||
echo "<td><pre>$sDisp1</pre></td>\n";
|
||||
echo "<td><pre>$sDisp2</pre></td>\n";
|
||||
echo "</tr>\n";
|
||||
echo "</table>\n";
|
||||
}
|
||||
|
||||
protected static function string_cmp_html($s1, $s2, $sHighlight)
|
||||
{
|
||||
$iDiffPos = self::first_diff($s1, $s2);
|
||||
if ($iDiffPos === false)
|
||||
{
|
||||
echo "strings are identical";
|
||||
return;
|
||||
}
|
||||
$sStart = substr($s1, 0, $iDiffPos);
|
||||
|
||||
$aLastDiff = self::last_diff($s1, $s2);
|
||||
$sEnd = substr($s1, $aLastDiff[0]);
|
||||
|
||||
$sMiddle1 = substr($s1, $iDiffPos, $aLastDiff[0] - $iDiffPos);
|
||||
$sMiddle2 = substr($s2, $iDiffPos, $aLastDiff[1] - $iDiffPos);
|
||||
|
||||
echo "<p>$sStart<span style=\"$sHighlight\">$sMiddle1</span>$sEnd</p>\n";
|
||||
echo "<p>$sStart<span style=\"$sHighlight\">$sMiddle2</span>$sEnd</p>\n";
|
||||
}
|
||||
|
||||
protected static function object_cmp_html($oObj1, $oObj2, $sHighlight)
|
||||
{
|
||||
$sObj1 = self::var_dump_string($oObj1);
|
||||
$sObj2 = self::var_dump_string($oObj2);
|
||||
return self::text_cmp_html($sObj1, $sObj2, $sHighlight);
|
||||
}
|
||||
|
||||
public static function var_cmp_html($var1, $var2, $sHighlight = 'color:red; font-weight:bold;')
|
||||
{
|
||||
if (is_object($var1))
|
||||
{
|
||||
return self::object_cmp_html($var1, $var2, $sHighlight);
|
||||
}
|
||||
else if (count(explode("\n", $var1)) > 1)
|
||||
{
|
||||
// multiline string
|
||||
return self::text_cmp_html($var1, $var2, $sHighlight);
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::string_cmp_html($var1, $var2, $sHighlight);
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
{
|
||||
if ($aCallStack == null) $aCallStack = debug_backtrace();
|
||||
@@ -176,6 +283,7 @@ class MyHelpers
|
||||
{
|
||||
if (!is_array($aData)) trigger_error("make_table_from_assoc_array: Error - the passed argument is not an array", E_USER_ERROR);
|
||||
$aFirstRow = reset($aData);
|
||||
if (count($aData) == 0) return '';
|
||||
if (!is_array($aFirstRow)) trigger_error("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array", E_USER_ERROR);
|
||||
$sOutput = "";
|
||||
$sOutput .= "<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"1\" CELLPADDING=\"1\">\n";
|
||||
|
||||
@@ -113,7 +113,7 @@ abstract class DBObject
|
||||
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey);
|
||||
if (empty($aRow))
|
||||
{
|
||||
trigger_error("Failed to reload object of class '".get_class($this)."', pkey = ".$this->m_iKey, E_USER_ERROR);
|
||||
trigger_error("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey, E_USER_ERROR);
|
||||
}
|
||||
$this->FromRow($aRow);
|
||||
|
||||
@@ -133,7 +133,7 @@ abstract class DBObject
|
||||
$sMyClass = $oRemoteExtKeyAtt->GetTargetClass();
|
||||
|
||||
$oMyselfSearch = new DBObjectSearch($sMyClass);
|
||||
$oMyselfSearch->AddCondition('pkey', $this->m_iKey, '=');
|
||||
$oMyselfSearch->AddCondition('id', $this->m_iKey, '=');
|
||||
|
||||
$oLinkSearch = new DBObjectSearch($sLinkClass);
|
||||
$oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe);
|
||||
@@ -157,7 +157,7 @@ abstract class DBObject
|
||||
|
||||
// Get the key
|
||||
//
|
||||
$sKeyField = "pkey";
|
||||
$sKeyField = "id";
|
||||
if (!array_key_exists($sKeyField, $aRow))
|
||||
{
|
||||
// #@# Bug ?
|
||||
@@ -168,7 +168,7 @@ abstract class DBObject
|
||||
$iPKey = $aRow[$sKeyField];
|
||||
if (!self::IsValidPKey($iPKey))
|
||||
{
|
||||
trigger_error("An object PKey must be an integer value ($iPKey)", E_USER_NOTICE);
|
||||
trigger_error("An object id must be an integer value ($iPKey)", E_USER_NOTICE);
|
||||
}
|
||||
$this->m_iKey = $iPKey;
|
||||
}
|
||||
@@ -367,7 +367,7 @@ abstract class DBObject
|
||||
{
|
||||
if (!self::IsValidPKey($iNewKey))
|
||||
{
|
||||
trigger_error("An object PKey must be an integer value ($iNewKey)", E_USER_ERROR);
|
||||
trigger_error("An object id must be an integer value ($iNewKey)", E_USER_ERROR);
|
||||
}
|
||||
|
||||
if ($this->m_bIsInDB && !empty($this->m_iKey) && ($this->m_iKey != $iNewKey))
|
||||
@@ -660,7 +660,7 @@ abstract class DBObject
|
||||
if (count($aChanges) != 0)
|
||||
{
|
||||
$oFilter = new DBObjectSearch(get_class($this));
|
||||
$oFilter->AddCondition('pkey', $this->m_iKey, '=');
|
||||
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
||||
|
||||
$sSQL = MetaModel::MakeUpdateQuery($oFilter, $aChanges);
|
||||
CMDBSource::Query($sSQL);
|
||||
@@ -691,7 +691,7 @@ abstract class DBObject
|
||||
public function DBDelete()
|
||||
{
|
||||
$oFilter = new DBObjectSearch(get_class($this));
|
||||
$oFilter->AddCondition('pkey', $this->m_iKey, '=');
|
||||
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
||||
|
||||
$sSQL = MetaModel::MakeDeleteQuery($oFilter);
|
||||
CMDBSource::Query($sSQL);
|
||||
|
||||
@@ -162,7 +162,7 @@ class DBObjectSearch
|
||||
|
||||
public function __DescribeHTML()
|
||||
{
|
||||
$sConditionDesc = $this->DescribeConditions();
|
||||
$sConditionDesc = $this->DescribeConditions();
|
||||
if (!empty($sConditionDesc))
|
||||
{
|
||||
return "Objects of class '$this->m_sClass', $sConditionDesc";
|
||||
@@ -179,8 +179,8 @@ class DBObjectSearch
|
||||
public function ResetCondition()
|
||||
{
|
||||
$this->m_oSearchCondition = new TrueExpression();
|
||||
// ? is that enough, do I need to rebuild the list after the subqueries ?
|
||||
$this->m_aClasses = array($this->m_sClassAlias => $this->m_sClass);
|
||||
// ? is that usefull/enough, do I need to rebuild the list after the subqueries ?
|
||||
// $this->m_aClasses = array($this->m_sClassAlias => $this->m_sClass);
|
||||
}
|
||||
|
||||
public function AddConditionExpression($oExpression)
|
||||
@@ -190,6 +190,11 @@ class DBObjectSearch
|
||||
|
||||
public function AddCondition($sFilterCode, $value, $sOpCode = null)
|
||||
{
|
||||
// #@# backward compatibility for pkey/id
|
||||
if (strtolower(trim($sFilterCode)) == 'pkey') $sFilterCode = 'id';
|
||||
// #@# todo - obsolete smoothly, first send exceptions
|
||||
// throw new CoreException('SibusQL has been obsoleted, please update your queries', array('sibusql'=>$sQuery, 'oql'=>$oFilter->ToOQL()));
|
||||
|
||||
MyHelpers::CheckKeyInArray('filter code', $sFilterCode, MetaModel::GetClassFilterDefs($this->m_sClass));
|
||||
$oFilterDef = MetaModel::GetClassFilterDef($this->m_sClass, $sFilterCode);
|
||||
|
||||
@@ -215,11 +220,13 @@ class DBObjectSearch
|
||||
break;
|
||||
|
||||
case "IN":
|
||||
if (!is_array($value)) $value = array($value);
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
break;
|
||||
|
||||
case "NOTIN":
|
||||
if (!is_array($value)) $value = array($value);
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
|
||||
$sOQLCondition = $oField->Render()." NOT IN $sListExpr";
|
||||
break;
|
||||
@@ -271,7 +278,7 @@ class DBObjectSearch
|
||||
$sOrigAlias = $this->m_sClassAlias;
|
||||
if (array_key_exists($sOrigAlias, $aClassAliases))
|
||||
{
|
||||
$this->m_sClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $oFilter->GetClass());
|
||||
$this->m_sClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $this->m_sClass);
|
||||
// Translate the condition expression with the new alias
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $this->m_sClassAlias;
|
||||
}
|
||||
@@ -316,19 +323,13 @@ class DBObjectSearch
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOrigAlias = $oFilter->GetClassAlias();
|
||||
$sKeyClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $oFilter->GetClass());
|
||||
if ($sKeyClassAlias != $sOrigAlias)
|
||||
{
|
||||
// Translate the condition expression with the new alias
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $sKeyClassAlias;
|
||||
}
|
||||
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
|
||||
|
||||
// #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!!
|
||||
$oNewFilter = clone $oFilter;
|
||||
$oNewFilter->ResetCondition();
|
||||
// $oNewFilter = clone $oFilter;
|
||||
// $oNewFilter->ResetCondition();
|
||||
|
||||
$this->m_aPointingTo[$sExtKeyAttCode] = $oNewFilter;
|
||||
$this->m_aPointingTo[$sExtKeyAttCode] = $oFilter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +341,7 @@ class DBObjectSearch
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation)
|
||||
protected function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
$sForeignClass = $oFilter->GetClass();
|
||||
$sForeignClassAlias = $oFilter->GetClassAlias();
|
||||
@@ -359,19 +360,13 @@ class DBObjectSearch
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOrigAlias = $oFilter->GetClassAlias();
|
||||
$sKeyClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $oFilter->GetClass());
|
||||
if ($sKeyClassAlias != $sOrigAlias)
|
||||
{
|
||||
// Translate the condition expression with the new alias
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $sKeyClassAlias;
|
||||
}
|
||||
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
|
||||
|
||||
// #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!!
|
||||
$oNewFilter = clone $oFilter;
|
||||
$oNewFilter->ResetCondition();
|
||||
//$oNewFilter = clone $oFilter;
|
||||
//$oNewFilter->ResetCondition();
|
||||
|
||||
$this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]= $oNewFilter;
|
||||
$this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]= $oFilter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,47 +588,36 @@ class DBObjectSearch
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
public function ToSibusQL()
|
||||
public function ToOQL()
|
||||
{
|
||||
$aConds = array(); // string conditions, to be merged into a logical AND
|
||||
foreach($this->m_aFullText as $sFullText)
|
||||
{
|
||||
$aConds[] = "* HAS ".self::Value2Expression($sFullText);
|
||||
}
|
||||
// #@# todo - changer ToSibusQL en ToOQL et l'implementer !
|
||||
// $aConds[] = $this->m_oSearchCondition->ToSibusQL
|
||||
/*
|
||||
foreach($this->m_aCriteria as $aCritInfo)
|
||||
{
|
||||
$aConds[] = $aCritInfo["filtercode"]." ".$aCritInfo["opcode"]." ".self::Value2Expression($aCritInfo["value"]);
|
||||
}
|
||||
*/
|
||||
$sRes = "SELECT ".$this->GetClass().' AS '.$this->GetClassAlias();
|
||||
$sRes .= $this->ToOQL_Joins();
|
||||
$sRes .= " WHERE ".$this->m_oSearchCondition->Render();
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
protected function ToOQL_Joins()
|
||||
{
|
||||
$sRes = '';
|
||||
foreach($this->m_aPointingTo as $sExtKey=>$oFilter)
|
||||
{
|
||||
$aConds[] = $sExtKey." IN (".$oFilter->ToSibusQL().")";
|
||||
$sRes .= ' JOIN '.$oFilter->GetClass().' AS '.$oFilter->GetClassAlias().' ON '.$this->GetClassAlias().'.'.$sExtKey.' = '.$oFilter->GetClassAlias().'.id';
|
||||
$sRes .= $oFilter->ToOQL_Joins();
|
||||
}
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
$aConds[] = "PKEY IS ".$sForeignExtKeyAttCode." IN (".$oForeignFilter->ToSibusQL().")";
|
||||
$sRes .= ' JOIN '.$oForeignFilter->GetClass().' AS '.$oForeignFilter->GetClassAlias().' ON '.$oForeignFilter->GetClassAlias().'.'.$sForeignExtKeyAttCode.' = '.$this->GetClassAlias().'.id';
|
||||
$sRes .= $oForeignFilter->ToOQL_Joins();
|
||||
}
|
||||
}
|
||||
foreach($this->m_aRelatedTo as $aRelatedTo)
|
||||
{
|
||||
$oFilter = $aRelatedTo['flt'];
|
||||
$sRelCode = $aRelatedTo['relcode'];
|
||||
$iMaxDepth = $aRelatedTo['maxdepth'];
|
||||
|
||||
$aConds[] = "RELATED ($sRelCode, $iMaxDepth) TO (".$oFilter->ToSibuSQL().")";
|
||||
}
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
$sValue = $this->GetClass();
|
||||
if (count($aConds) > 0)
|
||||
{
|
||||
$sValue .= ": ".implode(" AND ", $aConds);
|
||||
}
|
||||
return $sValue;
|
||||
public function ToSibusQL()
|
||||
{
|
||||
return "NONONO";
|
||||
}
|
||||
|
||||
static private function privProcessParams($sQuery, array $aParams, $oDbObject)
|
||||
@@ -659,7 +643,7 @@ class DBObjectSearch
|
||||
if (strpos($sParameterName, "this.") === 0)
|
||||
{
|
||||
$sAttCode = substr($sParameterName, strlen("this."));
|
||||
if ($sAttCode == 'pkey')
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$sValue = $oDbObject->GetKey();
|
||||
}
|
||||
@@ -717,19 +701,37 @@ class DBObjectSearch
|
||||
$sFltCode = $oExpression->GetName();
|
||||
if (empty($sClassAlias))
|
||||
{
|
||||
$iPos = $oExpression->GetPosition();
|
||||
throw new OqlNormalizeException('Missing class specification', $sQuery, 0, $iPos, '');
|
||||
// Try to find an alias
|
||||
// Build an array of field => array of aliases
|
||||
$aFieldClasses = array();
|
||||
foreach($aClassAliases as $sAlias => $sReal)
|
||||
{
|
||||
foreach(MetaModel::GetFiltersList($sReal) as $sAnFltCode)
|
||||
{
|
||||
$aFieldClasses[$sAnFltCode][] = $sAlias;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($sFltCode, $aFieldClasses))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
if (count($aFieldClasses[$sFltCode]) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sQuery, $oExpression->GetNameDetails());
|
||||
}
|
||||
$sClassAlias = $aFieldClasses[$sFltCode][0];
|
||||
}
|
||||
if (!array_key_exists($sClassAlias, $aClassAliases))
|
||||
else
|
||||
{
|
||||
$iPos = $oExpression->GetPosition();
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, 0, $iPos, $sClassAlias, array_keys($aClassAliases));
|
||||
}
|
||||
$sClass = $aClassAliases[$sClassAlias];
|
||||
if (!MetaModel::IsValidFilterCode($sClass, $sFltCode))
|
||||
{
|
||||
$iPos = $oExpression->GetPosition();
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, 0, $iPos, "$sFltCode in class $sClassAlias", MetaModel::GetFiltersList($sClass));
|
||||
if (!array_key_exists($sClassAlias, $aClassAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oExpression->GetParentDetails(), array_keys($aClassAliases));
|
||||
}
|
||||
$sClass = $aClassAliases[$sClassAlias];
|
||||
if (!MetaModel::IsValidFilterCode($sClass, $sFltCode))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), MetaModel::GetFiltersList($sClass));
|
||||
}
|
||||
}
|
||||
|
||||
return new FieldExpression($sFltCode, $sClassAlias);
|
||||
@@ -768,11 +770,11 @@ class DBObjectSearch
|
||||
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, 0, 0, $sClass, MetaModel::GetClasses());
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, $oOqlQuery->GetClassDetails(), MetaModel::GetClasses());
|
||||
}
|
||||
|
||||
$oResultFilter = new DBObjectSearch($sClass, $sClassAlias);
|
||||
$oResultFilter->m_aClasses = array($sClassAlias => $sClass);
|
||||
$aAliases = array($sClassAlias => $sClass);
|
||||
|
||||
// Maintain an array of filters, because the flat list is in fact referring to a tree
|
||||
// And this will be an easy way to dispatch the conditions
|
||||
@@ -788,17 +790,17 @@ class DBObjectSearch
|
||||
$sJoinClassAlias = $oJoinSpec->GetClassAlias();
|
||||
if (!MetaModel::IsValidClass($sJoinClass))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, 0, 0, $sJoinClass, MetaModel::GetClasses());
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, $oJoinSpec->GetClassDetails(), MetaModel::GetClasses());
|
||||
}
|
||||
if (array_key_exists($sJoinClassAlias, $oResultFilter->m_aClasses))
|
||||
if (array_key_exists($sJoinClassAlias, $aAliases))
|
||||
{
|
||||
if ($sJoinClassAlias != $sJoinClass)
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class alias', $sQuery, 0, 0, $sJoinClassAlias);
|
||||
throw new OqlNormalizeException('Duplicate class alias', $sQuery, $oJoinSpec->GetClassAliasDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class name', $sQuery, 0, 0, $sJoinClass);
|
||||
throw new OqlNormalizeException('Duplicate class name', $sQuery, $oJoinSpec->GetClassDetails());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,24 +815,24 @@ class DBObjectSearch
|
||||
$sPKeyDescriptor = $oRightField->GetName();
|
||||
if ($sPKeyDescriptor != 'id')
|
||||
{
|
||||
throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sQuery, 0, $oRightField->GetPosition(), $sPKeyDescriptor, array('id'));
|
||||
throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sQuery, $oRightField->GetNameDetails(), array('id'));
|
||||
}
|
||||
|
||||
$oResultFilter->m_aClasses[$sJoinClassAlias] = $sJoinClass;
|
||||
$aAliases[$sJoinClassAlias] = $sJoinClass;
|
||||
$aJoinItems[$sJoinClassAlias] = new DBObjectSearch($sJoinClass, $sJoinClassAlias);
|
||||
|
||||
if (!array_key_exists($sFromClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sQuery, 0, $oLeftField->GetPosition(), $sFromClass, array_keys($aJoinItems));
|
||||
throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sQuery, $oLeftField->GetParentDetails(), array_keys($aJoinItems));
|
||||
}
|
||||
if (!array_key_exists($sToClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sQuery, 0, $oRightField->GetPosition(), $sToClass, array_keys($aJoinItems));
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sQuery, $oRightField->GetParentDetails(), array_keys($aJoinItems));
|
||||
}
|
||||
$aExtKeys = array_keys(MetaModel::GetExternalKeys($oResultFilter->m_aClasses[$sFromClass]));
|
||||
$aExtKeys = array_keys(MetaModel::GetExternalKeys($aAliases[$sFromClass]));
|
||||
if (!in_array($sExtKeyAttCode, $aExtKeys))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sQuery, 0, $oLeftField->GetPosition(), $sExtKeyAttCode, $aExtKeys);
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sQuery, $oLeftField->GetNameDetails(), $aExtKeys);
|
||||
}
|
||||
|
||||
if ($sFromClass == $sJoinClassAlias)
|
||||
@@ -847,7 +849,7 @@ class DBObjectSearch
|
||||
$oConditionTree = $oOqlQuery->GetCondition();
|
||||
if ($oConditionTree instanceof Expression)
|
||||
{
|
||||
$oResultFilter->m_oSearchCondition = $oResultFilter->OQLExpressionToCondition($sQuery, $oConditionTree, $oResultFilter->m_aClasses);
|
||||
$oResultFilter->m_oSearchCondition = $oResultFilter->OQLExpressionToCondition($sQuery, $oConditionTree, $aAliases);
|
||||
}
|
||||
|
||||
return $oResultFilter;
|
||||
@@ -934,6 +936,10 @@ class DBObjectSearch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #@# todo - obsolete smoothly, first give the OQL version !
|
||||
// throw new CoreException('SibusQL has been obsoleted, please update your queries', array('sibusql'=>$sQuery, 'oql'=>$oFilter->ToOQL()));
|
||||
|
||||
return $oFilter;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ abstract class Expression
|
||||
// recursively builds an array of class => fieldname
|
||||
abstract public function ListRequiredFields();
|
||||
|
||||
abstract public function IsTrue();
|
||||
|
||||
public function RequiresField($sClass, $sFieldName)
|
||||
{
|
||||
// #@# todo - optimize : this is called quite often when building a single query !
|
||||
@@ -50,6 +52,8 @@ abstract class Expression
|
||||
|
||||
public function LogAnd($oExpr)
|
||||
{
|
||||
if ($this->IsTrue()) return clone $oExpr;
|
||||
if ($oExpr->IsTrue()) return clone $this;
|
||||
return new BinaryExpression($this, 'AND', $oExpr);
|
||||
}
|
||||
|
||||
@@ -57,7 +61,6 @@ abstract class Expression
|
||||
{
|
||||
return new BinaryExpression($this, 'OR', $oExpr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +93,16 @@ class BinaryExpression extends Expression
|
||||
$this->m_sOperator = $sOperator;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
if ($this->m_sOperator == 'AND')
|
||||
{
|
||||
if ($this->m_oLeftExpr->IsTrue() && $this->m_oLeftExpr->IsTrue()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetLeftExpr()
|
||||
{
|
||||
return $this->m_oLeftExpr;
|
||||
@@ -139,6 +152,12 @@ class UnaryExpression extends Expression
|
||||
$this->m_value = $value;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return ($this->m_value == 1);
|
||||
}
|
||||
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_value;
|
||||
@@ -179,6 +198,11 @@ class TrueExpression extends ScalarExpression
|
||||
{
|
||||
parent::__construct(1);
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class FieldExpression extends UnaryExpression
|
||||
@@ -194,6 +218,12 @@ class FieldExpression extends UnaryExpression
|
||||
$this->m_sName = $sName;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetParent() {return $this->m_sParent;}
|
||||
public function GetName() {return $this->m_sName;}
|
||||
|
||||
@@ -251,6 +281,12 @@ class ListExpression extends Expression
|
||||
$this->m_aExpressions = $aExpressions;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetItems()
|
||||
{
|
||||
return $this->m_aExpressions;
|
||||
@@ -300,6 +336,12 @@ class FunctionExpression extends Expression
|
||||
$this->m_aArgs = $aArgExpressions;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetVerb()
|
||||
{
|
||||
return $this->m_sVerb;
|
||||
@@ -353,6 +395,12 @@ class IntervalExpression extends Expression
|
||||
$this->m_sUnit = $sUnit;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_oValue;
|
||||
@@ -389,6 +437,12 @@ class CharConcatExpression extends Expression
|
||||
$this->m_aExpressions = $aExpressions;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetItems()
|
||||
{
|
||||
return $this->m_aExpressions;
|
||||
|
||||
@@ -107,10 +107,10 @@ class FilterPrivateKey extends FilterDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("pkey_field"));
|
||||
return array_merge(parent::ListExpectedParams(), array("id_field"));
|
||||
}
|
||||
|
||||
public function GetType() {return "PKey";}
|
||||
public function GetType() {return "PrivateKey";}
|
||||
public function GetTypeDesc() {return "Match against object identifier";}
|
||||
|
||||
public function GetLabel()
|
||||
@@ -139,7 +139,7 @@ class FilterPrivateKey extends FilterDefinition
|
||||
|
||||
public function GetFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sFieldName = $this->Get("pkey_field");
|
||||
$sFieldName = $this->Get("id_field");
|
||||
// #@# not obliged to quote... these are numbers !!!
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch($sOpCode)
|
||||
@@ -162,7 +162,7 @@ class FilterPrivateKey extends FilterDefinition
|
||||
}
|
||||
public function TemporaryGetSQLCol()
|
||||
{
|
||||
return $this->Get("pkey_field");
|
||||
return $this->Get("id_field");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ abstract class MetaModel
|
||||
final static public function DBGetTable($sClass, $sAttCode = null)
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
if (empty($sAttCode) || ($sAttCode == "pkey"))
|
||||
if (empty($sAttCode) || ($sAttCode == "id"))
|
||||
{
|
||||
$sTableRaw = self::$m_aClassParams[$sClass]["db_table"];
|
||||
if (empty($sTableRaw))
|
||||
@@ -801,19 +801,19 @@ abstract class MetaModel
|
||||
}
|
||||
}
|
||||
|
||||
// Add a 'pkey' filter
|
||||
// Add a 'id' filter
|
||||
//
|
||||
if (array_key_exists('pkey', self::$m_aAttribDefs[$sClass]))
|
||||
if (array_key_exists('id', self::$m_aAttribDefs[$sClass]))
|
||||
{
|
||||
trigger_error("Class $sClass, 'pkey' is a reserved keyword, it cannot be used as an attribute code", E_USER_ERROR);
|
||||
trigger_error("Class $sClass, 'id' is a reserved keyword, it cannot be used as an attribute code", E_USER_ERROR);
|
||||
}
|
||||
if (array_key_exists('pkey', self::$m_aFilterDefs[$sClass]))
|
||||
if (array_key_exists('id', self::$m_aFilterDefs[$sClass]))
|
||||
{
|
||||
trigger_error("Class $sClass, 'pkey' is a reserved keyword, it cannot be used as a filter code", E_USER_ERROR);
|
||||
trigger_error("Class $sClass, 'id' is a reserved keyword, it cannot be used as a filter code", E_USER_ERROR);
|
||||
}
|
||||
$oFilter = new FilterPrivateKey('pkey', array('pkey_field' => self::DBGetKey($sClass)));
|
||||
self::$m_aFilterDefs[$sClass]['pkey'] = $oFilter;
|
||||
self::$m_aFilterOrigins[$sClass]['pkey'] = $sClass;
|
||||
$oFilter = new FilterPrivateKey('id', array('id_field' => self::DBGetKey($sClass)));
|
||||
self::$m_aFilterDefs[$sClass]['id'] = $oFilter;
|
||||
self::$m_aFilterOrigins[$sClass]['id'] = $sClass;
|
||||
|
||||
// Add a 'class' attribute/filter to the root classes and their children
|
||||
//
|
||||
@@ -1270,8 +1270,8 @@ abstract class MetaModel
|
||||
|
||||
if (empty($aExpectedAtts) && $bIsOnQueriedClass)
|
||||
{
|
||||
// default to the whole list of attributes + the very std pkey/finalclass
|
||||
$aExpectedAtts['pkey'] = 'pkey';
|
||||
// default to the whole list of attributes + the very std id/finalclass
|
||||
$aExpectedAtts['id'] = 'id';
|
||||
foreach (self::GetAttributesList($sClass) as $sAttCode)
|
||||
{
|
||||
$aExpectedAtts[$sAttCode] = $sAttCode; // alias == attcode
|
||||
@@ -1337,7 +1337,6 @@ abstract class MetaModel
|
||||
// Filter on objects referencing me
|
||||
foreach ($oFilter->GetCriteria_ReferencedBy() as $sForeignClass => $aKeysAndFilters)
|
||||
{
|
||||
$sForeignClassAlias = $oFilter->GetClassAlias();
|
||||
foreach ($aKeysAndFilters as $sForeignKeyAttCode => $oForeignFilter)
|
||||
{
|
||||
$oForeignKeyAttDef = self::GetAttributeDef($sForeignClass, $sForeignKeyAttCode);
|
||||
@@ -1352,8 +1351,10 @@ abstract class MetaModel
|
||||
//self::DbgTrace($oSelectForeign->RenderSelect(array()));
|
||||
$oSelectForeign = self::MakeQuery($sGlobalTargetAlias, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oForeignFilter, $aExpAtts);
|
||||
|
||||
$sForeignKeyField = $oForeignKeyAttDef->GetSQLExpr();
|
||||
$oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyField);
|
||||
$sForeignClassAlias = $oForeignFilter->GetClassAlias();
|
||||
$sForeignKeyTable = $aTranslation[$sForeignClassAlias][$sForeignKeyAttCode][0];
|
||||
$sForeignKeyColumn = $aTranslation[$sForeignClassAlias][$sForeignKeyAttCode][1];
|
||||
$oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1415,7 +1416,7 @@ abstract class MetaModel
|
||||
$sTargetClass = $oFilter->GetClass();
|
||||
$sTargetAlias = $oFilter->GetClassAlias();
|
||||
$sTable = self::DBGetTable($sTableClass);
|
||||
$sTableAlias = self::GenerateUniqueAlias($aTableAliases, $sTable, $sTable);
|
||||
$sTableAlias = self::GenerateUniqueAlias($aTableAliases, $sTargetAlias.'_'.$sTable, $sTable);
|
||||
|
||||
$bIsOnQueriedClass = ($sTargetAlias == $sGlobalTargetAlias);
|
||||
|
||||
@@ -1432,12 +1433,12 @@ abstract class MetaModel
|
||||
//
|
||||
if ($bIsOnQueriedClass)
|
||||
{
|
||||
$aSelect[$aExpectedAtts['pkey']] = new FieldExpression(self::DBGetKey($sTableClass), $sTableAlias);
|
||||
$aSelect[$aExpectedAtts['id']] = new FieldExpression(self::DBGetKey($sTableClass), $sTableAlias);
|
||||
}
|
||||
// We need one pkey to be the key, let's take the one corresponding to the leaf
|
||||
if ($sTableClass == $sTargetClass)
|
||||
{
|
||||
$aTranslation[$sTargetAlias]['pkey'] = array($sTableAlias, self::DBGetKey($sTableClass));
|
||||
$aTranslation[$sTargetAlias]['id'] = array($sTableAlias, self::DBGetKey($sTableClass));
|
||||
}
|
||||
|
||||
// 1/b - Get the other attributes
|
||||
@@ -1481,10 +1482,6 @@ abstract class MetaModel
|
||||
//
|
||||
foreach(self::$m_aFilterDefs[$sTargetClass] as $sFltCode => $oFltAtt)
|
||||
{
|
||||
// Skip pkey
|
||||
// no, this is a bug now! ?!?!?
|
||||
// if ($sFltCode == 'pkey') continue;
|
||||
|
||||
// Skip this filter if not defined in this table
|
||||
if (self::$m_aFilterOrigins[$sTargetClass][$sFltCode] != $sTableClass) continue;
|
||||
|
||||
@@ -1579,23 +1576,25 @@ abstract class MetaModel
|
||||
return $oSelectBase;
|
||||
}
|
||||
|
||||
public static function GenerateUniqueAlias(&$aAliases, $sNewName, $sRealName, $iTentative = 0)
|
||||
public static function GenerateUniqueAlias(&$aAliases, $sNewName, $sRealName)
|
||||
{
|
||||
// Algo: Build an alias, then check it amongst the contents of $aAliases
|
||||
|
||||
if ($iTentative == 0) $sProposedAlias = $sNewName;
|
||||
else $sProposedAlias = $sNewName.$iTentative;
|
||||
|
||||
foreach($aAliases as $sAlias=>$sNameWeDontCare)
|
||||
if (!array_key_exists($sNewName, $aAliases))
|
||||
{
|
||||
// If the name is already used, then recursively try to get another one
|
||||
if ($sProposedAlias == $sAlias) return self::GenerateUniqueAlias($aAliases, $sNewName, $sRealName, $iTentative + 1);
|
||||
$aAliases[$sNewName] = $sRealName;
|
||||
return $sNewName;
|
||||
}
|
||||
|
||||
// The proposed alias has been proven to be unique
|
||||
// Record it and return its value
|
||||
$aAliases[$sProposedAlias] = $sRealName;
|
||||
return $sProposedAlias;
|
||||
|
||||
for ($i = 1 ; $i < 100 ; $i++)
|
||||
{
|
||||
$sAnAlias = $sNewName.$i;
|
||||
if (!array_key_exists($sAnAlias, $aAliases))
|
||||
{
|
||||
// Create that new alias
|
||||
$aAliases[$sAnAlias] = $sRealName;
|
||||
return $sAnAlias;
|
||||
}
|
||||
}
|
||||
throw new CoreException('Failed to create an alias', array('aliases' => $aAliases, 'new'=>$sNewName));
|
||||
}
|
||||
|
||||
public static function CheckDefinitions()
|
||||
@@ -1630,7 +1629,7 @@ abstract class MetaModel
|
||||
if (empty($sNameAttCode))
|
||||
{
|
||||
// let's try this !!!
|
||||
// $aErrors[$sClass][] = "Missing value for name definition: the framework will (should...) replace it by the pkey";
|
||||
// $aErrors[$sClass][] = "Missing value for name definition: the framework will (should...) replace it by the id";
|
||||
// $aSugFix[$sClass][] = "Expecting a value in ".implode(", ", self::GetAttributesList($sClass));
|
||||
}
|
||||
else if(!self::IsValidAttCode($sClass, $sNameAttCode))
|
||||
@@ -2013,7 +2012,7 @@ abstract class MetaModel
|
||||
{
|
||||
$sSelWrongRecs .= " AND maintable.`$sKeyField` NOT IN ('".implode("', '", $aPlannedDel[$sTable])."')";
|
||||
}
|
||||
$aWrongRecords = CMDBSource::QueryToCol($sSelWrongRecs, "pkey");
|
||||
$aWrongRecords = CMDBSource::QueryToCol($sSelWrongRecs, "id");
|
||||
if (count($aWrongRecords) == 0) return;
|
||||
|
||||
if (!array_key_exists($sRootClass, $aErrorsAndFixes)) $aErrorsAndFixes[$sRootClass] = array();
|
||||
@@ -2069,7 +2068,7 @@ abstract class MetaModel
|
||||
// skip the current table
|
||||
if ($sFriendTable == $sTable) continue;
|
||||
|
||||
$sFindRelatedRec = "SELECT DISTINCT maintable.`$sFriendKey` AS pkey FROM `$sFriendTable` AS maintable WHERE maintable.`$sFriendKey` IN ($sDeleteKeys)";
|
||||
$sFindRelatedRec = "SELECT DISTINCT maintable.`$sFriendKey` AS id FROM `$sFriendTable` AS maintable WHERE maintable.`$sFriendKey` IN ($sDeleteKeys)";
|
||||
self::DBCheckIntegrity_Check2Delete($sFindRelatedRec, "Cascading deletion of record in friend table `<em>$sTable</em>`", $sFriendClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel, true);
|
||||
}
|
||||
}
|
||||
@@ -2085,7 +2084,7 @@ abstract class MetaModel
|
||||
{
|
||||
$sSelWrongRecs .= " AND maintable.`$sKeyField` NOT IN ('".implode("', '", $aPlannedDel[$sTable])."')";
|
||||
}
|
||||
$aWrongRecords = CMDBSource::QueryToCol($sSelWrongRecs, "pkey");
|
||||
$aWrongRecords = CMDBSource::QueryToCol($sSelWrongRecs, "id");
|
||||
if (count($aWrongRecords) == 0) return;
|
||||
|
||||
if (!array_key_exists($sRootClass, $aErrorsAndFixes)) $aErrorsAndFixes[$sRootClass] = array();
|
||||
@@ -2150,7 +2149,7 @@ abstract class MetaModel
|
||||
$aAllowedValues = self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL);
|
||||
$sAllowedValues = implode(",", CMDBSource::Quote($aAllowedValues, true));
|
||||
|
||||
$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS pkey FROM `$sTable` AS maintable WHERE `$sFinalClassField` NOT IN ($sAllowedValues)";
|
||||
$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` AS maintable WHERE `$sFinalClassField` NOT IN ($sAllowedValues)";
|
||||
self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "final class (field `<em>$sFinalClassField</em>`) is wrong (expected a value in {".$sAllowedValues."})", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
|
||||
}
|
||||
|
||||
@@ -2168,13 +2167,13 @@ abstract class MetaModel
|
||||
// Check that any record found here has its counterpart in the root table
|
||||
// and which refers to a child class
|
||||
//
|
||||
$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS pkey FROM `$sTable` as maintable LEFT JOIN `$sRootTable` ON maintable.`$sKeyField` = `$sRootTable`.`$sRootKey` AND `$sRootTable`.`$sFinalClassField` IN ($sExpectedClasses) WHERE `$sRootTable`.`$sRootKey` IS NULL";
|
||||
$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` as maintable LEFT JOIN `$sRootTable` ON maintable.`$sKeyField` = `$sRootTable`.`$sRootKey` AND `$sRootTable`.`$sFinalClassField` IN ($sExpectedClasses) WHERE `$sRootTable`.`$sRootKey` IS NULL";
|
||||
self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Found a record in `<em>$sTable</em>`, but no counterpart in root table `<em>$sRootTable</em>` (inc. records pointing to a class in {".$sExpectedClasses."})", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
|
||||
|
||||
// Check that any record found in the root table and referring to a child class
|
||||
// has its counterpart here (detect orphan nodes -root or in the middle of the hierarchy)
|
||||
//
|
||||
$sSelWrongRecs = "SELECT DISTINCT maintable.`$sRootKey` AS pkey FROM `$sRootTable` AS maintable LEFT JOIN `$sTable` ON maintable.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sKeyField` IS NULL AND maintable.`$sFinalClassField` IN ($sExpectedClasses)";
|
||||
$sSelWrongRecs = "SELECT DISTINCT maintable.`$sRootKey` AS id FROM `$sRootTable` AS maintable LEFT JOIN `$sTable` ON maintable.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sKeyField` IS NULL AND maintable.`$sFinalClassField` IN ($sExpectedClasses)";
|
||||
self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Found a record in root table `<em>$sRootTable</em>`, but no counterpart in table `<em>$sTable</em>` (root records pointing to a class in {".$sExpectedClasses."})", $sRootClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
|
||||
}
|
||||
|
||||
@@ -2194,7 +2193,7 @@ abstract class MetaModel
|
||||
$sExtKeyField = $oAttDef->GetSQLExpr();
|
||||
|
||||
// Note: a class/table may have an external key on itself
|
||||
$sSelBase = "SELECT DISTINCT maintable.`$sKeyField` AS pkey, maintable.`$sExtKeyField` AS extkey FROM `$sTable` AS maintable LEFT JOIN `$sRemoteTable` ON maintable.`$sExtKeyField` = `$sRemoteTable`.`$sRemoteKey`";
|
||||
$sSelBase = "SELECT DISTINCT maintable.`$sKeyField` AS id, maintable.`$sExtKeyField` AS extkey FROM `$sTable` AS maintable LEFT JOIN `$sRemoteTable` ON maintable.`$sExtKeyField` = `$sRemoteTable`.`$sRemoteKey`";
|
||||
|
||||
$sSelWrongRecs = $sSelBase." WHERE `$sRemoteTable`.`$sRemoteKey` IS NULL";
|
||||
if ($oAttDef->IsNullAllowed())
|
||||
@@ -2239,7 +2238,7 @@ abstract class MetaModel
|
||||
|
||||
$sMyAttributeField = $oAttDef->GetSQLExpr();
|
||||
$sDefaultValue = $oAttDef->GetDefaultValue();
|
||||
$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS pkey FROM `$sTable` AS maintable WHERE maintable.`$sMyAttributeField` NOT IN ($sExpectedValues)";
|
||||
$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` AS maintable WHERE maintable.`$sMyAttributeField` NOT IN ($sExpectedValues)";
|
||||
self::DBCheckIntegrity_Check2Update($sSelWrongRecs, "Record having a column ('<em>$sAttCode</em>') with an unexpected value", $sMyAttributeField, CMDBSource::Quote($sDefaultValue), $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
|
||||
}
|
||||
}
|
||||
@@ -2464,7 +2463,7 @@ abstract class MetaModel
|
||||
public static function MakeSingleRow($sClass, $iKey)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oFilter->AddCondition('pkey', $iKey, '=');
|
||||
$oFilter->AddCondition('id', $iKey, '=');
|
||||
|
||||
$sSQL = self::MakeSelectQuery($oFilter);
|
||||
//echo "$sSQL</br>\n";
|
||||
@@ -2496,7 +2495,7 @@ abstract class MetaModel
|
||||
// @#@ possible improvement: check that the class is valid !
|
||||
$sRootClass = self::GetRootClass($sClass);
|
||||
$sFinalClassField = self::DBGetClassField($sRootClass);
|
||||
trigger_error("Empty class name for object $sClass::{$aRow["pkey"]} (root class '$sRootClass', field '{$sFinalClassField}' is empty)", E_USER_ERROR);
|
||||
trigger_error("Empty class name for object $sClass::{$aRow["id"]} (root class '$sRootClass', field '{$sFinalClassField}' is empty)", E_USER_ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,14 +41,25 @@ join_item(A) ::= JOIN class_name(X) ON join_condition(C).
|
||||
|
||||
join_condition(A) ::= field_id(X) EQ field_id(Y). { A = new BinaryOqlExpression(X, '=', Y); }
|
||||
|
||||
condition(A) ::= expression(X). { A = X; }
|
||||
condition(A) ::= expression_prio4(X). { A = X; }
|
||||
|
||||
expression(A) ::= PAR_OPEN expression(X) PAR_CLOSE. { A = X; }
|
||||
expression(A) ::= expression(X) operator(Y) expression(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
expression(A) ::= scalar(X). { A=X; }
|
||||
expression(A) ::= field_id(X). { A = X; }
|
||||
expression(A) ::= expression(X) list_operator(Y) list(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
expression(A) ::= func_name(X) PAR_OPEN arg_list(Y) PAR_CLOSE. { A = new FunctionOqlExpression(X, Y); }
|
||||
expression_basic(A) ::= scalar(X). { A = X; }
|
||||
expression_basic(A) ::= field_id(X). { A = X; }
|
||||
expression_basic(A) ::= func_name(X) PAR_OPEN arg_list(Y) PAR_CLOSE. { A = new FunctionOqlExpression(X, Y); }
|
||||
expression_basic(A) ::= PAR_OPEN expression_prio4(X) PAR_CLOSE. { A = X; }
|
||||
expression_basic(A) ::= expression_basic(X) list_operator(Y) list(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio1(A) ::= expression_basic(X). { A = X; }
|
||||
expression_prio1(A) ::= expression_prio1(X) operator1(Y) expression_basic(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio2(A) ::= expression_prio1(X). { A = X; }
|
||||
expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio3(A) ::= expression_prio2(X). { A = X; }
|
||||
expression_prio3(A) ::= expression_prio3(X) operator3(Y) expression_prio2(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio4(A) ::= expression_prio3(X). { A = X; }
|
||||
expression_prio4(A) ::= expression_prio4(X) operator4(Y) expression_prio3(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
|
||||
list(A) ::= PAR_OPEN scalar_list(X) PAR_CLOSE. {
|
||||
@@ -72,8 +83,8 @@ arg_list(A) ::= arg_list(L) COMA argument(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
argument(A) ::= expression(X). { A = X; }
|
||||
argument(A) ::= INTERVAL expression(X) interval_unit(Y). { A = new IntervalOqlExpression(X, Y); }
|
||||
argument(A) ::= expression_prio4(X). { A = X; }
|
||||
argument(A) ::= INTERVAL expression_prio4(X) interval_unit(Y). { A = new IntervalOqlExpression(X, Y); }
|
||||
|
||||
interval_unit(A) ::= F_DAY(X). { A = X; }
|
||||
interval_unit(A) ::= F_MONTH(X). { A = X; }
|
||||
@@ -85,40 +96,42 @@ scalar(A) ::= str_scalar(X). { A = X; }
|
||||
num_scalar(A) ::= num_value(X). { A = new ScalarOqlExpression(X); }
|
||||
str_scalar(A) ::= str_value(X). { A = new ScalarOqlExpression(X); }
|
||||
|
||||
field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression($this->m_iCol, Y, X); }
|
||||
class_name(A) ::= name(X). {A=X;}
|
||||
field_id(A) ::= name(X). { A = new FieldOqlExpression(X); }
|
||||
field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); }
|
||||
class_name(A) ::= name(X). { A=X; }
|
||||
|
||||
name(A) ::= NAME(X). {
|
||||
if (X[0] == '`')
|
||||
{
|
||||
A = substr(X, 1, strlen(X) - 2);
|
||||
$name = substr(X, 1, strlen(X) - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
A = X;
|
||||
$name = X;
|
||||
}
|
||||
A = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
|
||||
num_value(A) ::= NUMVAL(X). {A=X;}
|
||||
str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
|
||||
|
||||
operator(A) ::= log_operator(X). {A=X;}
|
||||
operator(A) ::= num_operator(X). {A=X;}
|
||||
operator(A) ::= str_operator(X). {A=X;}
|
||||
operator(A) ::= EQ(X). {A=X;}
|
||||
operator(A) ::= NOT_EQ(X). {A=X;}
|
||||
|
||||
log_operator(A) ::= LOG_AND(X). {A=X;}
|
||||
log_operator(A) ::= LOG_OR(X). {A=X;}
|
||||
operator1(A) ::= num_operator1(X). {A=X;}
|
||||
operator2(A) ::= num_operator2(X). {A=X;}
|
||||
operator2(A) ::= str_operator(X). {A=X;}
|
||||
operator2(A) ::= EQ(X). {A=X;}
|
||||
operator2(A) ::= NOT_EQ(X). {A=X;}
|
||||
operator3(A) ::= LOG_AND(X). {A=X;}
|
||||
operator4(A) ::= LOG_OR(X). {A=X;}
|
||||
|
||||
num_operator(A) ::= GT(X). {A=X;}
|
||||
num_operator(A) ::= LT(X). {A=X;}
|
||||
num_operator(A) ::= GE(X). {A=X;}
|
||||
num_operator(A) ::= LE(X). {A=X;}
|
||||
num_operator(A) ::= MATH_DIV(X). {A=X;}
|
||||
num_operator(A) ::= MATH_MULT(X). {A=X;}
|
||||
num_operator(A) ::= MATH_PLUS(X). {A=X;}
|
||||
num_operator(A) ::= MATH_MINUS(X). {A=X;}
|
||||
num_operator1(A) ::= MATH_DIV(X). {A=X;}
|
||||
num_operator1(A) ::= MATH_MULT(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_PLUS(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_MINUS(X). {A=X;}
|
||||
num_operator2(A) ::= GT(X). {A=X;}
|
||||
num_operator2(A) ::= LT(X). {A=X;}
|
||||
num_operator2(A) ::= GE(X). {A=X;}
|
||||
num_operator2(A) ::= LE(X). {A=X;}
|
||||
|
||||
str_operator(A) ::= LIKE(X). {A=X;}
|
||||
str_operator(A) ::= NOT_LIKE(X). {A=X;}
|
||||
@@ -175,18 +188,21 @@ class OQLParser extends OQLParserRaw
|
||||
// Data used when an exception is raised
|
||||
protected $m_iLine; // still not used
|
||||
protected $m_iCol;
|
||||
protected $m_iColPrev; // this is the interesting one, because the parser will reduce on the next token
|
||||
protected $m_sSourceQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_iLine = 0;
|
||||
$this->m_iCol = 0;
|
||||
$this->m_iColPrev = 0;
|
||||
$this->m_sSourceQuery = $sQuery;
|
||||
// no constructor - parent::__construct();
|
||||
}
|
||||
|
||||
public function doParse($token, $value, $iCurrPosition = 0)
|
||||
{
|
||||
$this->m_iColPrev = $this->m_iCol;
|
||||
$this->m_iCol = $iCurrPosition;
|
||||
|
||||
return parent::DoParse($token, $value);
|
||||
|
||||
@@ -11,26 +11,66 @@ class OQLException extends CoreException
|
||||
$this->m_sUnexpected = $sUnexpected;
|
||||
$this->m_aExpecting = $aExpecting;
|
||||
|
||||
if (is_null($this->m_aExpecting))
|
||||
if (is_null($this->m_aExpecting) || (count($this->m_aExpecting) == 0))
|
||||
{
|
||||
$sMessage = "$sIssue - found '$sUnexpected' at $iCol in '$sInput'";
|
||||
$sMessage = "$sIssue - found '{$this->m_sUnexpected}' at $iCol in '$sInput'";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sExpectations = '{'.implode(', ', $aExpecting).'}';
|
||||
$sMessage = "$sIssue - found '$sUnexpected' at $iCol in '$sInput', expecting $sExpectations";
|
||||
$sExpectations = '{'.implode(', ', $this->m_aExpecting).'}';
|
||||
$sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting);
|
||||
$sMessage = "$sIssue - found '{$this->m_sUnexpected}' at $iCol in '$sInput', expecting $sExpectations, I would suggest to use '$sSuggest'";
|
||||
}
|
||||
|
||||
// make sure everything is assigned properly
|
||||
parent::__construct($sMessage, 0);
|
||||
}
|
||||
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
{
|
||||
$sRet = htmlentities($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: ");
|
||||
$sRet .= htmlentities(substr($this->m_sInput, 0, $this->m_iCol));
|
||||
$sRet .= $sHighlightHtmlBegin.htmlentities(substr($this->m_sInput, $this->m_iCol, strlen($this->m_sUnexpected))).$sHighlightHtmlEnd;
|
||||
$sRet .= htmlentities(substr($this->m_sInput, $this->m_iCol + strlen($this->m_sUnexpected)));
|
||||
|
||||
if (!is_null($this->m_aExpecting) && (count($this->m_aExpecting) > 0))
|
||||
{
|
||||
$sExpectations = '{'.implode(', ', $this->m_aExpecting).'}';
|
||||
$sRet .= ", expecting ".htmlentities($sExpectations);
|
||||
$sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting);
|
||||
if (strlen($sSuggest) > 0)
|
||||
{
|
||||
$sRet .= ", I would suggest to use '$sHighlightHtmlBegin".htmlentities($sSuggest)."$sHighlightHtmlEnd'";
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
static protected function FindClosestString($sInput, $aDictionary)
|
||||
{
|
||||
// no shortest distance found, yet
|
||||
$fShortest = -1;
|
||||
$sRet = '';
|
||||
|
||||
// loop through words to find the closest
|
||||
foreach ($aDictionary as $sSuggestion)
|
||||
{
|
||||
// calculate the distance between the input string and the suggested one
|
||||
$fDist = levenshtein($sInput, $sSuggestion);
|
||||
if ($fDist == 0)
|
||||
{
|
||||
// Exact match
|
||||
return $sSuggestion;
|
||||
}
|
||||
|
||||
if ($fShortest < 0 || ($fDist < 4 && $fDist <= $fShortest))
|
||||
{
|
||||
// set the closest match, and shortest distance
|
||||
$sRet = $sSuggestion;
|
||||
$fShortest = $fDist;
|
||||
}
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
class OqlNormalizeException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, OqlName $oName, $aExpecting = null)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, 0, $oName->GetPos(), $oName->GetValue(), $aExpecting);
|
||||
}
|
||||
}
|
||||
|
||||
class OqlInterpreterException extends OQLException
|
||||
|
||||
@@ -1,30 +1,70 @@
|
||||
<?
|
||||
|
||||
// Position a string within an OQL query
|
||||
// This is a must if we want to be able to pinpoint an error at any stage of the query interpretation
|
||||
// In particular, the normalization phase requires this
|
||||
class OqlName
|
||||
{
|
||||
protected $m_sValue;
|
||||
protected $m_iPos;
|
||||
|
||||
public function __construct($sValue, $iPos)
|
||||
{
|
||||
$this->m_iPos = $iPos;
|
||||
$this->m_sValue = $sValue;
|
||||
}
|
||||
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_sValue;
|
||||
}
|
||||
|
||||
public function GetPos()
|
||||
{
|
||||
return $this->m_iPos;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->m_sValue;
|
||||
}
|
||||
}
|
||||
|
||||
class OqlJoinSpec
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sClassAlias;
|
||||
protected $m_oClass;
|
||||
protected $m_oClassAlias;
|
||||
protected $m_oLeftField;
|
||||
protected $m_oRightField;
|
||||
|
||||
protected $m_oNextJoinspec;
|
||||
|
||||
public function __construct($sClass, $sClassAlias, BinaryExpression $oExpression)
|
||||
public function __construct($oClass, $oClassAlias, BinaryExpression $oExpression)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sClassAlias = $sClassAlias;
|
||||
$this->m_oClass = $oClass;
|
||||
$this->m_oClassAlias = $oClassAlias;
|
||||
$this->m_oLeftField = $oExpression->GetLeftExpr();
|
||||
$this->m_oRightField = $oExpression->GetRightExpr();
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_sClass;
|
||||
return $this->m_oClass->GetValue();
|
||||
}
|
||||
public function GetClassAlias()
|
||||
{
|
||||
return $this->m_sClassAlias;
|
||||
return $this->m_oClassAlias->GetValue();
|
||||
}
|
||||
|
||||
public function GetClassDetails()
|
||||
{
|
||||
return $this->m_oClass;
|
||||
}
|
||||
public function GetClassAliasDetails()
|
||||
{
|
||||
return $this->m_oClassAlias;
|
||||
}
|
||||
|
||||
public function GetLeftField()
|
||||
{
|
||||
return $this->m_oLeftField;
|
||||
@@ -45,18 +85,30 @@ class ScalarOqlExpression extends ScalarExpression
|
||||
|
||||
class FieldOqlExpression extends FieldExpression
|
||||
{
|
||||
protected $m_iPosition; // position in the source string
|
||||
|
||||
public function __construct($iPosition, $sName, $sParent = '')
|
||||
protected $m_oParent;
|
||||
protected $m_oName;
|
||||
|
||||
public function __construct($oName, $oParent = null)
|
||||
{
|
||||
$this->m_iPosition = $iPosition;
|
||||
parent::__construct($sName, $sParent);
|
||||
if (is_null($oParent))
|
||||
{
|
||||
$oParent = new OqlName('', 0);
|
||||
}
|
||||
$this->m_oParent = $oParent;
|
||||
$this->m_oName = $oName;
|
||||
|
||||
parent::__construct($oName->GetValue(), $oParent->GetValue());
|
||||
}
|
||||
|
||||
public function GetPosition()
|
||||
public function GetParentDetails()
|
||||
{
|
||||
return $this->m_iPosition;
|
||||
}
|
||||
return $this->m_oParent;
|
||||
}
|
||||
|
||||
public function GetNameDetails()
|
||||
{
|
||||
return $this->m_oName;
|
||||
}
|
||||
}
|
||||
|
||||
class ListOqlExpression extends ListExpression
|
||||
@@ -72,27 +124,37 @@ class IntervalOqlExpression extends IntervalExpression
|
||||
}
|
||||
class OqlQuery
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sClassAlias;
|
||||
protected $m_oClass;
|
||||
protected $m_oClassAlias;
|
||||
protected $m_aJoins; // array of OqlJoinSpec
|
||||
protected $m_oCondition; // condition tree (expressions)
|
||||
|
||||
public function __construct($sClass, $sClassAlias = '', $oCondition = null, $aJoins = null)
|
||||
public function __construct($oClass, $oClassAlias = '', $oCondition = null, $aJoins = null)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sClassAlias = $sClassAlias;
|
||||
$this->m_oClass = $oClass;
|
||||
$this->m_oClassAlias = $oClassAlias;
|
||||
$this->m_aJoins = $aJoins;
|
||||
$this->m_oCondition = $oCondition;
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_sClass;
|
||||
return $this->m_oClass->GetValue();
|
||||
}
|
||||
public function GetClassAlias()
|
||||
{
|
||||
return $this->m_sClassAlias;
|
||||
return $this->m_oClassAlias->GetValue();
|
||||
}
|
||||
|
||||
public function GetClassDetails()
|
||||
{
|
||||
return $this->m_oClass;
|
||||
}
|
||||
public function GetClassAliasDetails()
|
||||
{
|
||||
return $this->m_oClassAlias;
|
||||
}
|
||||
|
||||
public function GetJoins()
|
||||
{
|
||||
return $this->m_aJoins;
|
||||
|
||||
@@ -104,8 +104,9 @@ class SQLQuery
|
||||
$oSQLQuery = $aJoinInfo["select"];
|
||||
$sLeftField = $aJoinInfo["leftfield"];
|
||||
$sRightField = $aJoinInfo["rightfield"];
|
||||
$sRightTableAlias = $aJoinInfo["righttablealias"];
|
||||
|
||||
echo "<li>Join '$sJoinType', $sLeftField, $sRightField".$oSQLQuery->DisplayHtml()."</li>\n";
|
||||
echo "<li>Join '$sJoinType', $sLeftField, $sRightTableAlias.$sRightField".$oSQLQuery->DisplayHtml()."</li>\n";
|
||||
}
|
||||
echo "</ul>";
|
||||
}
|
||||
@@ -131,28 +132,35 @@ class SQLQuery
|
||||
$this->m_oConditionExpr->LogAnd($oConditionExpr);
|
||||
}
|
||||
|
||||
private function AddJoin($sJoinType, $oSQLQuery, $sLeftField, $sRightField)
|
||||
private function AddJoin($sJoinType, $oSQLQuery, $sLeftField, $sRightField, $sRightTableAlias = '')
|
||||
{
|
||||
assert((get_class($oSQLQuery) == __CLASS__) || is_subclass_of($oSQLQuery, __CLASS__));
|
||||
if (!CMDBSource::IsField($this->m_sTable, $sLeftField))
|
||||
{
|
||||
trigger_error("Unknown field '$sLeftField' in table '".$this->m_sTable, E_USER_ERROR);
|
||||
}
|
||||
if (!CMDBSource::IsField($oSQLQuery->m_sTable, $sRightField))
|
||||
if (empty($sRightTableAlias))
|
||||
{
|
||||
trigger_error("Unknown field '$sRightField' in table '".$oSQLQuery->m_sTable."'", E_USER_ERROR);
|
||||
$sRightTableAlias = $oSQLQuery->m_sTableAlias;
|
||||
}
|
||||
// #@# Could not be verified here because the namespace is unknown - do we need to check it there?
|
||||
//
|
||||
// if (!CMDBSource::IsField($sRightTable, $sRightField))
|
||||
// {
|
||||
// trigger_error("Unknown field '$sRightField' in table '".$sRightTable."'", E_USER_ERROR);
|
||||
// }
|
||||
|
||||
$this->m_aJoinSelects[] = array(
|
||||
"jointype" => $sJoinType,
|
||||
"select" => $oSQLQuery,
|
||||
"leftfield" => $sLeftField,
|
||||
"rightfield" => $sRightField
|
||||
"rightfield" => $sRightField,
|
||||
"righttablealias" => $sRightTableAlias
|
||||
);
|
||||
}
|
||||
public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField)
|
||||
public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField, $sRigthtTable = '')
|
||||
{
|
||||
$this->AddJoin("inner", $oSQLQuery, $sLeftField, $sRightField);
|
||||
$this->AddJoin("inner", $oSQLQuery, $sLeftField, $sRightField, $sRigthtTable);
|
||||
}
|
||||
public function AddLeftJoin($oSQLQuery, $sLeftField, $sRightField)
|
||||
{
|
||||
@@ -259,9 +267,9 @@ class SQLQuery
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
break;
|
||||
case "inner":
|
||||
$sFrom .= " INNER JOIN `".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ON ".$aJoinInfo["joincondition"];
|
||||
$sFrom .= " INNER JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
$sFrom .= ") ON ".$aJoinInfo["joincondition"];
|
||||
break;
|
||||
case "left":
|
||||
$sFrom .= " LEFT JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
@@ -339,7 +347,7 @@ class SQLQuery
|
||||
return $sTableAlias;
|
||||
}
|
||||
|
||||
private function privRenderSingleTable(&$aFrom, &$aFields, &$aDelTables, &$aSetValues, $sJoinType = "first", $sCallerAlias = "", $sLeftField = "", $sRightField = "")
|
||||
private function privRenderSingleTable(&$aFrom, &$aFields, &$aDelTables, &$aSetValues, $sJoinType = 'first', $sCallerAlias = '', $sLeftField = '', $sRightField = '', $sRightTableAlias = '')
|
||||
{
|
||||
$aActualTableFields = CMDBSource::GetTableFieldsList($this->m_sTable);
|
||||
|
||||
@@ -347,7 +355,11 @@ class SQLQuery
|
||||
|
||||
// Handle the various kinds of join (or first table in the list)
|
||||
//
|
||||
$sJoinCond = "`$sCallerAlias`.`$sLeftField` = `{$this->m_sTableAlias}`.`$sRightField`";
|
||||
if (empty($sRightTableAlias))
|
||||
{
|
||||
$sRightTableAlias = $this->m_sTableAlias;
|
||||
}
|
||||
$sJoinCond = "`$sCallerAlias`.`$sLeftField` = `$sRightTableAlias`.`$sRightField`";
|
||||
switch ($sJoinType)
|
||||
{
|
||||
case "first":
|
||||
@@ -355,6 +367,7 @@ class SQLQuery
|
||||
break;
|
||||
case "inner":
|
||||
case "left":
|
||||
// table or tablealias ???
|
||||
$aFrom[$this->m_sTableAlias] = array("jointype"=>$sJoinType, "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond");
|
||||
break;
|
||||
}
|
||||
@@ -386,8 +399,9 @@ class SQLQuery
|
||||
$oRightSelect = $aJoinData["select"];
|
||||
$sLeftField = $aJoinData["leftfield"];
|
||||
$sRightField = $aJoinData["rightfield"];
|
||||
$sRightTableAlias = $aJoinData["righttablealias"];
|
||||
|
||||
$sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $sJoinType, $this->m_sTableAlias, $sLeftField, $sRightField);
|
||||
$sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $sJoinType, $this->m_sTableAlias, $sLeftField, $sRightField, $sRightTableAlias);
|
||||
}
|
||||
$aFrom[$this->m_sTableAlias]['subfrom'] = $aTempFrom;
|
||||
|
||||
|
||||
@@ -337,15 +337,18 @@ abstract class TestBizModel extends TestHandler
|
||||
static protected function show_list($oObjectSet)
|
||||
{
|
||||
$oObjectSet->Rewind();
|
||||
$aData = array();
|
||||
while ($oItem = $oObjectSet->Fetch())
|
||||
{
|
||||
$aValues = array();
|
||||
foreach(MetaModel::GetAttributesList(get_class($oItem)) as $sAttCode)
|
||||
{
|
||||
$aValues[] = $oItem->GetAsHTML($sAttCode);
|
||||
$aValues[$sAttCode] = $oItem->GetAsHTML($sAttCode);
|
||||
}
|
||||
echo $oItem->GetKey()." => ".implode(", ", $aValues)."</br>\n";
|
||||
//echo $oItem->GetKey()." => ".implode(", ", $aValues)."</br>\n";
|
||||
$aData[] = $aValues;
|
||||
}
|
||||
echo MyHelpers::make_table_from_assoc_array($aData);
|
||||
}
|
||||
|
||||
static protected function search_and_show_list(DBObjectSearch $oMyFilter)
|
||||
|
||||
@@ -42,7 +42,7 @@ function ShowClass($sClass, $sBaseArgs)
|
||||
$aProps["Subclasses (children + pure PHP)"] = sexyclasslist(MetaModel::GetSubclasses($sClass), $sBaseArgs);
|
||||
|
||||
$aProps["Description"] = MetaModel::GetClassDescription($sClass);
|
||||
$aProps["Autoincrement pkey?"] = MetaModel::IsAutoIncrementKey($sClass);
|
||||
$aProps["Autoincrement id?"] = MetaModel::IsAutoIncrementKey($sClass);
|
||||
$aProps["Key label"] = MetaModel::GetKeyLabel($sClass);
|
||||
$aProps["Name attribute"] = MetaModel::GetNameAttributeCode($sClass);
|
||||
$aProps["Reconciliation keys"] = implode(", ", MetaModel::GetReconcKeys($sClass));
|
||||
@@ -188,7 +188,8 @@ function DebugQuery($sConfigFile)
|
||||
|
||||
echo "<h1>Follow up the query build</h1>\n";
|
||||
MetaModel::StartDebugQuery();
|
||||
$oFlt = DBObjectSearch::FromSibuSQL($sQuery);
|
||||
$oFlt = DBObjectSearch::FromOQL($sQuery);
|
||||
echo "<p>To OQL: ".$oFlt->ToOQL()."</p>";
|
||||
$sSQL = MetaModel::MakeSelectQuery($oFlt);
|
||||
MetaModel::StopDebugQuery();
|
||||
|
||||
|
||||
@@ -706,6 +706,7 @@ class TestQueriesOnFarm extends TestBizModel
|
||||
throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)");
|
||||
return false;
|
||||
}
|
||||
echo "<p>To OQL: ".$oMyFilter->ToOQL()."</p>";
|
||||
|
||||
$this->search_and_show_list($oMyFilter);
|
||||
|
||||
@@ -721,25 +722,21 @@ class TestQueriesOnFarm extends TestBizModel
|
||||
$oFilter2 = DBObjectSearch::unserialize($sSerialize);
|
||||
try
|
||||
{
|
||||
$sQuery2 = MetaModel::MakeSelectQuery($oFilter2);
|
||||
$sQuery2 = MetaModel::MakeSelectQuery($oFilter2);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
echo "<p>Could not compute the query after unserialize</p>\n";
|
||||
echo "<p>Query 1: $sQuery1</p>\n";
|
||||
MyHelpers::var_dump_html($oMyFilter, true);
|
||||
echo "<p>Query 2: FAILED</p>\n";
|
||||
MyHelpers::var_dump_html($oFilter2, true);
|
||||
MyHelpers::var_cmp_html($oMyFilter, $oFilter2);
|
||||
throw $e;
|
||||
}
|
||||
//if ($oFilter2 != $oMyFilter) no, they may differ while the resulting query is the same!
|
||||
if ($sQuery1 != $sQuery2)
|
||||
{
|
||||
echo "<p>serialize/unserialize mismatch :-(</p>\n";
|
||||
echo "<p>Query 1: $sQuery1</p>\n";
|
||||
MyHelpers::var_dump_html($oMyFilter, true);
|
||||
echo "<p>Query 2: $sQuery2</p>\n";
|
||||
MyHelpers::var_dump_html($oFilter2, true);
|
||||
MyHelpers::var_cmp_html($sQuery1, $sQuery2);
|
||||
MyHelpers::var_cmp_html($oMyFilter, $oFilter2);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -782,10 +779,14 @@ class TestQueriesOnFarm extends TestBizModel
|
||||
|
||||
$aQueries = array(
|
||||
'SELECT Animal' => true,
|
||||
'SELECT Animal WHERE Animal.pkey = 1' => false,
|
||||
'SELECT Animal WHERE Animal.id = 1' => true,
|
||||
'SELECT Aniiimal' => false,
|
||||
'SELECTe Animal' => false,
|
||||
'SELECT * FROM Animal' => false,
|
||||
'SELECT Animal AS zoo WHERE zoo.species = \'human\'' => true,
|
||||
'SELECT Animal AS zoo WHERE species = \'human\'' => true,
|
||||
'SELECT Animal AS zoo WHERE espece = \'human\'' => false,
|
||||
'SELECT Animal AS zoo WHERE zoo.species IN (\'human\', "pig")' => true,
|
||||
'SELECT Animal AS zoo WHERE CONCATENATION(zoo.species, zoo.sex) LIKE "hum%male"' => false,
|
||||
'SELECT Animal AS zoo WHERE CONCAT(zoo.species, zoo.sex) LIKE "hum%male"' => true,
|
||||
@@ -793,6 +794,7 @@ class TestQueriesOnFarm extends TestBizModel
|
||||
'SELECT Animal AS zoo WHERE zoo.kind = \'human\'' => false,
|
||||
'SELECT Animal WHERE Animal.species = \'human\' AND Animal.sex = \'female\'' => true,
|
||||
'SELECT Mammal AS x WHERE (x.species = \'human\' AND x.name LIKE \'ro%\') OR (x.species = \'donkey\' AND x.name LIKE \'po%\')' => true,
|
||||
'SELECT Mammal AS x WHERE x.species = \'human\' AND x.name LIKE \'ro%\' OR x.species = \'donkey\' AND x.name LIKE \'po%\'' => true,
|
||||
'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true,
|
||||
'SELECT Mammal AS m WHERE DAY(m.birth) = 19' => true,
|
||||
'SELECT Mammal AS m WHERE YEAR(m.birth) = 1971' => true,
|
||||
@@ -802,6 +804,7 @@ class TestQueriesOnFarm extends TestBizModel
|
||||
'SELECT Mammal AS m WHERE m.name = IF(FLOOR(ROUND(m.height)) > 2, "pomme", "romain")' => true,
|
||||
'SELECT Mammal AS m WHERE (1 + 2' => false,
|
||||
'SELECT Mammal AS m WHERE (1 + 2 * 4 / 23) > 0' => true,
|
||||
'SELECT Mammal AS m WHERE (4 / 23 * 2 + 1) > 0' => true,
|
||||
'SELECT Mammal AS m WHERE 1/0' => true,
|
||||
'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true,
|
||||
'SELECT Animal JOIN Group ON Group.leader = Animal.id' => true,
|
||||
@@ -813,20 +816,30 @@ class TestQueriesOnFarm extends TestBizModel
|
||||
'SELECT Animal AS A JOIN Group AS G ON A.id = G.leader' => false,
|
||||
'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.qwerty = 123' => false,
|
||||
'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.name LIKE "a%"' => true,
|
||||
'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.id = 1' => true,
|
||||
'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE id = 1' => false,
|
||||
'SELECT Animal AS A JOIN Group AS G ON A.member = G.id' => false,
|
||||
'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id' => true,
|
||||
'SELECT Mammal AS M JOIN Group AS G ON A.member = G.id' => false,
|
||||
'SELECT Mammal AS myAlias JOIN Group AS myAlias ON myAlias.member = myAlias.id' => false,
|
||||
'SELECT Mammal AS Mammal JOIN Group AS Mammal ON Mammal.member = Mammal.id' => false,
|
||||
'SELECT Group AS G WHERE G.leader_name LIKE "%"' => true,
|
||||
'SELECT Group AS G WHERE G.leader_speed < 100000' => true,
|
||||
'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true,
|
||||
'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_speed < 100000' => true,
|
||||
'SELECT Mammal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true,
|
||||
'SELECT Mammal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true,
|
||||
'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true,
|
||||
'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true,
|
||||
'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id' => true,
|
||||
'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true,
|
||||
'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.pkey = 1' => true,
|
||||
'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.id = 1' => true,
|
||||
'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\'' => false,
|
||||
'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true,
|
||||
'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\'' => true,
|
||||
'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.speed = 0' => true,
|
||||
'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true,
|
||||
'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id' => true,
|
||||
'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id JOIN Mammal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.name=\'chloe\' OR Child.name=\'bizounours\'' => true,
|
||||
);
|
||||
//$aQueries = array(
|
||||
// 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true,
|
||||
|
||||
Reference in New Issue
Block a user