diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index e30a0d0aa..02e835357 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -167,6 +167,7 @@ abstract class AttributeDefinition public function IsScalar() {return false;} public function IsLinkSet() {return false;} public function IsExternalKey($iType = EXTKEY_RELATIVE) {return false;} + public function IsHierarchicalKey() {return false;} public function IsExternalField() {return false;} public function IsWritable() {return false;} public function IsNullAllowed() {return true;} @@ -2422,6 +2423,115 @@ class AttributeExternalKey extends AttributeDBFieldVoid } } +/** + * Special kind of External Key to manage a hierarchy of objects + */ +class AttributeHierarchicalKey extends AttributeExternalKey +{ + static protected function ListExpectedParams() + { + $aParams = parent::ListExpectedParams(); + //unset($aParams[array_search('targetclass', $aParams)]); + + //print_r($aParams); + return $aParams; // TODO: mettre les bons parametres ici !! + } + + public function GetEditClass() {return "ExtKey";} + public function RequiresIndex() + { + return true; + } + + public function IsHierarchicalKey() {return true;} + public function GetKeyAttDef($iType = EXTKEY_RELATIVE){return $this;} + public function GetKeyAttCode() {return $this->GetCode();} + + + public function GetBasicFilterOperators() + { + return parent::GetBasicFilterOperators(); + } + public function GetBasicFilterLooseOperator() + { + return parent::GetBasicFilterLooseOperator(); + } + + public function GetSQLColumns() + { + $aColumns = array(); + $aColumns[$this->GetCode()] = 'INT(11)'; + $aColumns[$this->GetSQLLeft()] = 'INT(11)'; + $aColumns[$this->GetSQLRight()] = 'INT(11)'; + return $aColumns; + } + public function GetSQLRight() + { + return $this->GetCode().'_right'; + } + public function GetSQLLeft() + { + return $this->GetCode().'_left'; + } + + public function GetSQLValues($value) + { + if (!is_array($value)) + { + $aValues[$this->GetCode()] = $value; + } + else + { + $aValues = array(); + $aValues[$this->GetCode()] = $value[$this->GetCode()]; + $aValues[$this->GetSQLRight()] = $value[$this->GetSQLRight()]; + $aValues[$this->GetSQLLeft()] = $value[$this->GetSQLLeft()]; + } + return $aValues; + } + + public function GetAllowedValues($aArgs = array(), $sContains = '') + { + if (array_key_exists('this', $aArgs)) + { + // Hierarchical keys have one more constraint: the "parent value" cannot be + // "under" themselves + $iRootId = $aArgs['this']->GetKey(); + if ($iRootId > 0) // ignore objects that do no exist in the database... + { + $oValSetDef = $this->GetValuesDef(); + $sClass = $this->GetHostClass(); // host class == target class for HK + $oFilter = DBObjectSearch::FromOQL("SELECT $sClass AS node JOIN $sClass AS root ON node.".$this->GetCode()." NOT BELOW root.id WHERE root.id = $iRootId"); + $oValSetDef->AddCondition($oFilter); + } + } + else + { + return parent::GetAllowedValues($aArgs, $sContains); + } + } + + public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '') + { + $oValSetDef = $this->GetValuesDef(); + if (array_key_exists('this', $aArgs)) + { + // Hierarchical keys have one more constraint: the "parent value" cannot be + // "under" themselves + $iRootId = $aArgs['this']->GetKey(); + if ($iRootId > 0) // ignore objects that do no exist in the database... + { + $aValuesSetDef = $this->GetValuesDef(); + $sClass = $this->GetHostClass(); // host class == target class for HK + $oFilter = DBObjectSearch::FromOQL("SELECT $sClass AS node JOIN $sClass AS root ON node.".$this->GetCode()." NOT BELOW root.id WHERE root.id = $iRootId"); + $oValSetDef->AddCondition($oFilter); + } + } + $oSet = $oValSetDef->ToObjectSet($aArgs, $sContains); + return $oSet; + } +} + /** * An attribute which corresponds to an external key (direct or indirect) * diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index 047996c51..caa978671 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -212,6 +212,8 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute $oMonoObjectSet = new DBObjectSet($oTargetSearch); if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES) { + if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renammed attributes... + $oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode')); $sAttName = $oAttDef->GetLabel(); $sNewValue = $this->Get('newvalue'); diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 2bbfa265d..577d869c0 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -719,6 +719,14 @@ abstract class DBObject return "Target object not found ($sTargetClass::$toCheck)"; } } + if ($oAtt->IsHierarchicalKey()) + { + // This check cannot be deactivated since otherwise the user may break things by a CSV import of a bulk modify + if ($toCheck == $this->GetKey()) + { + return "An object can not be its own parent in a hierarchy (".$oAtt->Getlabel()." = $toCheck)"; + } + } } elseif ($oAtt->IsScalar()) { @@ -1067,6 +1075,8 @@ abstract class DBObject $aValuesToWrite[] = CMDBSource::Quote($this->m_iKey); } + $aHierarchicalKeys = array(); + foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef) { // Skip this attribute if not defined in this table @@ -1077,6 +1087,10 @@ abstract class DBObject $aFieldsToWrite[] = "`$sColumn`"; $aValuesToWrite[] = CMDBSource::Quote($sValue); } + if ($oAttDef->IsHierarchicalKey()) + { + $aHierarchicalKeys[$sAttCode] = $oAttDef; + } } if (count($aValuesToWrite) == 0) return false; @@ -1099,6 +1113,17 @@ abstract class DBObject } else { + if (count($aHierarchicalKeys) > 0) + { + foreach($aHierarchicalKeys as $sAttCode => $oAttDef) + { + $aValues = MetaModel::HKInsertChildUnder($this->m_aCurrValues[$sAttCode], $oAttDef, $sTable); + $aFieldsToWrite[] = '`'.$oAttDef->GetSQLRight().'`'; + $aValuesToWrite[] = $aValues[$oAttDef->GetSQLRight()]; + $aFieldsToWrite[] = '`'.$oAttDef->GetSQLLeft().'`'; + $aValuesToWrite[] = $aValues[$oAttDef->GetSQLLeft()]; + } + } $sInsertSQL = "INSERT INTO `$sTable` (".join(",", $aFieldsToWrite).") VALUES (".join(", ", $aValuesToWrite).")"; $iNewKey = CMDBSource::InsertInto($sInsertSQL); } @@ -1250,22 +1275,68 @@ abstract class DBObject } $bHasANewExternalKeyValue = false; + $aHierarchicalKeys = array(); foreach($aChanges as $sAttCode => $valuecurr) { $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); if ($oAttDef->IsExternalKey()) $bHasANewExternalKeyValue = true; if (!$oAttDef->IsDirectField()) unset($aChanges[$sAttCode]); + if ($oAttDef->IsHierarchicalKey()) + { + $aHierarchicalKeys[$sAttCode] = $oAttDef; + } } - // Update scalar attributes - if (count($aChanges) != 0) + if (!MetaModel::DBIsReadOnly()) { - $oFilter = new DBObjectSearch(get_class($this)); - $oFilter->AddCondition('id', $this->m_iKey, '='); - - $sSQL = MetaModel::MakeUpdateQuery($oFilter, $aChanges); - if (!MetaModel::DBIsReadOnly()) + // Update the left & right indexes for each hierarchical key + foreach($aHierarchicalKeys as $sAttCode => $oAttDef) { + $sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode); + $sSQL = "SELECT `".$oAttDef->GetSQLRight()."` AS `right`, `".$oAttDef->GetSQLLeft()."` AS `left` FROM `$sTable` WHERE id=".$this->GetKey(); + $aRes = CMDBSource::QueryToArray($sSQL); + $iMyLeft = $aRes[0]['left']; + $iMyRight = $aRes[0]['right']; + $iDelta =$iMyRight - $iMyLeft + 1; + MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable); + + if ($aChanges[$sAttCode] == 0) + { + // No new parent, insert completely at the right of the tree + $sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`"; + $aRes = CMDBSource::QueryToArray($sSQL); + if (count($aRes) == 0) + { + $iNewLeft = 1; + } + else + { + $iNewLeft = $aRes[0]['max']+1; + } + } + else + { + // Insert at the right of the specified parent + $sSQL = "SELECT `".$oAttDef->GetSQLRight()."` FROM `$sTable` WHERE id=".((int)$aChanges[$sAttCode]); + $iNewLeft = CMDBSource::QueryToScalar($sSQL); + } + + MetaModel::HKReplugBranch($iNewLeft, $iNewLeft + $iDelta - 1, $oAttDef, $sTable); + + $aHKChanges = array(); + $aHKChanges[$sAttCode] = $aChanges[$sAttCode]; + $aHKChanges[$oAttDef->GetSQLLeft()] = $iNewLeft; + $aHKChanges[$oAttDef->GetSQLRight()] = $iNewLeft + $iDelta - 1; + $aChanges[$sAttCode] = $aHKChanges; // the 3 values will be stored by MakeUpdateQuery below + } + + // Update scalar attributes + if (count($aChanges) != 0) + { + $oFilter = new DBObjectSearch(get_class($this)); + $oFilter->AddCondition('id', $this->m_iKey, '='); + + $sSQL = MetaModel::MakeUpdateQuery($oFilter, $aChanges); CMDBSource::Query($sSQL); } } @@ -1321,6 +1392,34 @@ abstract class DBObject if (!MetaModel::DBIsReadOnly()) { + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef) + { + if ($oAttDef->IsHierarchicalKey()) + { + // Update the left & right indexes for each hierarchical key + $sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode); + $sSQL = "SELECT `".$oAttDef->GetSQLRight()."` AS `right`, `".$oAttDef->GetSQLLeft()."` AS `left` FROM `$sTable` WHERE id=".$this->GetKey(); + $aRes = CMDBSource::QueryToArray($sSQL); + $iMyLeft = $aRes[0]['left']; + $iMyRight = $aRes[0]['right']; + $iDelta =$iMyRight - $iMyLeft + 1; + MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable); + + // No new parent, insert completely at the right of the tree + $sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`"; + $aRes = CMDBSource::QueryToArray($sSQL); + if (count($aRes) == 0) + { + $iNewLeft = 1; + } + else + { + $iNewLeft = $aRes[0]['max']+1; + } + MetaModel::HKReplugBranch($iNewLeft, $iNewLeft + $iDelta - 1, $oAttDef, $sTable); + } + } + foreach(MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL) as $sParentClass) { $this->DBDeleteSingleTable($sParentClass); diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 72edb3ac0..eac184e4d 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -22,6 +22,12 @@ * @author Denis Flaven * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */ + +define('TREE_OPERATOR_EQUALS', 0); +define('TREE_OPERATOR_BELOW', 1); +define('TREE_OPERATOR_BELOW_STRICT', 2); +define('TREE_OPERATOR_NOT_BELOW', 3); +define('TREE_OPERATOR_NOT_BELOW_STRICT', 4); class DBObjectSearch { @@ -107,6 +113,7 @@ class DBObjectSearch if (count($this->m_aPointingTo) > 0) return false; if (count($this->m_aReferencedBy) > 0) return false; if (count($this->m_aRelatedTo) > 0) return false; + if (count($this->m_aParentConditions) > 0) return false; return true; } @@ -115,13 +122,39 @@ class DBObjectSearch // To replace __Describe } - public function DescribeConditionPointTo($sExtKeyAttCode) + public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo) { - if (!isset($this->m_aPointingTo[$sExtKeyAttCode])) return ""; - $oFilter = $this->m_aPointingTo[$sExtKeyAttCode]; - if ($oFilter->IsAny()) return ""; - $oAtt = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode); - return $oAtt->GetLabel()." having ({$oFilter->DescribeConditions()})"; + if (empty($aPointingTo)) return ""; + foreach($aPointingTo as $iOperatorCode => $oFilter) + { + if ($oFilter->IsAny()) break; + $oAtt = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode); + $sOperator = ''; + switch($iOperatorCode) + { + case TREE_OPERATOR_EQUALS: + $sOperator = 'having'; + break; + + case TREE_OPERATOR_BELOW: + $sOperator = 'below'; + break; + + case TREE_OPERATOR_BELOW_STRICT: + $sOperator = 'strictly below'; + break; + + case TREE_OPERATOR_NOT_BELOW: + $sOperator = 'not below'; + break; + + case TREE_OPERATOR_NOT_BELOW_STRICT: + $sOperator = 'strictly not below'; + break; + } + $aDescription[] = $oAtt->GetLabel()."$sOperator ({$oFilter->DescribeConditions()})"; + } + return implode(' and ', $aDescription); } public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode) @@ -141,6 +174,7 @@ class DBObjectSearch return "related ($sRelCode... peut mieux faire !, $iMaxDepth dig depth) to a {$oFilter->GetClass()} ({$oFilter->DescribeConditions()})"; } + public function DescribeConditions() { $aConditions = array(); @@ -159,10 +193,9 @@ class DBObjectSearch $aConditions[] = $this->RenderCondition(); $aCondPoint = array(); - foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter) + foreach($this->m_aPointingTo as $sExtKeyAttCode => $aPointingTo) { - if ($oFilter->IsAny()) continue; - $aCondPoint[] = $this->DescribeConditionPointTo($sExtKeyAttCode); + $aCondPoint[] = $this->DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo); } if (count($aCondPoint) > 0) { @@ -187,6 +220,11 @@ class DBObjectSearch $aConditions[] = implode(" and ", $aCondReferred); } + foreach ($this->m_aParentConditions as $aRelInfo) + { + $aCondReferred[] = $this->DescribeConditionParent($aRelInfo); + } + return implode(" and ", $aConditions); } @@ -210,6 +248,9 @@ class DBObjectSearch protected function TransferConditionExpression($oFilter, $aTranslation) { $oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false, false /* leave unresolved fields */); +//echo "

TransferConditionExpression:
"; +//echo "Adding Conditions:

".print_r($oTranslated, true)."
\n"; +//echo "

"; $this->AddConditionExpression($oTranslated); // #@# what about collisions in parameter names ??? $this->m_aParams = array_merge($this->m_aParams, $oFilter->m_aParams); @@ -218,6 +259,7 @@ class DBObjectSearch public function ResetCondition() { $this->m_oSearchCondition = new TrueExpression(); + $this->m_aParentConditions = array(); // ? is that usefull/enough, do I need to rebuild the list after the subqueries ? } @@ -385,25 +427,47 @@ class DBObjectSearch $this->m_aFullText[] = $sFullText; } + public function AddCondition_Parent($sAttCode, $iOperatorCode, $oExpression) + { + $oAttDef = MetaModel::GetAttributeDef($this->GetClass(), $sAttCode); + if (!$oAttDef instanceof AttributeHierarchicalKey) + { + throw new Exception("AddCondition_Parent can only be used on hierarchical keys. '$sAttCode' is not a hierarchical key."); + } + $this->m_aParentConditions[] = array( + 'attCode' => $sAttCode, + 'operator' => $iOperatorCode, + 'expression' => $oExpression, + ); + } + protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation) { $sOrigAlias = $this->GetClassAlias(); if (array_key_exists($sOrigAlias, $aClassAliases)) { $sNewAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $this->GetClass()); +//echo "

Generating a new alias for $sOrigAlias (already used). It is now: $sNewAlias

\n"; $this->m_aSelectedClasses[$sNewAlias] = $this->GetClass(); unset($this->m_aSelectedClasses[$sOrigAlias]); + $this->m_aClasses[$sNewAlias] = $this->GetClass(); + unset($this->m_aClasses[$sOrigAlias]); + // Translate the condition expression with the new alias $aAliasTranslation[$sOrigAlias]['*'] = $sNewAlias; } +//echo "

Adding the alias for ".$this->GetClass().". as ".$this->GetClassAlias()."

\n"; // add the alias into the filter aliases list $aClassAliases[$this->GetClassAlias()] = $this->GetClass(); - foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter) + foreach($this->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo) { - $oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation); + foreach($aPointingTo as $iOperatorCode => $oFilter) + { + $oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation); + } } foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences) @@ -415,16 +479,17 @@ class DBObjectSearch } } - public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode) + public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS) { $aAliasTranslation = array(); - $res = $this->AddCondition_PointingTo_InNameSpace($oFilter, $sExtKeyAttCode, $this->m_aClasses, $aAliasTranslation); + $res = $this->AddCondition_PointingTo_InNameSpace($oFilter, $sExtKeyAttCode, $this->m_aClasses, $aAliasTranslation, $iOperatorCode); $this->TransferConditionExpression($oFilter, $aAliasTranslation); return $res; } - protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation) + protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode) { +//echo "

Calling: AddCondition_PointingTo_InNameSpace([".implode(',', $aClassAliases)."], [".implode(',', $aAliasTranslation)."]);

"; if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode)) { throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored"); @@ -434,20 +499,36 @@ class DBObjectSearch { throw new CoreException("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}"); } + if(($iOperatorCode != TREE_OPERATOR_EQUALS) && !($oAttExtKey instanceof AttributeHierarchicalKey)) + { + throw new CoreException("The specified tree operator $isOperatorCode is not applicable to the key '{$this->GetClass()}::$sExtKeyAttCode', which is not a HierarchicalKey"); + } if (array_key_exists($sExtKeyAttCode, $this->m_aPointingTo)) { - $this->m_aPointingTo[$sExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation); + if (array_key_exists($iOperatorCode, $this->m_aPointingTo[$sExtKeyAttCode])) + { + // Same ext key and same operator, merge the filters together + $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation); + } + else + { +//echo "

Calling: AddToNameSpace([".implode(',', $aClassAliases)."], [".implode(',', $aAliasTranslation)."]);

"; + $oFilter->AddToNamespace($aClassAliases, $aAliasTranslation); + + $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode] = $oFilter; + } } else { +//echo "

Calling: AddToNameSpace([".implode(',', $aClassAliases)."], [".implode(',', $aAliasTranslation)."]);

"; $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(); - $this->m_aPointingTo[$sExtKeyAttCode] = $oFilter; + $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode] = $oFilter; } } @@ -491,6 +572,7 @@ class DBObjectSearch public function AddCondition_LinkedTo(DBObjectSearch $oLinkFilter, $sExtKeyAttCodeToMe, $sExtKeyAttCodeTarget, DBObjectSearch $oFilterTarget) { $oLinkFilterFinal = clone $oLinkFilter; + // todo : new function prototype $oLinkFilterFinal->AddCondition_PointingTo($sExtKeyAttCodeToMe); $this->AddCondition_ReferencedBy($oLinkFilterFinal, $sExtKeyAttCodeToMe); @@ -523,9 +605,12 @@ class DBObjectSearch $this->m_aFullText = array_merge($this->m_aFullText, $oFilter->m_aFullText); $this->m_aRelatedTo = array_merge($this->m_aRelatedTo, $oFilter->m_aRelatedTo); - foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$oExtFilter) + foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo) { - $this->AddCondition_PointingTo_InNamespace($oExtFilter, $sExtKeyAttCode, $aClassAliases, $aAliasTranslation); + foreach($aPointingTo as $iOperatorCode => $oExtFilter) + { + $this->AddCondition_PointingTo_InNamespace($oExtFilter, $sExtKeyAttCode, $aClassAliases, $aAliasTranslation, $iOperatorCode); + } } foreach($oFilter->m_aReferencedBy as $sForeignClass => $aReferences) { @@ -544,7 +629,7 @@ class DBObjectSearch { return $this->m_aPointingTo; } - if (!array_key_exists($sKeyAttCode, $this->m_aPointingTo)) return null; + if (!array_key_exists($sKeyAttCode, $this->m_aPointingTo)) return array(); return $this->m_aPointingTo[$sKeyAttCode]; } public function GetCriteria_ReferencedBy($sRemoteClass = "", $sForeignExtKeyAttCode = "") @@ -710,10 +795,36 @@ class DBObjectSearch protected function ToOQL_Joins() { $sRes = ''; - foreach($this->m_aPointingTo as $sExtKey=>$oFilter) + foreach($this->m_aPointingTo as $sExtKey => $aPointingTo) { - $sRes .= ' JOIN '.$oFilter->GetClass().' AS '.$oFilter->GetClassAlias().' ON '.$this->GetClassAlias().'.'.$sExtKey.' = '.$oFilter->GetClassAlias().'.id'; - $sRes .= $oFilter->ToOQL_Joins(); + foreach($aPointingTo as $iOperatorCode => $oFilter) + { + switch($iOperatorCode) + { + case TREE_OPERATOR_EQUALS: + $sOperator = ' = '; + break; + + case TREE_OPERATOR_BELOW: + $sOperator = ' BELOW '; + break; + + case TREE_OPERATOR_BELOW_STRICT: + $sOperator = ' BELOW STRICT '; + break; + + case TREE_OPERATOR_NOT_BELOW: + $sOperator = ' NOT BELOW '; + break; + + case TREE_OPERATOR_NOT_BELOW_STRICT: + $sOperator = ' NOT BELOW STRICT '; + break; + + } + $sRes .= ' JOIN '.$oFilter->GetClass().' AS '.$oFilter->GetClassAlias().' ON '.$this->GetClassAlias().'.'.$sExtKey.$sOperator.$oFilter->GetClassAlias().'.id'; + $sRes .= $oFilter->ToOQL_Joins(); + } } foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences) { @@ -926,7 +1037,26 @@ class DBObjectSearch } else { - $aJoinItems[$sFromClass]->AddCondition_PointingTo($aJoinItems[$sToClass], $sExtKeyAttCode); + $sOperator = $oJoinSpec->GetOperator(); + switch($sOperator) + { + case '=': + $iOperatorCode = TREE_OPERATOR_EQUALS; + break; + case 'BELOW': + $iOperatorCode = TREE_OPERATOR_BELOW; + break; + case 'BELOW_STRICT': + $iOperatorCode = TREE_OPERATOR_BELOW_STRICT; + break; + case 'NOT_BELOW': + $iOperatorCode = TREE_OPERATOR_NOT_BELOW; + break; + case 'NOT_BELOW_STRICT': + $iOperatorCode = TREE_OPERATOR_NOT_BELOW_STRICT; + break; + } + $aJoinItems[$sFromClass]->AddCondition_PointingTo($aJoinItems[$sToClass], $sExtKeyAttCode, $iOperatorCode); } } } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index e651aae62..feeefe7c2 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -699,7 +699,11 @@ abstract class MetaModel final static public function GetAttributeDef($sClass, $sAttCode) { - self::_check_subclass($sClass); + self::_check_subclass($sClass); +if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) +{ + echo "

$sAttCode is NOT a valid attribute of class $sClass.

"; +} return self::$m_aAttribDefs[$sClass][$sAttCode]; } @@ -1666,6 +1670,23 @@ abstract class MetaModel self::_check_subclass($sClass); return (self::GetRootClass($sClass) == $sClass); } + /** + * Tells if a class contains a hierarchical key, and if so what is its AttCode + * @return mixed String = sAttCode or false if the class is not part of a hierarchy + */ + public static function IsHierarchicalClass($sClass) + { + $sHierarchicalKeyCode = false; + foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAtt) + { + if ($oAtt->IsHierarchicalKey()) + { + $sHierarchicalKeyCode = $sAttCode; // Found the hierarchical key, no need to continue + break; + } + } + return $sHierarchicalKeyCode; + } public static function EnumRootClasses() { return array_unique(self::$m_aRootClasses); @@ -1943,6 +1964,7 @@ abstract class MetaModel try { $sRes = $oSelect->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount); +//echo "

MakeQuery: $sRes

"; } catch (MissingQueryArgument $e) { @@ -2094,8 +2116,9 @@ abstract class MetaModel } } } - +//echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."

\n"; $aExpectedAtts = array(); // array of (attcode => fieldexpression) +//echo "

".__LINE__.": GetUnresolvedFields($sClassAlias, ...)

\n"; $oQBExpr->GetUnresolvedFields($sClassAlias, $aExpectedAtts); // Compute a clear view of required joins (from the current class) @@ -2120,17 +2143,22 @@ abstract class MetaModel } } // Get all Ext keys used by the filter - foreach ($oFilter->GetCriteria_PointingTo() as $sKeyAttCode => $trash) + foreach ($oFilter->GetCriteria_PointingTo() as $sKeyAttCode => $aPointingTo) { - $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; - $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array(); + if (array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo)) + { + $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; + $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array(); + } } if (array_key_exists('friendlyname', $aExpectedAtts)) { $aTranslateNow = array(); $aTranslateNow[$sClassAlias]['friendlyname'] = self::GetNameExpression($sClass, $sClassAlias); +//echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."

\n"; $oQBExpr->Translate($aTranslateNow, false); +//echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."

\n"; $aNameSpec = self::GetNameSpec($sClass); foreach($aNameSpec[1] as $i => $sAttCode) @@ -2188,6 +2216,7 @@ abstract class MetaModel foreach(self::EnumParentClasses($sClass) as $sParentClass) { if (!self::HasTable($sParentClass)) continue; +//echo "

Parent class: $sParentClass... let's call MakeQuerySingleTable()

"; self::DbgTrace("Parent class: $sParentClass... let's call MakeQuerySingleTable()"); $oSelectParentTable = self::MakeQuerySingleTable($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $sParentClass, $aExtKeys, $aValues); if (is_null($oSelectBase)) @@ -2272,6 +2301,7 @@ abstract class MetaModel protected static function MakeQuerySingleTable($aSelectedClasses, &$oQBExpr, &$aClassAliases, &$aTableAliases, $oFilter, $sTableClass, $aExtKeys, $aValues) { // $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields)) +//echo "MAKEQUERY($sTableClass)-liste des clefs externes($sTableClass):
".print_r($aExtKeys, true)."

\n"; // Prepare the query for a single table (compound objects) // Ignores the items (attributes/filters) that are not on the target table @@ -2346,6 +2376,7 @@ abstract class MetaModel } else { +//echo "

MakeQuerySingleTable: Field $sAttCode is part of the table $sTable (named: $sTableAlias)

"; // standard field, or external key // add it to the output foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr) @@ -2362,92 +2393,257 @@ abstract class MetaModel // $oSelectBase = new SQLQuery($sTable, $sTableAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField); +//echo "MAKEQUERY- Classe $sTableClass
\n"; // 4 - The external keys -> joins... // + $aAllPointingTo = $oFilter->GetCriteria_PointingTo(); + if (array_key_exists($sTableClass, $aExtKeys)) { foreach ($aExtKeys[$sTableClass] as $sKeyAttCode => $aExtFields) { - $oKeyAttDef = self::GetAttributeDef($sTargetClass, $sKeyAttCode); + $oKeyAttDef = self::GetAttributeDef($sTableClass, $sKeyAttCode); - $oExtFilter = $oFilter->GetCriteria_PointingTo($sKeyAttCode); - - // In case the join was not explicitely defined in the filter, - // we need to do it now - if (empty($oExtFilter)) + $aPointingTo = $oFilter->GetCriteria_PointingTo($sKeyAttCode); +//echo "MAKEQUERY-Cle '$sKeyAttCode'
\n"; + if (!array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo)) { +//echo "MAKEQUERY-Ajoutons l'operateur TREE_OPERATOR_EQUALS pour $sKeyAttCode
\n"; + // The join was not explicitely defined in the filter, + // we need to do it now $sKeyClass = $oKeyAttDef->GetTargetClass(); $sKeyClassAlias = self::GenerateUniqueAlias($aClassAliases, $sKeyClass.'_'.$sKeyAttCode, $sKeyClass); $oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias); - } - else - { - // The aliases should not conflict because normalization occured while building the filter - $sKeyClass = $oExtFilter->GetFirstJoinedClass(); - $sKeyClassAlias = $oExtFilter->GetFirstJoinedClassAlias(); - - // Note: there is no search condition in $oExtFilter, because normalization did merge the condition onto the top of the filter tree - } - // Specify expected attributes for the target class query - // ... and use the current alias ! - $aTranslateNow = array(); // Translation for external fields - must be performed before the join is done (recursion...) - foreach($aExtFields as $sAttCode => $oAtt) + $aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS] = $oExtFilter; + } + } + } +//echo "MAKEQUERY-liste des clefs de jointure:
".print_r(array_keys($aAllPointingTo), true)."

\n"; + + foreach ($aAllPointingTo as $sKeyAttCode => $aPointingTo) + { + foreach($aPointingTo as $iOperatorCode => $oExtFilter) + { + if (!MetaModel::IsValidAttCode($sTableClass, $sKeyAttCode)) continue; // Not defined in the class, skip it + // The aliases should not conflict because normalization occured while building the filter + $oKeyAttDef = self::GetAttributeDef($sTableClass, $sKeyAttCode); + $sKeyClass = $oExtFilter->GetFirstJoinedClass(); + $sKeyClassAlias = $oExtFilter->GetFirstJoinedClassAlias(); + +//echo "MAKEQUERY-$sTableClass::$sKeyAttCode Foreach PointingTo($iOperatorCode) $sKeyClass (alias:$sKeyClassAlias)
\n"; + + // Note: there is no search condition in $oExtFilter, because normalization did merge the condition onto the top of the filter tree + + if ($iOperatorCode == TREE_OPERATOR_EQUALS) { - if ($oAtt instanceof AttributeFriendlyName) + // Specify expected attributes for the target class query + // ... and use the current alias ! + $aTranslateNow = array(); // Translation for external fields - must be performed before the join is done (recursion...) +//echo "MAKEQUERY-array_key_exists($sTableClass, \$aExtKeys)
\n"; + if (array_key_exists($sTableClass, $aExtKeys) && array_key_exists($sKeyAttCode, $aExtKeys[$sTableClass])) { - // Note: for a given ext key, there is one single attribute "friendly name" - $aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias); - } - else - { - $sExtAttCode = $oAtt->GetExtAttCode(); - // Translate mainclass.extfield => remoteclassalias.remotefieldcode - $oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode); - foreach ($oRemoteAttDef->GetSQLExpressions() as $sColID => $sRemoteAttExpr) + foreach($aExtKeys[$sTableClass][$sKeyAttCode] as $sAttCode => $oAtt) { - $aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias); +//echo "MAKEQUERY aExtKeys[$sTableClass][$sKeyAttCode] => $sAttCode-oAtt:
".print_r($oAtt, true)."

\n"; + if ($oAtt instanceof AttributeFriendlyName) + { + // Note: for a given ext key, there is one single attribute "friendly name" + $aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias); +//echo "

aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias);

\n"; + } + else + { + $sExtAttCode = $oAtt->GetExtAttCode(); + // Translate mainclass.extfield => remoteclassalias.remotefieldcode + $oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode); + foreach ($oRemoteAttDef->GetSQLExpressions() as $sColID => $sRemoteAttExpr) + { + $aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias); +//echo "

aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);

\n"; + } +//echo "

ExtAttr2: $sTargetAlias.$sAttCode to $sKeyClassAlias.$sRemoteAttExpr (class: $sKeyClass)

\n"; + } + } + // Translate prior to recursing + // +//echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."\n".print_r($aTranslateNow, true)."

\n"; + $oQBExpr->Translate($aTranslateNow, false); +//echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."

\n"; + +//echo "

External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()/p>\n"; + self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()"); + $oQBExpr->PushJoinField(new FieldExpression('id', $sKeyClassAlias)); + +//echo "

Recursive MakeQuery ".__LINE__.":

\n".print_r($aSelectedClasses, true)."

\n"; + $oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter); + + $oJoinExpr = $oQBExpr->PopJoinField(); + $sExternalKeyTable = $oJoinExpr->GetParent(); + $sExternalKeyField = $oJoinExpr->GetName(); + + $aCols = $oKeyAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc()) + $sLocalKeyField = current($aCols); // get the first column for an external key + + self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField"); + if ($oKeyAttDef->IsNullAllowed()) + { + $oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable); + } + else + { + $oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable); } - //#@# debug - echo "

$sTargetAlias.$sAttCode to $sKeyClassAlias.$sRemoteAttExpr (class: $sKeyClass)

\n"; } } - // Translate prior to recursing - // - $oQBExpr->Translate($aTranslateNow, false); - - self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()"); - - $oQBExpr->PushJoinField(new FieldExpression('id', $sKeyClassAlias)); - - $oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter); - - $oJoinExpr = $oQBExpr->PopJoinField(); - $sExternalKeyTable = $oJoinExpr->GetParent(); - $sExternalKeyField = $oJoinExpr->GetName(); - - $aCols = $oKeyAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc()) - $sLocalKeyField = current($aCols); // get the first column for an external key - - self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField"); - if ($oKeyAttDef->IsNullAllowed()) - { - $oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable); - } else { - $oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable); + $oQBExpr->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias)); + $oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter); + $oJoinExpr = $oQBExpr->PopJoinField(); +//echo "MAKEQUERY-PopJoinField pour $sKeyAttCode, $sKeyClassAlias:
".print_r($oJoinExpr, true)."

\n"; + $sExternalKeyTable = $oJoinExpr->GetParent(); + $sExternalKeyField = $oJoinExpr->GetName(); + $sLeftIndex = $sExternalKeyField.'_left'; // TODO use GetSQLLeft() + $sRightIndex = $sExternalKeyField.'_right'; // TODO use GetSQLRight() + + $LocalKeyLeft = $oKeyAttDef->GetSQLLeft(); +//echo "MAKEQUERY-LocalKeyLeft pour $sKeyAttCode => $LocalKeyLeft
\n"; + + $oSelectBase->AddInnerJoinTree($oSelectExtKey, $LocalKeyLeft, $sLeftIndex, $sRightIndex, $sExternalKeyTable, $iOperatorCode); } } } // Translate the selected columns // +//echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."

\n"; $oQBExpr->Translate($aTranslation, false); +//echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."

\n"; //MyHelpers::var_dump_html($oSelectBase->RenderSelect()); return $oSelectBase; } + /** + * Special processing for the hierarchical keys stored as nested sets + * @param $iId integer The identifier of the parent + * @param $oAttDef AttributeDefinition The attribute corresponding to the hierarchical key + * @param $stable string The name of the database table containing the hierarchical key + */ + public static function HKInsertChildUnder($iId, $oAttDef, $sTable) + { + // Get the parent id.right value + if ($iId == 0) + { + // No parent, insert completely at the right of the tree + $sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`"; + $aRes = CMDBSource::QueryToArray($sSQL); + if (count($aRes) == 0) + { + $iMyRight = 1; + } + else + { + $iMyRight = $aRes[0]['max']+1; + } + } + else + { + $sSQL = "SELECT `".$oAttDef->GetSQLRight()."` FROM `$sTable` WHERE id=".$iId; + $iMyRight = CMDBSource::QueryToScalar($sSQL); + $sSQLUpdateRight = "UPDATE `$sTable` SET `".$oAttDef->GetSQLRight()."` = `".$oAttDef->GetSQLRight()."` + 2 WHERE `".$oAttDef->GetSQLRight()."` >= $iMyRight"; + CMDBSource::Query($sSQLUpdateRight); + $sSQLUpdateLeft = "UPDATE `$sTable` SET `".$oAttDef->GetSQLLeft()."` = `".$oAttDef->GetSQLLeft()."` + 2 WHERE `".$oAttDef->GetSQLLeft()."` > $iMyRight"; + CMDBSource::Query($sSQLUpdateLeft); + } + return array($oAttDef->GetSQLRight() => $iMyRight+1, $oAttDef->GetSQLLeft() => $iMyRight); + } + + /** + * Special processing for the hierarchical keys stored as nested sets: temporary remove the branch + * @param $iId integer The identifier of the parent + * @param $oAttDef AttributeDefinition The attribute corresponding to the hierarchical key + * @param $sTable string The name of the database table containing the hierarchical key + */ + public static function HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable) + { + $iDelta = $iMyRight - $iMyLeft + 1; + $sSQL = "UPDATE `$sTable` SET `".$oAttDef->GetSQLRight()."` = $iMyLeft - `".$oAttDef->GetSQLRight()."`, `".$oAttDef->GetSQLLeft()."` = $iMyLeft - `".$oAttDef->GetSQLLeft(); + $sSQL .= "` WHERE `".$oAttDef->GetSQLLeft()."`> $iMyLeft AND `".$oAttDef->GetSQLRight()."`< $iMyRight"; + CMDBSource::Query($sSQL); + $sSQL = "UPDATE `$sTable` SET `".$oAttDef->GetSQLLeft()."` = `".$oAttDef->GetSQLLeft()."` - $iDelta WHERE `".$oAttDef->GetSQLLeft()."` > $iMyRight"; + CMDBSource::Query($sSQL); + $sSQL = "UPDATE `$sTable` SET `".$oAttDef->GetSQLRight()."` = `".$oAttDef->GetSQLRight()."` - $iDelta WHERE `".$oAttDef->GetSQLRight()."` > $iMyRight"; + CMDBSource::Query($sSQL); + } + + /** + * Special processing for the hierarchical keys stored as nested sets: replug the temporary removed branch + * @param $iId integer The identifier of the parent + * @param $oAttDef AttributeDefinition The attribute corresponding to the hierarchical key + * @param $sTable string The name of the database table containing the hierarchical key + */ + public static function HKReplugBranch($iNewLeft, $iNewRight, $oAttDef, $sTable) + { + $iDelta = $iNewRight - $iNewLeft + 1; + $sSQL = "UPDATE `$sTable` SET `".$oAttDef->GetSQLLeft()."` = `".$oAttDef->GetSQLLeft()."` + $iDelta WHERE `".$oAttDef->GetSQLLeft()."` > $iNewLeft"; + CMDBSource::Query($sSQL); + $sSQL = "UPDATE `$sTable` SET `".$oAttDef->GetSQLRight()."` = `".$oAttDef->GetSQLRight()."` + $iDelta WHERE `".$oAttDef->GetSQLRight()."` >= $iNewLeft"; + CMDBSource::Query($sSQL); + $sSQL = "UPDATE `$sTable` SET `".$oAttDef->GetSQLRight()."` = $iNewLeft - `".$oAttDef->GetSQLRight()."`, `".$oAttDef->GetSQLLeft()."` = $iNewLeft - `".$oAttDef->GetSQLLeft()."` WHERE `".$oAttDef->GetSQLRight()."`< 0"; + CMDBSource::Query($sSQL); + } + + /** + * Initializes (i.e converts) a hierarchy stored using a 'parent_id' external key + * into a hierarchy stored with a HierarchicalKey, by initializing the _left and _right values + * to correspond to the existing hierarchy in the database + * @param $sClass string Name of the class to process + * @param $sAttCode string Code of the attribute to process + */ + public static function HKInit($sClass, $sAttCode) + { + $idx = 1; + $oAttDef = self::GetAttributeDef($sClass, $sAttCode); + $sTable = self::DBGetTable($sClass, $sAttCode); + if ($oAttDef->IsHierarchicalKey()) + { + try + { + CMDBSource::Query('START TRANSACTION'); + self::HKInitChildren($sTable, $sAttCode, $oAttDef, 0, $idx); + CMDBSource::Query('COMMIT'); + } + catch(Exception $e) + { + CMDBSource::Query('ROLLBACK'); + throw new Exception("An error occured (".$e->getMessage().") while initializing the hierarchy for ($sClass, $sAttCode). The database was not modified."); + } + } + } + + /** + * Recursive helper function called by HKInit + */ + protected static function HKInitChildren($sTable, $sAttCode, $oAttDef, $iId, &$iCurrIndex) + { + $sSQL = "SELECT id FROM `$sTable` WHERE `$sAttCode` = $iId"; + $aRes = CMDBSource::QueryToArray($sSQL); + $aTree = array(); + $sLeft = $oAttDef->GetSQLLeft(); + $sRight = $oAttDef->GetSQLRight(); + foreach($aRes as $aValues) + { + $iChildId = $aValues['id']; + $iLeft = $iCurrIndex++; + $aChildren = self::HKInitChildren($sTable, $sAttCode, $oAttDef, $iChildId, $iCurrIndex); + $iRight = $iCurrIndex++; + $sSQL = "UPDATE `$sTable` SET `$sLeft` = $iLeft, `$sRight` = $iRight WHERE id= $iChildId"; + CMDBSource::Query($sSQL); + } + } + public static function GenerateUniqueAlias(&$aAliases, $sNewName, $sRealName) { if (!array_key_exists($sNewName, $aAliases)) diff --git a/core/oql/build.bash b/core/oql/build.bash new file mode 100755 index 000000000..cff08c526 --- /dev/null +++ b/core/oql/build.bash @@ -0,0 +1,4 @@ +#!/bin/bash +php /usr/share/php/PHP/LexerGenerator/cli.php oql-lexer.plex +php /usr/share/php/PHP/ParserGenerator/cli.php oql-parser.y + diff --git a/core/oql/oql-lexer.php b/core/oql/oql-lexer.php index 5b75df049..78bf2e052 100644 --- a/core/oql/oql-lexer.php +++ b/core/oql/oql-lexer.php @@ -104,155 +104,159 @@ class OQLLexerRaw if ($this->count >= strlen($this->data)) { return false; // end of input } - do { - $rules = array( - '/^[ \t\n\r]+/', - '/^SELECT/', - '/^FROM/', - '/^AS/', - '/^WHERE/', - '/^JOIN/', - '/^ON/', - '/^\//', - '/^\\*/', - '/^\\+/', - '/^-/', - '/^AND/', - '/^OR/', - '/^,/', - '/^\\(/', - '/^\\)/', - '/^REGEXP/', - '/^=/', - '/^!=/', - '/^>/', - '/^=/', - '/^<=/', - '/^LIKE/', - '/^NOT LIKE/', - '/^IN/', - '/^NOT IN/', - '/^INTERVAL/', - '/^IF/', - '/^ELT/', - '/^COALESCE/', - '/^ISNULL/', - '/^CONCAT/', - '/^SUBSTR/', - '/^TRIM/', - '/^DATE/', - '/^DATE_FORMAT/', - '/^CURRENT_DATE/', - '/^NOW/', - '/^TIME/', - '/^TO_DAYS/', - '/^FROM_DAYS/', - '/^YEAR/', - '/^MONTH/', - '/^DAY/', - '/^HOUR/', - '/^MINUTE/', - '/^SECOND/', - '/^DATE_ADD/', - '/^DATE_SUB/', - '/^ROUND/', - '/^FLOOR/', - '/^INET_ATON/', - '/^INET_NTOA/', - '/^[0-9]+|0x[0-9a-fA-F]+/', - '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/', - '/^([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/', - '/^:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/', - '/^\\./', - ); - $match = false; - foreach ($rules as $index => $rule) { - if (preg_match($rule, substr($this->data, $this->count), $yymatches)) { - if ($match) { - if (strlen($yymatches[0]) > strlen($match[0][0])) { - $match = array($yymatches, $index); // matches, token - } - } else { - $match = array($yymatches, $index); - } - } - } - if (!$match) { - throw new Exception('Unexpected input at line' . $this->line . - ': ' . $this->data[$this->count]); - } - $this->token = $match[1]; - $this->value = $match[0][0]; - $yysubmatches = $match[0]; - array_shift($yysubmatches); - if (!$yysubmatches) { - $yysubmatches = array(); - } - $r = $this->{'yy_r1_' . $this->token}($yysubmatches); - if ($r === null) { - $this->count += strlen($this->value); - $this->line += substr_count($this->value, "\n"); - // accept this token - return true; - } elseif ($r === true) { - // we have changed state - // process this token in the new state - return $this->yylex(); - } elseif ($r === false) { - $this->count += strlen($this->value); - $this->line += substr_count($this->value, "\n"); - if ($this->count >= strlen($this->data)) { - return false; // end of input - } - // skip this token - continue; - } else { - $yy_yymore_patterns = array_slice($rules, $this->token, true); - // yymore is needed - do { - if (!isset($yy_yymore_patterns[$this->token])) { - throw new Exception('cannot do yymore for the last token'); - } - $match = false; - foreach ($yy_yymore_patterns[$this->token] as $index => $rule) { - if (preg_match('/' . $rule . '/', - substr($this->data, $this->count), $yymatches)) { - $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns - if ($match) { - if (strlen($yymatches[0]) > strlen($match[0][0])) { - $match = array($yymatches, $index); // matches, token - } - } else { - $match = array($yymatches, $index); - } - } - } - if (!$match) { - throw new Exception('Unexpected input at line' . $this->line . - ': ' . $this->data[$this->count]); - } - $this->token = $match[1]; - $this->value = $match[0][0]; - $yysubmatches = $match[0]; - array_shift($yysubmatches); - if (!$yysubmatches) { - $yysubmatches = array(); - } - $this->line = substr_count($this->value, "\n"); - $r = $this->{'yy_r1_' . $this->token}(); - } while ($r !== null || !$r); - if ($r === true) { - // we have changed state - // process this token in the new state - return $this->yylex(); - } else { - // accept - $this->count += strlen($this->value); - $this->line += substr_count($this->value, "\n"); - return true; - } - } + do { + $rules = array( + '/\G[ \t\n\r]+/ ', + '/\GSELECT/ ', + '/\GFROM/ ', + '/\GAS/ ', + '/\GWHERE/ ', + '/\GJOIN/ ', + '/\GON/ ', + '/\G\// ', + '/\G\\*/ ', + '/\G\\+/ ', + '/\G-/ ', + '/\GAND/ ', + '/\GOR/ ', + '/\G,/ ', + '/\G\\(/ ', + '/\G\\)/ ', + '/\GREGEXP/ ', + '/\G=/ ', + '/\G!=/ ', + '/\G>/ ', + '/\G=/ ', + '/\G<=/ ', + '/\GLIKE/ ', + '/\GNOT LIKE/ ', + '/\GIN/ ', + '/\GNOT IN/ ', + '/\GINTERVAL/ ', + '/\GIF/ ', + '/\GELT/ ', + '/\GCOALESCE/ ', + '/\GISNULL/ ', + '/\GCONCAT/ ', + '/\GSUBSTR/ ', + '/\GTRIM/ ', + '/\GDATE/ ', + '/\GDATE_FORMAT/ ', + '/\GCURRENT_DATE/ ', + '/\GNOW/ ', + '/\GTIME/ ', + '/\GTO_DAYS/ ', + '/\GFROM_DAYS/ ', + '/\GYEAR/ ', + '/\GMONTH/ ', + '/\GDAY/ ', + '/\GHOUR/ ', + '/\GMINUTE/ ', + '/\GSECOND/ ', + '/\GDATE_ADD/ ', + '/\GDATE_SUB/ ', + '/\GROUND/ ', + '/\GFLOOR/ ', + '/\GINET_ATON/ ', + '/\GINET_NTOA/ ', + '/\GBELOW/ ', + '/\GBELOW STRICT/ ', + '/\GNOT BELOW/ ', + '/\GNOT BELOW STRICT/ ', + '/\G[0-9]+|0x[0-9a-fA-F]+/ ', + '/\G\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/ ', + '/\G([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ ', + '/\G:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/ ', + '/\G\\./ ', + ); + $match = false; + foreach ($rules as $index => $rule) { + if (preg_match($rule, substr($this->data, $this->count), $yymatches)) { + if ($match) { + if (strlen($yymatches[0]) > strlen($match[0][0])) { + $match = array($yymatches, $index); // matches, token + } + } else { + $match = array($yymatches, $index); + } + } + } + if (!$match) { + throw new Exception('Unexpected input at line ' . $this->line . + ': ' . $this->data[$this->count]); + } + $this->token = $match[1]; + $this->value = $match[0][0]; + $yysubmatches = $match[0]; + array_shift($yysubmatches); + if (!$yysubmatches) { + $yysubmatches = array(); + } + $r = $this->{'yy_r1_' . $this->token}($yysubmatches); + if ($r === null) { + $this->count += strlen($this->value); + $this->line += substr_count($this->value, "\n"); + // accept this token + return true; + } elseif ($r === true) { + // we have changed state + // process this token in the new state + return $this->yylex(); + } elseif ($r === false) { + $this->count += strlen($this->value); + $this->line += substr_count($this->value, "\n"); + if ($this->count >= strlen($this->data)) { + return false; // end of input + } + // skip this token + continue; + } else { + $yy_yymore_patterns = array_slice($rules, $this->token, true); + // yymore is needed + do { + if (!isset($yy_yymore_patterns[$this->token])) { + throw new Exception('cannot do yymore for the last token'); + } + $match = false; + foreach ($yy_yymore_patterns[$this->token] as $index => $rule) { + if (preg_match('/' . $rule . '/', + $this->data, $yymatches, null, $this->count)) { + $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns + if ($match) { + if (strlen($yymatches[0]) > strlen($match[0][0])) { + $match = array($yymatches, $index); // matches, token + } + } else { + $match = array($yymatches, $index); + } + } + } + if (!$match) { + throw new Exception('Unexpected input at line ' . $this->line . + ': ' . $this->data[$this->count]); + } + $this->token = $match[1]; + $this->value = $match[0][0]; + $yysubmatches = $match[0]; + array_shift($yysubmatches); + if (!$yysubmatches) { + $yysubmatches = array(); + } + $this->line = substr_count($this->value, "\n"); + $r = $this->{'yy_r1_' . $this->token}(); + } while ($r !== null || !$r); + if ($r === true) { + // we have changed state + // process this token in the new state + return $this->yylex(); + } else { + // accept + $this->count += strlen($this->value); + $this->line += substr_count($this->value, "\n"); + return true; + } + } } while (true); } // end function @@ -530,24 +534,44 @@ class OQLLexerRaw function yy_r1_54($yy_subpatterns) { - $this->token = OQLParser::NUMVAL; + $this->token = OQLParser::BELOW; } function yy_r1_55($yy_subpatterns) { - $this->token = OQLParser::STRVAL; + $this->token = OQLParser::BELOW_STRICT; } function yy_r1_56($yy_subpatterns) { - $this->token = OQLParser::NAME; + $this->token = OQLParser::NOT_BELOW; } function yy_r1_57($yy_subpatterns) { - $this->token = OQLParser::VARNAME; + $this->token = OQLParser::NOT_BELOW_STRICT; } function yy_r1_58($yy_subpatterns) + { + + $this->token = OQLParser::NUMVAL; + } + function yy_r1_59($yy_subpatterns) + { + + $this->token = OQLParser::STRVAL; + } + function yy_r1_60($yy_subpatterns) + { + + $this->token = OQLParser::NAME; + } + function yy_r1_61($yy_subpatterns) + { + + $this->token = OQLParser::VARNAME; + } + function yy_r1_62($yy_subpatterns) { $this->token = OQLParser::DOT; diff --git a/core/oql/oql-lexer.plex b/core/oql/oql-lexer.plex index 9c0890e5a..a8c3a3c45 100644 --- a/core/oql/oql-lexer.plex +++ b/core/oql/oql-lexer.plex @@ -132,6 +132,10 @@ f_round = "ROUND" f_floor = "FLOOR" f_inet_aton = "INET_ATON" f_inet_ntoa = "INET_NTOA" +below = "BELOW" +below_strict = "BELOW STRICT" +not_below = "NOT BELOW" +not_below_strict = "NOT BELOW STRICT" numval = /[0-9]+|0x[0-9a-fA-F]+/ strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/ name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ @@ -302,6 +306,18 @@ f_inet_aton { f_inet_ntoa { $this->token = OQLParser::F_INET_NTOA; } +below { + $this->token = OQLParser::BELOW; +} +below_strict { + $this->token = OQLParser::BELOW_STRICT; +} +not_below { + $this->token = OQLParser::NOT_BELOW; +} +not_below_strict { + $this->token = OQLParser::NOT_BELOW_STRICT; +} numval { $this->token = OQLParser::NUMVAL; } diff --git a/core/oql/oql-parser.php b/core/oql/oql-parser.php index a76d5cd47..c1fb2ea39 100644 --- a/core/oql/oql-parser.php +++ b/core/oql/oql-parser.php @@ -30,7 +30,7 @@ class OQLParser_yyToken implements ArrayAccess function __toString() { - return $this->_string; + return $this->string; } function offsetExists($offset) @@ -119,59 +119,63 @@ class OQLParserRaw#line 102 "oql-parser.php" const JOIN = 6; const ON = 7; const EQ = 8; - const PAR_OPEN = 9; - const PAR_CLOSE = 10; - const INTERVAL = 11; - const F_SECOND = 12; - const F_MINUTE = 13; - const F_HOUR = 14; - const F_DAY = 15; - const F_MONTH = 16; - const F_YEAR = 17; - const DOT = 18; - const VARNAME = 19; - const NAME = 20; - const NUMVAL = 21; - const STRVAL = 22; - const REGEXP = 23; - const NOT_EQ = 24; - const LOG_AND = 25; - const LOG_OR = 26; - const MATH_DIV = 27; - const MATH_MULT = 28; - const MATH_PLUS = 29; - const MATH_MINUS = 30; - const GT = 31; - const LT = 32; - const GE = 33; - const LE = 34; - const LIKE = 35; - const NOT_LIKE = 36; - const IN = 37; - const NOT_IN = 38; - const F_IF = 39; - const F_ELT = 40; - const F_COALESCE = 41; - const F_ISNULL = 42; - const F_CONCAT = 43; - const F_SUBSTR = 44; - const F_TRIM = 45; - const F_DATE = 46; - const F_DATE_FORMAT = 47; - const F_CURRENT_DATE = 48; - const F_NOW = 49; - const F_TIME = 50; - const F_TO_DAYS = 51; - const F_FROM_DAYS = 52; - const F_DATE_ADD = 53; - const F_DATE_SUB = 54; - const F_ROUND = 55; - const F_FLOOR = 56; - const F_INET_ATON = 57; - const F_INET_NTOA = 58; - const YY_NO_ACTION = 238; - const YY_ACCEPT_ACTION = 237; - const YY_ERROR_ACTION = 236; + const BELOW = 9; + const BELOW_STRICT = 10; + const NOT_BELOW = 11; + const NOT_BELOW_STRICT = 12; + const PAR_OPEN = 13; + const PAR_CLOSE = 14; + const INTERVAL = 15; + const F_SECOND = 16; + const F_MINUTE = 17; + const F_HOUR = 18; + const F_DAY = 19; + const F_MONTH = 20; + const F_YEAR = 21; + const DOT = 22; + const VARNAME = 23; + const NAME = 24; + const NUMVAL = 25; + const STRVAL = 26; + const REGEXP = 27; + const NOT_EQ = 28; + const LOG_AND = 29; + const LOG_OR = 30; + const MATH_DIV = 31; + const MATH_MULT = 32; + const MATH_PLUS = 33; + const MATH_MINUS = 34; + const GT = 35; + const LT = 36; + const GE = 37; + const LE = 38; + const LIKE = 39; + const NOT_LIKE = 40; + const IN = 41; + const NOT_IN = 42; + const F_IF = 43; + const F_ELT = 44; + const F_COALESCE = 45; + const F_ISNULL = 46; + const F_CONCAT = 47; + const F_SUBSTR = 48; + const F_TRIM = 49; + const F_DATE = 50; + const F_DATE_FORMAT = 51; + const F_CURRENT_DATE = 52; + const F_NOW = 53; + const F_TIME = 54; + const F_TO_DAYS = 55; + const F_FROM_DAYS = 56; + const F_DATE_ADD = 57; + const F_DATE_SUB = 58; + const F_ROUND = 59; + const F_FLOOR = 60; + const F_INET_ATON = 61; + const F_INET_NTOA = 62; + const YY_NO_ACTION = 250; + const YY_ACCEPT_ACTION = 249; + const YY_ERROR_ACTION = 248; /* Next are that tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement @@ -223,183 +227,194 @@ class OQLParserRaw#line 102 "oql-parser.php" ** shifting non-terminals after a reduce. ** self::$yy_default Default action for each state. */ - const YY_SZ_ACTTAB = 455; + const YY_SZ_ACTTAB = 501; static public $yy_action = array( - /* 0 */ 17, 6, 29, 143, 143, 28, 23, 56, 5, 120, - /* 10 */ 12, 2, 107, 10, 133, 134, 135, 102, 83, 82, - /* 20 */ 87, 88, 103, 104, 121, 119, 115, 116, 106, 105, - /* 30 */ 117, 118, 136, 66, 58, 59, 60, 57, 93, 92, - /* 40 */ 91, 89, 90, 108, 109, 128, 127, 126, 124, 125, - /* 50 */ 129, 130, 131, 132, 123, 122, 114, 113, 5, 35, - /* 60 */ 7, 3, 24, 70, 133, 134, 135, 106, 83, 82, - /* 70 */ 87, 88, 4, 75, 74, 73, 76, 77, 79, 62, - /* 80 */ 52, 69, 30, 20, 26, 63, 55, 106, 93, 92, - /* 90 */ 91, 89, 90, 108, 109, 128, 127, 126, 124, 125, - /* 100 */ 129, 130, 131, 132, 123, 122, 114, 113, 5, 54, - /* 110 */ 64, 64, 64, 64, 133, 134, 135, 8, 83, 82, - /* 120 */ 87, 88, 71, 43, 16, 24, 24, 31, 25, 81, - /* 130 */ 111, 27, 188, 19, 53, 23, 49, 50, 93, 92, - /* 140 */ 91, 89, 90, 108, 109, 128, 127, 126, 124, 125, - /* 150 */ 129, 130, 131, 132, 123, 122, 114, 113, 237, 110, - /* 160 */ 100, 52, 64, 64, 9, 68, 64, 97, 46, 32, - /* 170 */ 96, 101, 51, 44, 45, 84, 21, 24, 15, 38, - /* 180 */ 40, 13, 24, 82, 11, 52, 95, 94, 86, 85, - /* 190 */ 54, 97, 39, 32, 96, 101, 51, 47, 112, 1, - /* 200 */ 21, 23, 15, 8, 40, 99, 80, 72, 78, 52, - /* 210 */ 95, 94, 86, 85, 54, 97, 39, 32, 96, 101, - /* 220 */ 51, 42, 197, 197, 21, 52, 15, 197, 40, 197, - /* 230 */ 67, 55, 61, 52, 95, 94, 86, 85, 54, 97, - /* 240 */ 36, 32, 96, 101, 51, 197, 197, 197, 21, 197, - /* 250 */ 15, 197, 40, 197, 54, 48, 98, 52, 95, 94, - /* 260 */ 86, 85, 54, 97, 46, 32, 96, 101, 51, 197, - /* 270 */ 197, 197, 21, 52, 15, 197, 40, 197, 197, 65, - /* 280 */ 197, 52, 95, 94, 86, 85, 54, 97, 41, 32, - /* 290 */ 96, 101, 51, 197, 197, 197, 21, 197, 15, 197, - /* 300 */ 40, 197, 54, 197, 197, 52, 95, 94, 86, 85, - /* 310 */ 54, 97, 33, 32, 96, 101, 51, 197, 197, 197, - /* 320 */ 21, 197, 15, 197, 40, 197, 197, 197, 197, 52, - /* 330 */ 95, 94, 86, 85, 54, 97, 18, 32, 96, 101, - /* 340 */ 51, 197, 197, 197, 21, 197, 15, 197, 40, 197, - /* 350 */ 197, 197, 197, 52, 95, 94, 86, 85, 54, 97, - /* 360 */ 197, 32, 96, 101, 51, 197, 197, 197, 21, 197, - /* 370 */ 15, 197, 37, 197, 197, 197, 197, 52, 95, 94, - /* 380 */ 86, 85, 54, 97, 197, 32, 96, 101, 51, 197, - /* 390 */ 197, 197, 21, 197, 14, 197, 197, 197, 197, 197, - /* 400 */ 197, 52, 95, 94, 86, 85, 54, 97, 197, 32, - /* 410 */ 96, 101, 51, 197, 197, 197, 22, 197, 197, 197, - /* 420 */ 197, 197, 197, 197, 197, 52, 95, 94, 86, 85, - /* 430 */ 54, 97, 197, 34, 96, 101, 51, 197, 197, 197, - /* 440 */ 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, - /* 450 */ 95, 94, 86, 85, 54, + /* 0 */ 6, 84, 5, 1, 30, 28, 118, 117, 99, 32, + /* 10 */ 115, 116, 114, 112, 66, 67, 69, 68, 70, 61, + /* 20 */ 18, 19, 20, 21, 16, 34, 151, 151, 141, 32, + /* 30 */ 113, 104, 103, 95, 94, 93, 91, 92, 96, 97, + /* 40 */ 102, 101, 100, 98, 137, 136, 135, 133, 134, 138, + /* 50 */ 17, 56, 23, 56, 8, 53, 75, 51, 122, 85, + /* 60 */ 31, 40, 6, 144, 30, 13, 109, 108, 118, 117, + /* 70 */ 99, 141, 115, 116, 114, 112, 41, 123, 121, 30, + /* 80 */ 59, 72, 59, 119, 120, 124, 125, 130, 129, 128, + /* 90 */ 90, 126, 113, 104, 103, 95, 94, 93, 91, 92, + /* 100 */ 96, 97, 102, 101, 100, 98, 137, 136, 135, 133, + /* 110 */ 134, 138, 6, 10, 127, 11, 27, 35, 118, 117, + /* 120 */ 99, 74, 115, 116, 114, 112, 132, 131, 47, 139, + /* 130 */ 141, 30, 8, 143, 142, 63, 50, 58, 33, 30, + /* 140 */ 25, 52, 113, 104, 103, 95, 94, 93, 91, 92, + /* 150 */ 96, 97, 102, 101, 100, 98, 137, 136, 135, 133, + /* 160 */ 134, 138, 249, 140, 76, 56, 72, 72, 77, 72, + /* 170 */ 72, 82, 45, 36, 86, 83, 57, 56, 29, 2, + /* 180 */ 24, 9, 15, 82, 49, 37, 86, 83, 57, 64, + /* 190 */ 107, 105, 106, 110, 59, 4, 79, 72, 7, 48, + /* 200 */ 200, 111, 107, 105, 106, 110, 59, 72, 65, 12, + /* 210 */ 56, 116, 3, 32, 89, 42, 82, 39, 36, 86, + /* 220 */ 83, 57, 55, 78, 56, 24, 56, 15, 56, 49, + /* 230 */ 88, 71, 51, 60, 80, 107, 105, 106, 110, 59, + /* 240 */ 81, 56, 205, 205, 205, 205, 205, 82, 45, 36, + /* 250 */ 86, 83, 57, 59, 205, 59, 24, 59, 15, 205, + /* 260 */ 49, 205, 205, 205, 205, 205, 107, 105, 106, 110, + /* 270 */ 59, 56, 205, 56, 205, 205, 205, 87, 56, 73, + /* 280 */ 205, 205, 205, 205, 82, 39, 36, 86, 83, 57, + /* 290 */ 205, 205, 205, 24, 205, 15, 205, 49, 205, 205, + /* 300 */ 59, 62, 59, 107, 105, 106, 110, 59, 56, 205, + /* 310 */ 205, 205, 205, 205, 82, 43, 36, 86, 83, 57, + /* 320 */ 205, 205, 205, 24, 205, 15, 205, 49, 205, 205, + /* 330 */ 54, 205, 205, 107, 105, 106, 110, 59, 56, 205, + /* 340 */ 205, 205, 205, 205, 82, 22, 36, 86, 83, 57, + /* 350 */ 205, 205, 205, 24, 205, 15, 205, 49, 205, 205, + /* 360 */ 205, 205, 205, 107, 105, 106, 110, 59, 205, 56, + /* 370 */ 205, 205, 205, 205, 205, 82, 38, 36, 86, 83, + /* 380 */ 57, 205, 205, 205, 24, 205, 15, 205, 49, 205, + /* 390 */ 205, 205, 205, 56, 107, 105, 106, 110, 59, 82, + /* 400 */ 44, 36, 86, 83, 57, 205, 205, 205, 24, 205, + /* 410 */ 15, 205, 49, 205, 205, 205, 205, 56, 107, 105, + /* 420 */ 106, 110, 59, 82, 205, 36, 86, 83, 57, 205, + /* 430 */ 205, 205, 24, 205, 15, 205, 46, 205, 205, 205, + /* 440 */ 205, 205, 107, 105, 106, 110, 59, 56, 205, 205, + /* 450 */ 205, 205, 205, 82, 205, 36, 86, 83, 57, 205, + /* 460 */ 205, 205, 24, 205, 14, 205, 205, 205, 205, 205, + /* 470 */ 205, 56, 107, 105, 106, 110, 59, 82, 205, 36, + /* 480 */ 86, 83, 57, 205, 205, 205, 26, 205, 205, 205, + /* 490 */ 205, 205, 205, 205, 205, 205, 107, 105, 106, 110, + /* 500 */ 59, ); static public $yy_lookahead = array( - /* 0 */ 1, 4, 2, 3, 4, 2, 6, 10, 9, 8, - /* 10 */ 7, 4, 10, 81, 15, 16, 17, 10, 19, 20, - /* 20 */ 21, 22, 37, 38, 23, 24, 94, 95, 26, 77, - /* 30 */ 29, 30, 31, 32, 33, 34, 35, 36, 39, 40, - /* 40 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - /* 50 */ 51, 52, 53, 54, 55, 56, 57, 58, 9, 64, - /* 60 */ 11, 5, 67, 65, 15, 16, 17, 26, 19, 20, - /* 70 */ 21, 22, 9, 12, 13, 14, 15, 16, 17, 25, - /* 80 */ 63, 63, 63, 63, 63, 68, 69, 26, 39, 40, - /* 90 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - /* 100 */ 51, 52, 53, 54, 55, 56, 57, 58, 9, 92, - /* 110 */ 92, 92, 92, 92, 15, 16, 17, 84, 19, 20, - /* 120 */ 21, 22, 64, 64, 8, 67, 67, 3, 4, 27, - /* 130 */ 28, 2, 18, 63, 63, 6, 66, 63, 39, 40, - /* 140 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - /* 150 */ 51, 52, 53, 54, 55, 56, 57, 58, 60, 61, - /* 160 */ 62, 63, 92, 92, 83, 65, 92, 69, 70, 71, - /* 170 */ 72, 73, 74, 18, 64, 92, 78, 67, 80, 64, - /* 180 */ 82, 7, 67, 20, 79, 63, 88, 89, 90, 91, - /* 190 */ 92, 69, 70, 71, 72, 73, 74, 75, 93, 9, - /* 200 */ 78, 6, 80, 84, 82, 65, 87, 65, 86, 63, - /* 210 */ 88, 89, 90, 91, 92, 69, 70, 71, 72, 73, - /* 220 */ 74, 76, 96, 96, 78, 63, 80, 96, 82, 96, - /* 230 */ 68, 69, 86, 63, 88, 89, 90, 91, 92, 69, - /* 240 */ 70, 71, 72, 73, 74, 96, 96, 96, 78, 96, - /* 250 */ 80, 96, 82, 96, 92, 85, 62, 63, 88, 89, - /* 260 */ 90, 91, 92, 69, 70, 71, 72, 73, 74, 96, - /* 270 */ 96, 96, 78, 63, 80, 96, 82, 96, 96, 69, - /* 280 */ 96, 63, 88, 89, 90, 91, 92, 69, 70, 71, - /* 290 */ 72, 73, 74, 96, 96, 96, 78, 96, 80, 96, - /* 300 */ 82, 96, 92, 96, 96, 63, 88, 89, 90, 91, - /* 310 */ 92, 69, 70, 71, 72, 73, 74, 96, 96, 96, - /* 320 */ 78, 96, 80, 96, 82, 96, 96, 96, 96, 63, - /* 330 */ 88, 89, 90, 91, 92, 69, 70, 71, 72, 73, - /* 340 */ 74, 96, 96, 96, 78, 96, 80, 96, 82, 96, - /* 350 */ 96, 96, 96, 63, 88, 89, 90, 91, 92, 69, - /* 360 */ 96, 71, 72, 73, 74, 96, 96, 96, 78, 96, - /* 370 */ 80, 96, 82, 96, 96, 96, 96, 63, 88, 89, - /* 380 */ 90, 91, 92, 69, 96, 71, 72, 73, 74, 96, - /* 390 */ 96, 96, 78, 96, 80, 96, 96, 96, 96, 96, - /* 400 */ 96, 63, 88, 89, 90, 91, 92, 69, 96, 71, - /* 410 */ 72, 73, 74, 96, 96, 96, 78, 96, 96, 96, - /* 420 */ 96, 96, 96, 96, 96, 63, 88, 89, 90, 91, - /* 430 */ 92, 69, 96, 71, 72, 73, 74, 96, 96, 96, - /* 440 */ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - /* 450 */ 88, 89, 90, 91, 92, + /* 0 */ 13, 68, 15, 13, 71, 2, 19, 20, 21, 6, + /* 10 */ 23, 24, 25, 26, 16, 17, 18, 19, 20, 21, + /* 20 */ 8, 9, 10, 11, 12, 2, 3, 4, 30, 6, + /* 30 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + /* 40 */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + /* 50 */ 1, 67, 67, 67, 88, 70, 72, 73, 8, 73, + /* 60 */ 2, 68, 13, 29, 71, 7, 41, 42, 19, 20, + /* 70 */ 21, 30, 23, 24, 25, 26, 68, 27, 28, 71, + /* 80 */ 96, 96, 96, 33, 34, 35, 36, 37, 38, 39, + /* 90 */ 40, 81, 43, 44, 45, 46, 47, 48, 49, 50, + /* 100 */ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + /* 110 */ 61, 62, 13, 85, 14, 83, 3, 4, 19, 20, + /* 120 */ 21, 69, 23, 24, 25, 26, 98, 99, 68, 97, + /* 130 */ 30, 71, 88, 31, 32, 91, 68, 67, 67, 71, + /* 140 */ 67, 67, 43, 44, 45, 46, 47, 48, 49, 50, + /* 150 */ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + /* 160 */ 61, 62, 64, 65, 66, 67, 96, 96, 67, 96, + /* 170 */ 96, 73, 74, 75, 76, 77, 78, 67, 67, 4, + /* 180 */ 82, 87, 84, 73, 86, 75, 76, 77, 78, 14, + /* 190 */ 92, 93, 94, 95, 96, 5, 69, 96, 4, 22, + /* 200 */ 22, 96, 92, 93, 94, 95, 96, 96, 14, 7, + /* 210 */ 67, 24, 13, 6, 69, 80, 73, 74, 75, 76, + /* 220 */ 77, 78, 79, 69, 67, 82, 67, 84, 67, 86, + /* 230 */ 73, 72, 73, 90, 73, 92, 93, 94, 95, 96, + /* 240 */ 66, 67, 100, 100, 100, 100, 100, 73, 74, 75, + /* 250 */ 76, 77, 78, 96, 100, 96, 82, 96, 84, 100, + /* 260 */ 86, 100, 100, 100, 100, 100, 92, 93, 94, 95, + /* 270 */ 96, 67, 100, 67, 100, 100, 100, 73, 67, 73, + /* 280 */ 100, 100, 100, 100, 73, 74, 75, 76, 77, 78, + /* 290 */ 100, 100, 100, 82, 100, 84, 100, 86, 100, 100, + /* 300 */ 96, 90, 96, 92, 93, 94, 95, 96, 67, 100, + /* 310 */ 100, 100, 100, 100, 73, 74, 75, 76, 77, 78, + /* 320 */ 100, 100, 100, 82, 100, 84, 100, 86, 100, 100, + /* 330 */ 89, 100, 100, 92, 93, 94, 95, 96, 67, 100, + /* 340 */ 100, 100, 100, 100, 73, 74, 75, 76, 77, 78, + /* 350 */ 100, 100, 100, 82, 100, 84, 100, 86, 100, 100, + /* 360 */ 100, 100, 100, 92, 93, 94, 95, 96, 100, 67, + /* 370 */ 100, 100, 100, 100, 100, 73, 74, 75, 76, 77, + /* 380 */ 78, 100, 100, 100, 82, 100, 84, 100, 86, 100, + /* 390 */ 100, 100, 100, 67, 92, 93, 94, 95, 96, 73, + /* 400 */ 74, 75, 76, 77, 78, 100, 100, 100, 82, 100, + /* 410 */ 84, 100, 86, 100, 100, 100, 100, 67, 92, 93, + /* 420 */ 94, 95, 96, 73, 100, 75, 76, 77, 78, 100, + /* 430 */ 100, 100, 82, 100, 84, 100, 86, 100, 100, 100, + /* 440 */ 100, 100, 92, 93, 94, 95, 96, 67, 100, 100, + /* 450 */ 100, 100, 100, 73, 100, 75, 76, 77, 78, 100, + /* 460 */ 100, 100, 82, 100, 84, 100, 100, 100, 100, 100, + /* 470 */ 100, 67, 92, 93, 94, 95, 96, 73, 100, 75, + /* 480 */ 76, 77, 78, 100, 100, 100, 82, 100, 100, 100, + /* 490 */ 100, 100, 100, 100, 100, 100, 92, 93, 94, 95, + /* 500 */ 96, ); - const YY_SHIFT_USE_DFLT = -16; - const YY_SHIFT_MAX = 55; + const YY_SHIFT_USE_DFLT = -14; + const YY_SHIFT_MAX = 59; static public $yy_shift_ofst = array( - /* 0 */ -1, 49, 49, 99, 99, 99, 99, 99, 99, 99, - /* 10 */ 99, 99, 163, 163, 1, 1, 163, 163, 61, 0, - /* 20 */ 129, 102, 102, 163, 195, 163, 195, 163, 163, 163, - /* 30 */ 195, 163, -15, 2, -15, 56, 41, 54, 56, 41, - /* 40 */ 54, 41, 63, 56, 163, 56, 41, 7, -3, 124, - /* 50 */ 3, 190, 155, 174, 114, 116, + /* 0 */ 49, -13, -13, 99, 99, 99, 99, 99, 99, 99, + /* 10 */ 99, 99, 187, 187, 50, 50, 187, 187, 187, 187, + /* 20 */ 187, 187, -2, 23, 102, 3, 102, 187, 187, 207, + /* 30 */ 207, 187, 187, 207, 187, 187, 25, 25, 100, 41, + /* 40 */ 190, 190, 199, 41, 41, 41, 34, 190, 187, 34, + /* 50 */ 190, 12, 58, 113, 194, 175, 177, -10, 202, 178, ); - const YY_REDUCE_USE_DFLT = -69; - const YY_REDUCE_MAX = 46; + const YY_REDUCE_USE_DFLT = -68; + const YY_REDUCE_MAX = 50; static public $yy_reduce_ofst = array( - /* 0 */ 98, 122, 146, 194, 170, 242, 218, 266, 290, 314, - /* 10 */ 338, 362, 162, 17, -68, -68, 210, 70, 119, 115, - /* 20 */ 110, 105, 105, 74, 58, 18, -5, 19, 71, 21, - /* 30 */ 59, 20, 145, 33, 145, 142, 33, 81, 140, 33, - /* 40 */ 81, 33, -48, -2, 83, 100, 33, + /* 0 */ 98, 143, 211, 241, 174, 271, 302, 326, 350, 380, + /* 10 */ 404, 110, 159, -16, 28, 28, 206, -15, -14, 157, + /* 20 */ 204, 161, 44, 60, 32, 68, 32, 73, 71, 8, + /* 30 */ -67, 70, 74, -7, 111, 101, 135, 135, -34, -34, + /* 40 */ 154, 145, 10, -34, -34, -34, 94, 52, 105, 94, + /* 50 */ 127, ); static public $yyExpectedTokens = array( - /* 0 */ array(1, 9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 1 */ array(9, 11, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 2 */ array(9, 11, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 3 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 4 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 5 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 6 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 7 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 8 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 9 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 10 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 11 */ array(9, 15, 16, 17, 19, 20, 21, 22, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, ), - /* 12 */ array(20, ), - /* 13 */ array(20, ), - /* 14 */ array(8, 23, 24, 29, 30, 31, 32, 33, 34, 35, 36, ), - /* 15 */ array(8, 23, 24, 29, 30, 31, 32, 33, 34, 35, 36, ), - /* 16 */ array(20, ), - /* 17 */ array(20, ), - /* 18 */ array(12, 13, 14, 15, 16, 17, 26, ), - /* 19 */ array(2, 3, 4, 6, ), - /* 20 */ array(2, 6, ), - /* 21 */ array(27, 28, ), - /* 22 */ array(27, 28, ), - /* 23 */ array(20, ), - /* 24 */ array(6, ), - /* 25 */ array(20, ), - /* 26 */ array(6, ), - /* 27 */ array(20, ), - /* 28 */ array(20, ), - /* 29 */ array(20, ), + /* 0 */ array(1, 13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 1 */ array(13, 15, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 2 */ array(13, 15, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 3 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 4 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 5 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 6 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 7 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 8 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 9 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 10 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 11 */ array(13, 19, 20, 21, 23, 24, 25, 26, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, ), + /* 12 */ array(24, ), + /* 13 */ array(24, ), + /* 14 */ array(8, 27, 28, 33, 34, 35, 36, 37, 38, 39, 40, ), + /* 15 */ array(8, 27, 28, 33, 34, 35, 36, 37, 38, 39, 40, ), + /* 16 */ array(24, ), + /* 17 */ array(24, ), + /* 18 */ array(24, ), + /* 19 */ array(24, ), + /* 20 */ array(24, ), + /* 21 */ array(24, ), + /* 22 */ array(16, 17, 18, 19, 20, 21, 30, ), + /* 23 */ array(2, 3, 4, 6, ), + /* 24 */ array(31, 32, ), + /* 25 */ array(2, 6, ), + /* 26 */ array(31, 32, ), + /* 27 */ array(24, ), + /* 28 */ array(24, ), + /* 29 */ array(6, ), /* 30 */ array(6, ), - /* 31 */ array(20, ), - /* 32 */ array(37, 38, ), - /* 33 */ array(10, 26, ), - /* 34 */ array(37, 38, ), - /* 35 */ array(5, ), - /* 36 */ array(26, ), - /* 37 */ array(25, ), - /* 38 */ array(5, ), - /* 39 */ array(26, ), - /* 40 */ array(25, ), - /* 41 */ array(26, ), - /* 42 */ array(9, ), - /* 43 */ array(5, ), - /* 44 */ array(20, ), - /* 45 */ array(5, ), - /* 46 */ array(26, ), - /* 47 */ array(4, 10, ), - /* 48 */ array(4, 10, ), - /* 49 */ array(3, 4, ), - /* 50 */ array(2, 7, ), - /* 51 */ array(9, ), - /* 52 */ array(18, ), - /* 53 */ array(7, ), - /* 54 */ array(18, ), - /* 55 */ array(8, ), - /* 56 */ array(), - /* 57 */ array(), - /* 58 */ array(), - /* 59 */ array(), + /* 31 */ array(24, ), + /* 32 */ array(24, ), + /* 33 */ array(6, ), + /* 34 */ array(24, ), + /* 35 */ array(24, ), + /* 36 */ array(41, 42, ), + /* 37 */ array(41, 42, ), + /* 38 */ array(14, 30, ), + /* 39 */ array(30, ), + /* 40 */ array(5, ), + /* 41 */ array(5, ), + /* 42 */ array(13, ), + /* 43 */ array(30, ), + /* 44 */ array(30, ), + /* 45 */ array(30, ), + /* 46 */ array(29, ), + /* 47 */ array(5, ), + /* 48 */ array(24, ), + /* 49 */ array(29, ), + /* 50 */ array(5, ), + /* 51 */ array(8, 9, 10, 11, 12, ), + /* 52 */ array(2, 7, ), + /* 53 */ array(3, 4, ), + /* 54 */ array(4, 14, ), + /* 55 */ array(4, 14, ), + /* 56 */ array(22, ), + /* 57 */ array(13, ), + /* 58 */ array(7, ), + /* 59 */ array(22, ), /* 60 */ array(), /* 61 */ array(), /* 62 */ array(), @@ -477,22 +492,31 @@ static public $yy_action = array( /* 134 */ array(), /* 135 */ array(), /* 136 */ array(), + /* 137 */ array(), + /* 138 */ array(), + /* 139 */ array(), + /* 140 */ array(), + /* 141 */ array(), + /* 142 */ array(), + /* 143 */ array(), + /* 144 */ array(), ); static public $yy_default = array( - /* 0 */ 236, 171, 236, 236, 236, 236, 236, 236, 236, 236, - /* 10 */ 236, 236, 236, 236, 165, 164, 236, 236, 236, 149, - /* 20 */ 149, 162, 163, 236, 148, 236, 149, 236, 236, 236, - /* 30 */ 149, 236, 160, 236, 161, 146, 169, 167, 146, 174, - /* 40 */ 166, 170, 236, 146, 236, 146, 153, 236, 236, 236, - /* 50 */ 236, 236, 236, 236, 186, 236, 168, 210, 207, 208, - /* 60 */ 209, 173, 199, 150, 188, 152, 206, 151, 141, 144, - /* 70 */ 142, 147, 140, 178, 177, 176, 179, 180, 172, 181, - /* 80 */ 175, 201, 190, 189, 187, 185, 184, 191, 192, 216, - /* 90 */ 217, 215, 214, 213, 183, 182, 154, 155, 145, 139, - /* 100 */ 138, 156, 157, 211, 212, 159, 200, 158, 218, 219, - /* 110 */ 137, 202, 193, 235, 234, 194, 195, 203, 204, 198, - /* 120 */ 197, 196, 233, 232, 223, 224, 222, 221, 220, 225, - /* 130 */ 226, 230, 231, 229, 228, 227, 205, + /* 0 */ 248, 183, 248, 248, 248, 248, 248, 248, 248, 248, + /* 10 */ 248, 248, 248, 248, 177, 176, 248, 248, 248, 248, + /* 20 */ 248, 248, 248, 157, 174, 157, 175, 248, 248, 157, + /* 30 */ 156, 248, 248, 157, 248, 248, 172, 173, 248, 186, + /* 40 */ 154, 154, 248, 181, 182, 165, 179, 154, 248, 178, + /* 50 */ 154, 248, 248, 248, 248, 248, 248, 248, 248, 198, + /* 60 */ 184, 193, 185, 187, 169, 180, 188, 189, 191, 190, + /* 70 */ 192, 158, 200, 164, 147, 159, 146, 152, 150, 149, + /* 80 */ 163, 153, 167, 168, 155, 160, 166, 162, 161, 148, + /* 90 */ 222, 231, 232, 230, 229, 228, 233, 234, 238, 239, + /* 100 */ 237, 236, 235, 227, 226, 195, 196, 194, 224, 223, + /* 110 */ 197, 199, 204, 225, 203, 201, 202, 240, 241, 215, + /* 120 */ 216, 210, 209, 208, 217, 218, 171, 170, 221, 220, + /* 130 */ 219, 207, 206, 245, 246, 244, 243, 242, 247, 205, + /* 140 */ 145, 212, 214, 213, 211, ); /* The next thing included is series of defines which control ** various aspects of the generated parser. @@ -509,11 +533,11 @@ static public $yy_action = array( ** self::YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. */ - const YYNOCODE = 97; + const YYNOCODE = 101; const YYSTACKDEPTH = 100; - const YYNSTATE = 137; - const YYNRULE = 99; - const YYERRORSYMBOL = 59; + const YYNSTATE = 145; + const YYNRULE = 103; + const YYERRORSYMBOL = 63; const YYERRSYMDT = 'yy0'; const YYFALLBACK = 0; /** The next table maps tokens into fallback tokens. If a construct @@ -579,7 +603,7 @@ static public $yy_action = array( /** * @var int */ - public $yyidx; /* Index of top element in stack */ + public $yyidx = -1; /* Index of top element in stack */ /** * @var int */ @@ -597,7 +621,8 @@ static public $yy_action = array( static public $yyTokenName = array( '$', 'SELECT', 'AS_ALIAS', 'FROM', 'COMA', 'WHERE', 'JOIN', 'ON', - 'EQ', 'PAR_OPEN', 'PAR_CLOSE', 'INTERVAL', + 'EQ', 'BELOW', 'BELOW_STRICT', 'NOT_BELOW', + 'NOT_BELOW_STRICT', 'PAR_OPEN', 'PAR_CLOSE', 'INTERVAL', 'F_SECOND', 'F_MINUTE', 'F_HOUR', 'F_DAY', 'F_MONTH', 'F_YEAR', 'DOT', 'VARNAME', 'NAME', 'NUMVAL', 'STRVAL', 'REGEXP', @@ -642,89 +667,93 @@ static public $yy_action = array( /* 13 */ "join_item ::= JOIN class_name AS_ALIAS class_name ON join_condition", /* 14 */ "join_item ::= JOIN class_name ON join_condition", /* 15 */ "join_condition ::= field_id EQ field_id", - /* 16 */ "condition ::= expression_prio4", - /* 17 */ "expression_basic ::= scalar", - /* 18 */ "expression_basic ::= field_id", - /* 19 */ "expression_basic ::= var_name", - /* 20 */ "expression_basic ::= func_name PAR_OPEN arg_list PAR_CLOSE", - /* 21 */ "expression_basic ::= PAR_OPEN expression_prio4 PAR_CLOSE", - /* 22 */ "expression_basic ::= expression_basic list_operator list", - /* 23 */ "expression_prio1 ::= expression_basic", - /* 24 */ "expression_prio1 ::= expression_prio1 operator1 expression_basic", - /* 25 */ "expression_prio2 ::= expression_prio1", - /* 26 */ "expression_prio2 ::= expression_prio2 operator2 expression_prio1", - /* 27 */ "expression_prio3 ::= expression_prio2", - /* 28 */ "expression_prio3 ::= expression_prio3 operator3 expression_prio2", - /* 29 */ "expression_prio4 ::= expression_prio3", - /* 30 */ "expression_prio4 ::= expression_prio4 operator4 expression_prio3", - /* 31 */ "list ::= PAR_OPEN list_items PAR_CLOSE", - /* 32 */ "list_items ::= expression_prio4", - /* 33 */ "list_items ::= list_items COMA expression_prio4", - /* 34 */ "arg_list ::=", - /* 35 */ "arg_list ::= argument", - /* 36 */ "arg_list ::= arg_list COMA argument", - /* 37 */ "argument ::= expression_prio4", - /* 38 */ "argument ::= INTERVAL expression_prio4 interval_unit", - /* 39 */ "interval_unit ::= F_SECOND", - /* 40 */ "interval_unit ::= F_MINUTE", - /* 41 */ "interval_unit ::= F_HOUR", - /* 42 */ "interval_unit ::= F_DAY", - /* 43 */ "interval_unit ::= F_MONTH", - /* 44 */ "interval_unit ::= F_YEAR", - /* 45 */ "scalar ::= num_scalar", - /* 46 */ "scalar ::= str_scalar", - /* 47 */ "num_scalar ::= num_value", - /* 48 */ "str_scalar ::= str_value", - /* 49 */ "field_id ::= name", - /* 50 */ "field_id ::= class_name DOT name", - /* 51 */ "class_name ::= name", - /* 52 */ "var_name ::= VARNAME", - /* 53 */ "name ::= NAME", - /* 54 */ "num_value ::= NUMVAL", - /* 55 */ "str_value ::= STRVAL", - /* 56 */ "operator1 ::= num_operator1", - /* 57 */ "operator2 ::= num_operator2", - /* 58 */ "operator2 ::= str_operator", - /* 59 */ "operator2 ::= REGEXP", - /* 60 */ "operator2 ::= EQ", - /* 61 */ "operator2 ::= NOT_EQ", - /* 62 */ "operator3 ::= LOG_AND", - /* 63 */ "operator4 ::= LOG_OR", - /* 64 */ "num_operator1 ::= MATH_DIV", - /* 65 */ "num_operator1 ::= MATH_MULT", - /* 66 */ "num_operator2 ::= MATH_PLUS", - /* 67 */ "num_operator2 ::= MATH_MINUS", - /* 68 */ "num_operator2 ::= GT", - /* 69 */ "num_operator2 ::= LT", - /* 70 */ "num_operator2 ::= GE", - /* 71 */ "num_operator2 ::= LE", - /* 72 */ "str_operator ::= LIKE", - /* 73 */ "str_operator ::= NOT_LIKE", - /* 74 */ "list_operator ::= IN", - /* 75 */ "list_operator ::= NOT_IN", - /* 76 */ "func_name ::= F_IF", - /* 77 */ "func_name ::= F_ELT", - /* 78 */ "func_name ::= F_COALESCE", - /* 79 */ "func_name ::= F_ISNULL", - /* 80 */ "func_name ::= F_CONCAT", - /* 81 */ "func_name ::= F_SUBSTR", - /* 82 */ "func_name ::= F_TRIM", - /* 83 */ "func_name ::= F_DATE", - /* 84 */ "func_name ::= F_DATE_FORMAT", - /* 85 */ "func_name ::= F_CURRENT_DATE", - /* 86 */ "func_name ::= F_NOW", - /* 87 */ "func_name ::= F_TIME", - /* 88 */ "func_name ::= F_TO_DAYS", - /* 89 */ "func_name ::= F_FROM_DAYS", - /* 90 */ "func_name ::= F_YEAR", - /* 91 */ "func_name ::= F_MONTH", - /* 92 */ "func_name ::= F_DAY", - /* 93 */ "func_name ::= F_DATE_ADD", - /* 94 */ "func_name ::= F_DATE_SUB", - /* 95 */ "func_name ::= F_ROUND", - /* 96 */ "func_name ::= F_FLOOR", - /* 97 */ "func_name ::= F_INET_ATON", - /* 98 */ "func_name ::= F_INET_NTOA", + /* 16 */ "join_condition ::= field_id BELOW field_id", + /* 17 */ "join_condition ::= field_id BELOW_STRICT field_id", + /* 18 */ "join_condition ::= field_id NOT_BELOW field_id", + /* 19 */ "join_condition ::= field_id NOT_BELOW_STRICT field_id", + /* 20 */ "condition ::= expression_prio4", + /* 21 */ "expression_basic ::= scalar", + /* 22 */ "expression_basic ::= field_id", + /* 23 */ "expression_basic ::= var_name", + /* 24 */ "expression_basic ::= func_name PAR_OPEN arg_list PAR_CLOSE", + /* 25 */ "expression_basic ::= PAR_OPEN expression_prio4 PAR_CLOSE", + /* 26 */ "expression_basic ::= expression_basic list_operator list", + /* 27 */ "expression_prio1 ::= expression_basic", + /* 28 */ "expression_prio1 ::= expression_prio1 operator1 expression_basic", + /* 29 */ "expression_prio2 ::= expression_prio1", + /* 30 */ "expression_prio2 ::= expression_prio2 operator2 expression_prio1", + /* 31 */ "expression_prio3 ::= expression_prio2", + /* 32 */ "expression_prio3 ::= expression_prio3 operator3 expression_prio2", + /* 33 */ "expression_prio4 ::= expression_prio3", + /* 34 */ "expression_prio4 ::= expression_prio4 operator4 expression_prio3", + /* 35 */ "list ::= PAR_OPEN list_items PAR_CLOSE", + /* 36 */ "list_items ::= expression_prio4", + /* 37 */ "list_items ::= list_items COMA expression_prio4", + /* 38 */ "arg_list ::=", + /* 39 */ "arg_list ::= argument", + /* 40 */ "arg_list ::= arg_list COMA argument", + /* 41 */ "argument ::= expression_prio4", + /* 42 */ "argument ::= INTERVAL expression_prio4 interval_unit", + /* 43 */ "interval_unit ::= F_SECOND", + /* 44 */ "interval_unit ::= F_MINUTE", + /* 45 */ "interval_unit ::= F_HOUR", + /* 46 */ "interval_unit ::= F_DAY", + /* 47 */ "interval_unit ::= F_MONTH", + /* 48 */ "interval_unit ::= F_YEAR", + /* 49 */ "scalar ::= num_scalar", + /* 50 */ "scalar ::= str_scalar", + /* 51 */ "num_scalar ::= num_value", + /* 52 */ "str_scalar ::= str_value", + /* 53 */ "field_id ::= name", + /* 54 */ "field_id ::= class_name DOT name", + /* 55 */ "class_name ::= name", + /* 56 */ "var_name ::= VARNAME", + /* 57 */ "name ::= NAME", + /* 58 */ "num_value ::= NUMVAL", + /* 59 */ "str_value ::= STRVAL", + /* 60 */ "operator1 ::= num_operator1", + /* 61 */ "operator2 ::= num_operator2", + /* 62 */ "operator2 ::= str_operator", + /* 63 */ "operator2 ::= REGEXP", + /* 64 */ "operator2 ::= EQ", + /* 65 */ "operator2 ::= NOT_EQ", + /* 66 */ "operator3 ::= LOG_AND", + /* 67 */ "operator4 ::= LOG_OR", + /* 68 */ "num_operator1 ::= MATH_DIV", + /* 69 */ "num_operator1 ::= MATH_MULT", + /* 70 */ "num_operator2 ::= MATH_PLUS", + /* 71 */ "num_operator2 ::= MATH_MINUS", + /* 72 */ "num_operator2 ::= GT", + /* 73 */ "num_operator2 ::= LT", + /* 74 */ "num_operator2 ::= GE", + /* 75 */ "num_operator2 ::= LE", + /* 76 */ "str_operator ::= LIKE", + /* 77 */ "str_operator ::= NOT_LIKE", + /* 78 */ "list_operator ::= IN", + /* 79 */ "list_operator ::= NOT_IN", + /* 80 */ "func_name ::= F_IF", + /* 81 */ "func_name ::= F_ELT", + /* 82 */ "func_name ::= F_COALESCE", + /* 83 */ "func_name ::= F_ISNULL", + /* 84 */ "func_name ::= F_CONCAT", + /* 85 */ "func_name ::= F_SUBSTR", + /* 86 */ "func_name ::= F_TRIM", + /* 87 */ "func_name ::= F_DATE", + /* 88 */ "func_name ::= F_DATE_FORMAT", + /* 89 */ "func_name ::= F_CURRENT_DATE", + /* 90 */ "func_name ::= F_NOW", + /* 91 */ "func_name ::= F_TIME", + /* 92 */ "func_name ::= F_TO_DAYS", + /* 93 */ "func_name ::= F_FROM_DAYS", + /* 94 */ "func_name ::= F_YEAR", + /* 95 */ "func_name ::= F_MONTH", + /* 96 */ "func_name ::= F_DAY", + /* 97 */ "func_name ::= F_DATE_ADD", + /* 98 */ "func_name ::= F_DATE_SUB", + /* 99 */ "func_name ::= F_ROUND", + /* 100 */ "func_name ::= F_FLOOR", + /* 101 */ "func_name ::= F_INET_ATON", + /* 102 */ "func_name ::= F_INET_NTOA", ); /** @@ -1067,7 +1096,7 @@ static public $yy_action = array( fprintf(self::$yyTraceFILE, "%sShift %d\n", self::$yyTracePrompt, $yyNewState); fprintf(self::$yyTraceFILE, "%sStack:", self::$yyTracePrompt); - for($i = 1; $i <= $this->yyidx; $i++) { + for ($i = 1; $i <= $this->yyidx; $i++) { fprintf(self::$yyTraceFILE, " %s", self::$yyTokenName[$this->yystack[$i]->major]); } @@ -1089,105 +1118,109 @@ static public $yy_action = array( * */ static public $yyRuleInfo = array( - array( 'lhs' => 60, 'rhs' => 1 ), - array( 'lhs' => 60, 'rhs' => 1 ), - array( 'lhs' => 61, 'rhs' => 4 ), - array( 'lhs' => 61, 'rhs' => 6 ), - array( 'lhs' => 61, 'rhs' => 6 ), - array( 'lhs' => 61, 'rhs' => 8 ), - array( 'lhs' => 66, 'rhs' => 1 ), - array( 'lhs' => 66, 'rhs' => 3 ), - array( 'lhs' => 65, 'rhs' => 2 ), - array( 'lhs' => 65, 'rhs' => 0 ), - array( 'lhs' => 64, 'rhs' => 2 ), array( 'lhs' => 64, 'rhs' => 1 ), - array( 'lhs' => 64, 'rhs' => 0 ), - array( 'lhs' => 67, 'rhs' => 6 ), - array( 'lhs' => 67, 'rhs' => 4 ), - array( 'lhs' => 68, 'rhs' => 3 ), - array( 'lhs' => 62, 'rhs' => 1 ), - array( 'lhs' => 71, 'rhs' => 1 ), - array( 'lhs' => 71, 'rhs' => 1 ), - array( 'lhs' => 71, 'rhs' => 1 ), - array( 'lhs' => 71, 'rhs' => 4 ), - array( 'lhs' => 71, 'rhs' => 3 ), - array( 'lhs' => 71, 'rhs' => 3 ), - array( 'lhs' => 78, 'rhs' => 1 ), - array( 'lhs' => 78, 'rhs' => 3 ), - array( 'lhs' => 80, 'rhs' => 1 ), - array( 'lhs' => 80, 'rhs' => 3 ), - array( 'lhs' => 82, 'rhs' => 1 ), - array( 'lhs' => 82, 'rhs' => 3 ), + array( 'lhs' => 64, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 4 ), + array( 'lhs' => 65, 'rhs' => 6 ), + array( 'lhs' => 65, 'rhs' => 6 ), + array( 'lhs' => 65, 'rhs' => 8 ), array( 'lhs' => 70, 'rhs' => 1 ), array( 'lhs' => 70, 'rhs' => 3 ), - array( 'lhs' => 77, 'rhs' => 3 ), - array( 'lhs' => 85, 'rhs' => 1 ), - array( 'lhs' => 85, 'rhs' => 3 ), - array( 'lhs' => 75, 'rhs' => 0 ), + array( 'lhs' => 69, 'rhs' => 2 ), + array( 'lhs' => 69, 'rhs' => 0 ), + array( 'lhs' => 68, 'rhs' => 2 ), + array( 'lhs' => 68, 'rhs' => 1 ), + array( 'lhs' => 68, 'rhs' => 0 ), + array( 'lhs' => 71, 'rhs' => 6 ), + array( 'lhs' => 71, 'rhs' => 4 ), + array( 'lhs' => 72, 'rhs' => 3 ), + array( 'lhs' => 72, 'rhs' => 3 ), + array( 'lhs' => 72, 'rhs' => 3 ), + array( 'lhs' => 72, 'rhs' => 3 ), + array( 'lhs' => 72, 'rhs' => 3 ), + array( 'lhs' => 66, 'rhs' => 1 ), array( 'lhs' => 75, 'rhs' => 1 ), + array( 'lhs' => 75, 'rhs' => 1 ), + array( 'lhs' => 75, 'rhs' => 1 ), + array( 'lhs' => 75, 'rhs' => 4 ), array( 'lhs' => 75, 'rhs' => 3 ), + array( 'lhs' => 75, 'rhs' => 3 ), + array( 'lhs' => 82, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 3 ), + array( 'lhs' => 84, 'rhs' => 1 ), + array( 'lhs' => 84, 'rhs' => 3 ), array( 'lhs' => 86, 'rhs' => 1 ), array( 'lhs' => 86, 'rhs' => 3 ), - array( 'lhs' => 87, 'rhs' => 1 ), - array( 'lhs' => 87, 'rhs' => 1 ), - array( 'lhs' => 87, 'rhs' => 1 ), - array( 'lhs' => 87, 'rhs' => 1 ), - array( 'lhs' => 87, 'rhs' => 1 ), - array( 'lhs' => 87, 'rhs' => 1 ), - array( 'lhs' => 72, 'rhs' => 1 ), - array( 'lhs' => 72, 'rhs' => 1 ), - array( 'lhs' => 88, 'rhs' => 1 ), + array( 'lhs' => 74, 'rhs' => 1 ), + array( 'lhs' => 74, 'rhs' => 3 ), + array( 'lhs' => 81, 'rhs' => 3 ), array( 'lhs' => 89, 'rhs' => 1 ), - array( 'lhs' => 69, 'rhs' => 1 ), - array( 'lhs' => 69, 'rhs' => 3 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 73, 'rhs' => 1 ), - array( 'lhs' => 92, 'rhs' => 1 ), - array( 'lhs' => 90, 'rhs' => 1 ), - array( 'lhs' => 91, 'rhs' => 1 ), + array( 'lhs' => 89, 'rhs' => 3 ), + array( 'lhs' => 79, 'rhs' => 0 ), array( 'lhs' => 79, 'rhs' => 1 ), - array( 'lhs' => 81, 'rhs' => 1 ), - array( 'lhs' => 81, 'rhs' => 1 ), - array( 'lhs' => 81, 'rhs' => 1 ), - array( 'lhs' => 81, 'rhs' => 1 ), - array( 'lhs' => 81, 'rhs' => 1 ), + array( 'lhs' => 79, 'rhs' => 3 ), + array( 'lhs' => 90, 'rhs' => 1 ), + array( 'lhs' => 90, 'rhs' => 3 ), + array( 'lhs' => 91, 'rhs' => 1 ), + array( 'lhs' => 91, 'rhs' => 1 ), + array( 'lhs' => 91, 'rhs' => 1 ), + array( 'lhs' => 91, 'rhs' => 1 ), + array( 'lhs' => 91, 'rhs' => 1 ), + array( 'lhs' => 91, 'rhs' => 1 ), + array( 'lhs' => 76, 'rhs' => 1 ), + array( 'lhs' => 76, 'rhs' => 1 ), + array( 'lhs' => 92, 'rhs' => 1 ), + array( 'lhs' => 93, 'rhs' => 1 ), + array( 'lhs' => 73, 'rhs' => 1 ), + array( 'lhs' => 73, 'rhs' => 3 ), + array( 'lhs' => 67, 'rhs' => 1 ), + array( 'lhs' => 77, 'rhs' => 1 ), + array( 'lhs' => 96, 'rhs' => 1 ), + array( 'lhs' => 94, 'rhs' => 1 ), + array( 'lhs' => 95, 'rhs' => 1 ), array( 'lhs' => 83, 'rhs' => 1 ), - array( 'lhs' => 84, 'rhs' => 1 ), - array( 'lhs' => 93, 'rhs' => 1 ), - array( 'lhs' => 93, 'rhs' => 1 ), - array( 'lhs' => 94, 'rhs' => 1 ), - array( 'lhs' => 94, 'rhs' => 1 ), - array( 'lhs' => 94, 'rhs' => 1 ), - array( 'lhs' => 94, 'rhs' => 1 ), - array( 'lhs' => 94, 'rhs' => 1 ), - array( 'lhs' => 94, 'rhs' => 1 ), - array( 'lhs' => 95, 'rhs' => 1 ), - array( 'lhs' => 95, 'rhs' => 1 ), - array( 'lhs' => 76, 'rhs' => 1 ), - array( 'lhs' => 76, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 87, 'rhs' => 1 ), + array( 'lhs' => 88, 'rhs' => 1 ), + array( 'lhs' => 97, 'rhs' => 1 ), + array( 'lhs' => 97, 'rhs' => 1 ), + array( 'lhs' => 98, 'rhs' => 1 ), + array( 'lhs' => 98, 'rhs' => 1 ), + array( 'lhs' => 98, 'rhs' => 1 ), + array( 'lhs' => 98, 'rhs' => 1 ), + array( 'lhs' => 98, 'rhs' => 1 ), + array( 'lhs' => 98, 'rhs' => 1 ), + array( 'lhs' => 99, 'rhs' => 1 ), + array( 'lhs' => 99, 'rhs' => 1 ), + array( 'lhs' => 80, 'rhs' => 1 ), + array( 'lhs' => 80, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), ); /** @@ -1204,11 +1237,11 @@ static public $yy_action = array( 4 => 4, 5 => 5, 6 => 6, - 32 => 6, - 35 => 6, + 36 => 6, + 39 => 6, 7 => 7, - 33 => 7, - 36 => 7, + 37 => 7, + 40 => 7, 8 => 8, 9 => 9, 12 => 9, @@ -1218,84 +1251,88 @@ static public $yy_action = array( 14 => 14, 15 => 15, 16 => 16, - 17 => 16, - 18 => 16, - 19 => 16, - 23 => 16, - 25 => 16, - 27 => 16, - 29 => 16, - 37 => 16, - 39 => 16, - 40 => 16, - 41 => 16, - 42 => 16, - 43 => 16, - 44 => 16, - 45 => 16, - 46 => 16, + 17 => 17, + 18 => 18, + 19 => 19, 20 => 20, - 21 => 21, - 22 => 22, - 24 => 22, - 26 => 22, - 28 => 22, - 30 => 22, - 31 => 31, - 34 => 34, + 21 => 20, + 22 => 20, + 23 => 20, + 27 => 20, + 29 => 20, + 31 => 20, + 33 => 20, + 41 => 20, + 43 => 20, + 44 => 20, + 45 => 20, + 46 => 20, + 47 => 20, + 48 => 20, + 49 => 20, + 50 => 20, + 24 => 24, + 25 => 25, + 26 => 26, + 28 => 26, + 30 => 26, + 32 => 26, + 34 => 26, + 35 => 35, 38 => 38, - 47 => 47, - 48 => 47, - 49 => 49, - 50 => 50, + 42 => 42, 51 => 51, - 76 => 51, - 77 => 51, - 78 => 51, - 79 => 51, - 80 => 51, - 81 => 51, - 82 => 51, - 83 => 51, - 84 => 51, - 85 => 51, - 86 => 51, - 87 => 51, - 88 => 51, - 89 => 51, - 90 => 51, - 91 => 51, - 92 => 51, - 93 => 51, - 94 => 51, - 95 => 51, - 96 => 51, - 97 => 51, - 98 => 51, - 52 => 52, + 52 => 51, 53 => 53, 54 => 54, - 56 => 54, - 57 => 54, - 58 => 54, - 59 => 54, - 60 => 54, - 61 => 54, - 62 => 54, - 63 => 54, - 64 => 54, - 65 => 54, - 66 => 54, - 67 => 54, - 68 => 54, - 69 => 54, - 70 => 54, - 71 => 54, - 72 => 54, - 73 => 54, - 74 => 54, - 75 => 54, 55 => 55, + 80 => 55, + 81 => 55, + 82 => 55, + 83 => 55, + 84 => 55, + 85 => 55, + 86 => 55, + 87 => 55, + 88 => 55, + 89 => 55, + 90 => 55, + 91 => 55, + 92 => 55, + 93 => 55, + 94 => 55, + 95 => 55, + 96 => 55, + 97 => 55, + 98 => 55, + 99 => 55, + 100 => 55, + 101 => 55, + 102 => 55, + 56 => 56, + 57 => 57, + 58 => 58, + 60 => 58, + 61 => 58, + 62 => 58, + 63 => 58, + 64 => 58, + 65 => 58, + 66 => 58, + 67 => 58, + 68 => 58, + 69 => 58, + 70 => 58, + 71 => 58, + 72 => 58, + 73 => 58, + 74 => 58, + 75 => 58, + 76 => 58, + 77 => 58, + 78 => 58, + 79 => 58, + 59 => 59, ); /* Beginning here are the reduction cases. A typical example ** follows: @@ -1305,44 +1342,44 @@ static public $yy_action = array( */ #line 29 "oql-parser.y" function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; } -#line 1312 "oql-parser.php" +#line 1349 "oql-parser.php" #line 32 "oql-parser.y" function yy_r2(){ $this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor)); } -#line 1317 "oql-parser.php" +#line 1354 "oql-parser.php" #line 35 "oql-parser.y" function yy_r3(){ $this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor)); } -#line 1322 "oql-parser.php" +#line 1359 "oql-parser.php" #line 39 "oql-parser.y" function yy_r4(){ $this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -4]->minor); } -#line 1327 "oql-parser.php" +#line 1364 "oql-parser.php" #line 42 "oql-parser.y" function yy_r5(){ $this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -6]->minor); } -#line 1332 "oql-parser.php" +#line 1369 "oql-parser.php" #line 47 "oql-parser.y" function yy_r6(){ $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); } -#line 1337 "oql-parser.php" +#line 1374 "oql-parser.php" #line 50 "oql-parser.y" function yy_r7(){ array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; } -#line 1343 "oql-parser.php" +#line 1380 "oql-parser.php" #line 55 "oql-parser.y" function yy_r8(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } -#line 1346 "oql-parser.php" +#line 1383 "oql-parser.php" #line 56 "oql-parser.y" function yy_r9(){ $this->_retvalue = null; } -#line 1349 "oql-parser.php" +#line 1386 "oql-parser.php" #line 58 "oql-parser.y" function yy_r10(){ // insert the join statement on top of the existing list @@ -1350,69 +1387,81 @@ static public $yy_action = array( // and return the updated array $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } -#line 1357 "oql-parser.php" +#line 1394 "oql-parser.php" #line 64 "oql-parser.y" function yy_r11(){ $this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor); } -#line 1362 "oql-parser.php" +#line 1399 "oql-parser.php" #line 70 "oql-parser.y" function yy_r13(){ // create an array with one single item $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } -#line 1368 "oql-parser.php" +#line 1405 "oql-parser.php" #line 75 "oql-parser.y" function yy_r14(){ // create an array with one single item $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } -#line 1374 "oql-parser.php" +#line 1411 "oql-parser.php" #line 80 "oql-parser.y" function yy_r15(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); } -#line 1377 "oql-parser.php" +#line 1414 "oql-parser.php" +#line 81 "oql-parser.y" + function yy_r16(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); } +#line 1417 "oql-parser.php" #line 82 "oql-parser.y" - function yy_r16(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } -#line 1380 "oql-parser.php" -#line 87 "oql-parser.y" - function yy_r20(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } -#line 1383 "oql-parser.php" -#line 88 "oql-parser.y" - function yy_r21(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } -#line 1386 "oql-parser.php" -#line 89 "oql-parser.y" - function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } -#line 1389 "oql-parser.php" -#line 104 "oql-parser.y" - function yy_r31(){ + function yy_r17(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); } +#line 1420 "oql-parser.php" +#line 83 "oql-parser.y" + function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); } +#line 1423 "oql-parser.php" +#line 84 "oql-parser.y" + function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); } +#line 1426 "oql-parser.php" +#line 86 "oql-parser.y" + function yy_r20(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } +#line 1429 "oql-parser.php" +#line 91 "oql-parser.y" + function yy_r24(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 1432 "oql-parser.php" +#line 92 "oql-parser.y" + function yy_r25(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } +#line 1435 "oql-parser.php" +#line 93 "oql-parser.y" + function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 1438 "oql-parser.php" +#line 108 "oql-parser.y" + function yy_r35(){ $this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor); } -#line 1394 "oql-parser.php" -#line 115 "oql-parser.y" - function yy_r34(){ +#line 1443 "oql-parser.php" +#line 119 "oql-parser.y" + function yy_r38(){ $this->_retvalue = array(); } -#line 1399 "oql-parser.php" -#line 126 "oql-parser.y" - function yy_r38(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } -#line 1402 "oql-parser.php" -#line 138 "oql-parser.y" - function yy_r47(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); } -#line 1405 "oql-parser.php" -#line 141 "oql-parser.y" - function yy_r49(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); } -#line 1408 "oql-parser.php" +#line 1448 "oql-parser.php" +#line 130 "oql-parser.y" + function yy_r42(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 1451 "oql-parser.php" #line 142 "oql-parser.y" - function yy_r50(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); } -#line 1411 "oql-parser.php" -#line 143 "oql-parser.y" - function yy_r51(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } -#line 1414 "oql-parser.php" + function yy_r51(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); } +#line 1454 "oql-parser.php" +#line 145 "oql-parser.y" + function yy_r53(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); } +#line 1457 "oql-parser.php" #line 146 "oql-parser.y" - function yy_r52(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); } -#line 1417 "oql-parser.php" -#line 148 "oql-parser.y" - function yy_r53(){ + function yy_r54(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); } +#line 1460 "oql-parser.php" +#line 147 "oql-parser.y" + function yy_r55(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } +#line 1463 "oql-parser.php" +#line 150 "oql-parser.y" + function yy_r56(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); } +#line 1466 "oql-parser.php" +#line 152 "oql-parser.y" + function yy_r57(){ if ($this->yystack[$this->yyidx + 0]->minor[0] == '`') { $name = substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2); @@ -1423,13 +1472,13 @@ static public $yy_action = array( } $this->_retvalue = new OqlName($name, $this->m_iColPrev); } -#line 1430 "oql-parser.php" -#line 160 "oql-parser.y" - function yy_r54(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } -#line 1433 "oql-parser.php" -#line 161 "oql-parser.y" - function yy_r55(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); } -#line 1436 "oql-parser.php" +#line 1479 "oql-parser.php" +#line 164 "oql-parser.y" + function yy_r58(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } +#line 1482 "oql-parser.php" +#line 165 "oql-parser.y" + function yy_r59(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); } +#line 1485 "oql-parser.php" /** * placeholder for the left hand side in a reduce operation. @@ -1490,7 +1539,7 @@ static public $yy_action = array( $yygoto = self::$yyRuleInfo[$yyruleno]['lhs']; $yysize = self::$yyRuleInfo[$yyruleno]['rhs']; $this->yyidx -= $yysize; - for($i = $yysize; $i; $i--) { + for ($i = $yysize; $i; $i--) { // pop all of the right-hand side parameters array_pop($this->yystack); } @@ -1544,7 +1593,7 @@ static public $yy_action = array( #line 25 "oql-parser.y" throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN); -#line 1552 "oql-parser.php" +#line 1601 "oql-parser.php" } /** @@ -1570,9 +1619,11 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo * The first argument is the major token number. The second is * the token value string as scanned from the input. * - * @param int the token number - * @param mixed the token value - * @param mixed any extra arguments that should be passed to handlers + * @param int $yymajor the token number + * @param mixed $yytokenvalue the token value + * @param mixed ... any extra arguments that should be passed to handlers + * + * @return void */ function doParse($yymajor, $yytokenvalue) { @@ -1594,14 +1645,19 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo $yyendofinput = ($yymajor==0); if (self::$yyTraceFILE) { - fprintf(self::$yyTraceFILE, "%sInput %s\n", - self::$yyTracePrompt, self::$yyTokenName[$yymajor]); + fprintf( + self::$yyTraceFILE, + "%sInput %s\n", + self::$yyTracePrompt, + self::$yyTokenName[$yymajor] + ); } do { $yyact = $this->yy_find_shift_action($yymajor); - if ($yymajor < self::YYERRORSYMBOL && - !$this->yy_is_expected_token($yymajor)) { + if ($yymajor < self::YYERRORSYMBOL + && !$this->yy_is_expected_token($yymajor) + ) { // force a syntax error $yyact = self::YY_ERROR_ACTION; } @@ -1617,8 +1673,11 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo $this->yy_reduce($yyact - self::YYNSTATE); } elseif ($yyact == self::YY_ERROR_ACTION) { if (self::$yyTraceFILE) { - fprintf(self::$yyTraceFILE, "%sSyntax Error!\n", - self::$yyTracePrompt); + fprintf( + self::$yyTraceFILE, + "%sSyntax Error!\n", + self::$yyTracePrompt + ); } if (self::YYERRORSYMBOL) { /* A syntax error has occurred. @@ -1644,18 +1703,22 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo $this->yy_syntax_error($yymajor, $yytokenvalue); } $yymx = $this->yystack[$this->yyidx]->major; - if ($yymx == self::YYERRORSYMBOL || $yyerrorhit ){ + if ($yymx == self::YYERRORSYMBOL || $yyerrorhit ) { if (self::$yyTraceFILE) { - fprintf(self::$yyTraceFILE, "%sDiscard input token %s\n", - self::$yyTracePrompt, self::$yyTokenName[$yymajor]); + fprintf( + self::$yyTraceFILE, + "%sDiscard input token %s\n", + self::$yyTracePrompt, + self::$yyTokenName[$yymajor] + ); } $this->yy_destructor($yymajor, $yytokenvalue); $yymajor = self::YYNOCODE; } else { - while ($this->yyidx >= 0 && - $yymx != self::YYERRORSYMBOL && - ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE - ){ + while ($this->yyidx >= 0 + && $yymx != self::YYERRORSYMBOL + && ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE + ) { $this->yy_pop_parser_stack(); } if ($this->yyidx < 0 || $yymajor==0) { @@ -1696,7 +1759,8 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo } } while ($yymajor != self::YYNOCODE && $this->yyidx >= 0); } -}#line 213 "oql-parser.y" +} +#line 217 "oql-parser.y" class OQLParserException extends OQLException @@ -1761,4 +1825,4 @@ class OQLParser extends OQLParserRaw } } -#line 1771 "oql-parser.php" +#line 1834 "oql-parser.php" diff --git a/core/oql/oql-parser.y b/core/oql/oql-parser.y index 9a214b7dc..34bd6214b 100644 --- a/core/oql/oql-parser.y +++ b/core/oql/oql-parser.y @@ -78,6 +78,10 @@ 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); } +join_condition(A) ::= field_id(X) BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW', Y); } +join_condition(A) ::= field_id(X) BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW_STRICT', Y); } +join_condition(A) ::= field_id(X) NOT_BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_BELOW', Y); } +join_condition(A) ::= field_id(X) NOT_BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_BELOW_STRICT', Y); } condition(A) ::= expression_prio4(X). { A = X; } diff --git a/core/oql/oqlquery.class.inc.php b/core/oql/oqlquery.class.inc.php index 159636f38..c999f730d 100644 --- a/core/oql/oqlquery.class.inc.php +++ b/core/oql/oqlquery.class.inc.php @@ -59,6 +59,7 @@ class OqlJoinSpec protected $m_oClassAlias; protected $m_oLeftField; protected $m_oRightField; + protected $m_sOperator; protected $m_oNextJoinspec; @@ -68,6 +69,8 @@ class OqlJoinSpec $this->m_oClassAlias = $oClassAlias; $this->m_oLeftField = $oExpression->GetLeftExpr(); $this->m_oRightField = $oExpression->GetRightExpr(); + $this->m_oRightField = $oExpression->GetRightExpr(); + $this->m_sOperator = $oExpression->GetOperator(); } public function GetClass() @@ -96,6 +99,10 @@ class OqlJoinSpec { return $this->m_oRightField; } + public function GetOperator() + { + return $this->m_sOperator; + } } class BinaryOqlExpression extends BinaryExpression diff --git a/core/sqlquery.class.inc.php b/core/sqlquery.class.inc.php index 5dbe45603..06ee60b0e 100644 --- a/core/sqlquery.class.inc.php +++ b/core/sqlquery.class.inc.php @@ -172,9 +172,25 @@ class SQLQuery "righttablealias" => $sRightTableAlias ); } - public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField, $sRigthtTable = '') + public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField, $sRightTable = '') { - $this->AddJoin("inner", $oSQLQuery, $sLeftField, $sRightField, $sRigthtTable); + $this->AddJoin("inner", $oSQLQuery, $sLeftField, $sRightField, $sRightTable); + } + public function AddInnerJoinTree($oSQLQuery, $sLeftField, $sRightFieldLeft, $sRightFieldRight, $sRightTableAlias = '', $iOperatorCode = TREE_OPERATOR_BELOW) + { + assert((get_class($oSQLQuery) == __CLASS__) || is_subclass_of($oSQLQuery, __CLASS__)); + if (empty($sRightTableAlias)) + { + $sRightTableAlias = $oSQLQuery->m_sTableAlias; + } + $this->m_aJoinSelects[] = array( + "jointype" => 'inner_tree', + "select" => $oSQLQuery, + "leftfield" => $sLeftField, + "rightfield_left" => $sRightFieldLeft, + "rightfield_right" => $sRightFieldRight, + "righttablealias" => $sRightTableAlias, + "tree_operator" => $iOperatorCode); } public function AddLeftJoin($oSQLQuery, $sLeftField, $sRightField) { @@ -227,7 +243,6 @@ class SQLQuery $aSetValues = array(); $aSelectedIdFields = array(); $this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues, $aSelectedIdFields); - $sFrom = self::ClauseFrom($aFrom); $sValues = self::ClauseValues($aSetValues); $sWhere = self::ClauseWhere($oCondition, $aArgs); @@ -315,6 +330,7 @@ class SQLQuery $sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]); break; case "inner": + case "inner_tree": $sFrom .= " INNER JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`"; $sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]); $sFrom .= ") ON ".$aJoinInfo["joincondition"]; @@ -368,12 +384,12 @@ class SQLQuery // Purpose: prepare the query data, once for all private function privRender(&$aFrom, &$aFields, &$oCondition, &$aDelTables, &$aSetValues, &$aSelectedIdFields) { - $sTableAlias = $this->privRenderSingleTable($aFrom, $aFields, $aDelTables, $aSetValues, $aSelectedIdFields); + $sTableAlias = $this->privRenderSingleTable($aFrom, $aFields, $aDelTables, $aSetValues, $aSelectedIdFields, '', array('jointype' => 'first')); $oCondition = $this->m_oConditionExpr; return $sTableAlias; } - private function privRenderSingleTable(&$aFrom, &$aFields, &$aDelTables, &$aSetValues, &$aSelectedIdFields, $sJoinType = 'first', $sCallerAlias = '', $sLeftField = '', $sRightField = '', $sRightTableAlias = '') + private function privRenderSingleTable(&$aFrom, &$aFields, &$aDelTables, &$aSetValues, &$aSelectedIdFields, $sCallerAlias = '', $aJoinData) { $aActualTableFields = CMDBSource::GetTableFieldsList($this->m_sTable); @@ -381,12 +397,15 @@ class SQLQuery // Handle the various kinds of join (or first table in the list) // - if (empty($sRightTableAlias)) + if (empty($aJoinData['righttablealias'])) { $sRightTableAlias = $this->m_sTableAlias; } - $sJoinCond = "`$sCallerAlias`.`$sLeftField` = `$sRightTableAlias`.`$sRightField`"; - switch ($sJoinType) + else + { + $sRightTableAlias = $aJoinData['righttablealias']; + } + switch ($aJoinData['jointype']) { case "first": $aFrom[$this->m_sTableAlias] = array("jointype"=>"first", "tablename"=>$this->m_sTable, "joincondition"=>""); @@ -394,7 +413,33 @@ class SQLQuery case "inner": case "left": // table or tablealias ??? - $aFrom[$this->m_sTableAlias] = array("jointype"=>$sJoinType, "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond"); + $sJoinCond = "`$sCallerAlias`.`{$aJoinData['leftfield']}` = `$sRightTableAlias`.`{$aJoinData['rightfield']}`"; + $aFrom[$this->m_sTableAlias] = array("jointype"=>$aJoinData['jointype'], "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond"); + break; + case "inner_tree": + $sNodeLeft = "`$sCallerAlias`.`{$aJoinData['leftfield']}`"; + $sRootLeft = "`$sRightTableAlias`.`{$aJoinData['rightfield_left']}`"; + $sRootRight = "`$sRightTableAlias`.`{$aJoinData['rightfield_right']}`"; + switch($aJoinData['tree_operator']) + { + case TREE_OPERATOR_BELOW: + $sJoinCond = "$sNodeLeft >= $sRootLeft AND $sNodeLeft <= $sRootRight"; + break; + + case TREE_OPERATOR_BELOW_STRICT: + $sJoinCond = "$sNodeLeft > $sRootLeft AND $sNodeLeft < $sRootRight"; + break; + + case TREE_OPERATOR_NOT_BELOW: // Complementary of 'BELOW' + $sJoinCond = "$sNodeLeft < $sRootLeft OR $sNodeLeft > $sRootRight"; + break; + + case TREE_OPERATOR_NOT_BELOW_STRICT: // Complementary of BELOW_STRICT + $sJoinCond = "$sNodeLeft <= $sRootLeft OR $sNodeLeft >= $sRootRight"; + break; + + } + $aFrom[$this->m_sTableAlias] = array("jointype"=>$aJoinData['jointype'], "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond"); break; } @@ -409,6 +454,7 @@ class SQLQuery { $aDelTables[] = "`{$this->m_sTableAlias}`"; } +//echo "

in privRenderSingleTable this->m_aValues

".print_r($this->m_aValues, true)."

\n"; foreach($this->m_aValues as $sFieldName=>$value) { $aSetValues["`{$this->m_sTableAlias}`.`$sFieldName`"] = $value; // quoted further! @@ -424,13 +470,13 @@ class SQLQuery $aTempFrom = array(); // temporary subset of 'from' specs, to be grouped in the final query foreach ($this->m_aJoinSelects as $aJoinData) { - $sJoinType = $aJoinData["jointype"]; $oRightSelect = $aJoinData["select"]; - $sLeftField = $aJoinData["leftfield"]; - $sRightField = $aJoinData["rightfield"]; - $sRightTableAlias = $aJoinData["righttablealias"]; +// $sJoinType = $aJoinData["jointype"]; +// $sLeftField = $aJoinData["leftfield"]; +// $sRightField = $aJoinData["rightfield"]; +// $sRightTableAlias = $aJoinData["righttablealias"]; - $sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $aSelectedIdFields, $sJoinType, $this->m_sTableAlias, $sLeftField, $sRightField, $sRightTableAlias); + $sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $aSelectedIdFields, $this->m_sTableAlias, $aJoinData); } $aFrom[$this->m_sTableAlias]['subfrom'] = $aTempFrom; diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php index 2d472f0c5..e5f3c828c 100644 --- a/core/valuesetdef.class.inc.php +++ b/core/valuesetdef.class.inc.php @@ -95,6 +95,7 @@ class ValueSetObjects extends ValueSetDefinition protected $m_sFilterExpr; // in OQL protected $m_sValueAttCode; protected $m_aOrderBy; + protected $m_aExtraConditions; private $m_bAllowAllData; public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false) @@ -104,8 +105,13 @@ class ValueSetObjects extends ValueSetDefinition $this->m_sValueAttCode = $sValueAttCode; $this->m_aOrderBy = $aOrderBy; $this->m_bAllowAllData = $bAllowAllData; + $this->m_aExtraConditions = array(); } + public function AddCondition(DBObjectSearch $oFilter) + { + $this->m_aExtraConditions[] = $oFilter; + } public function ToObjectSet($aArgs = array(), $sContains = '') { @@ -117,6 +123,10 @@ class ValueSetObjects extends ValueSetDefinition { $oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr); } + foreach($this->m_aExtraConditions as $oExtraFilter) + { + $oFilter->MergeWith($oExtraFilter); + } return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs); } @@ -148,6 +158,10 @@ class ValueSetObjects extends ValueSetDefinition $oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr); } if (!$oFilter) return false; + foreach($this->m_aExtraConditions as $oExtraFilter) + { + $oFilter->MergeWith($oExtraFilter); + } $oValueExpr = new ScalarExpression('%'.$sContains.'%'); $oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias()); @@ -289,6 +303,34 @@ class ValueSetEnum extends ValueSetDefinition } } +/** + * Fixed set values, defined as a range: 0..59 (with an optional increment) + * + * @package iTopORM + */ +class ValueSetRange extends ValueSetDefinition +{ + protected $m_iStart; + protected $m_iEnd; + + public function __construct($iStart, $iEnd, $iStep = 1) + { + $this->m_iStart = $iStart; + $this->m_iEnd = $iEnd; + $this->m_iStep = $iStep; + } + + protected function LoadValues($aArgs) + { + $iValue = $this->m_iStart; + for($iValue = $this->m_iStart; $iValue <= $this->m_iEnd; $iValue += $this->m_iStep) + { + $this->m_aValues[$iValue] = $iValue; + } + return true; + } +} + /** * Data model classes