diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index 20f28f3532..fef0ae8e4e 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -252,6 +252,10 @@ abstract class Expression ); } + public function GetCriteria() + { + return $this; + } /** * Split binary expression on given operator * @@ -395,7 +399,7 @@ class BinaryExpression extends Expression { throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr))); } - if ((($sOperator == "IN") || ($sOperator == "NOT IN")) && !($oRightExpr instanceof ListExpression)) + if ((($sOperator == "IN") || ($sOperator == "NOT IN")) && !($oRightExpr instanceof ListExpression || $oRightExpr instanceof NestedQueryExpression)) { throw new CoreException("Expecting a List Expression object on the right hand for operator $sOperator", array('found_class' => get_class($oRightExpr))); @@ -503,7 +507,12 @@ class BinaryExpression extends Expression $oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved); return new BinaryExpression($oLeft, $this->GetOperator(), $oRight); } - + public function GetCriteria() + { + $oLeft = $this->GetLeftExpr()->GetCriteria(); + $oRight = $this->GetRightExpr()->GetCriteria(); + return new BinaryExpression($oLeft, $this->GetOperator(), $oRight); + } public function ListRequiredFields() { $aLeft = $this->GetLeftExpr()->ListRequiredFields(); @@ -1882,7 +1891,108 @@ class ListExpression extends Expression } } +class NestedQueryExpression extends Expression +{ + protected $m_oNestedQuery; + /*$m_oNestedQuery is an DBSearch object*/ + public function __construct($oNestedQuery) + { + $this->m_oNestedQuery = $oNestedQuery; + } + public static function FromOQLObjectQuery($oObjQuery)//$oNestedQuery as OQLObjectQuery + { + $aExpressions = $oObjQuery->ToDBSearch(""); + return new NestedQueryExpression($aExpressions); + } + + public function IsTrue() + { + // return true if we are certain that it will be true + return false; + } + public function GetNestedQuery() + { + return $this->m_oNestedQuery; + } + + // recursive rendering + /*TODO modif en cours*/ + public function RenderExpression($bForSQL = false, &$aArgs = null, $bRetrofitParams = false) + { + if ($bForSQL) + { + return '('.$this->m_oNestedQuery->GetRootFilter()->MakeSelectQuery().')'; + } + else{ + return '('.$this->m_oNestedQuery->ToOQL(false, null, false).')'; + } + } + /*TODO*/ + public function Browse(Closure $callback) + { + $callback($this); + foreach ($this->m_oCondition as $oExpr) + { + $oExpr->Browse($callback); + } + } + /**/ + public function ApplyParameters($aArgs) + { + $this->m_oNestedQuery->ApplyParameters($aArgs); + } + /**/ + public function GetUnresolvedFields($sAlias, &$aUnresolved) + { + $this->m_oNestedQuery->m_oQBExpressions->GetUnresolvedFields($sAlias, $aUnresolved); + } + /**/ + public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true) + { + // Check and prepare the select information + $this->m_oNestedQuery->m_oQBExpressions-> Translate($aTranslationData, $bMatchAll , $bMarkFieldsAsResolved ); + return clone $this; + + } + /*TODO*/ + public function ListRequiredFields() + { + $aRes = array(); + foreach ($this->m_oNestedQuery->getCondition() as $oExpr) + { + $aRes = array_merge($aRes, $oExpr->ListRequiredFields()); + } + return $aRes; + } + /*TODO */ + public function CollectUsedParents(&$aTable) + { + + } + + public function ListConstantFields() + { + return $this->m_oNestedQuery->ListConstantFields(); + } + + public function RenameParam($sOldName, $sNewName) + { + $this->m_oNestedQuery->RenameParam($sOldName, $sNewName); + } + + public function RenameAlias($sOldName, $sNewName) + { + $this->m_oNestedQuery->RenameAlias($sOldName, $sNewName); + } + /*TODO return QueryBuiderContext*/ + public function GetCriteria() + { + //transform oNestedQuery to object QueryBuilderContext + $queryBuilderContext=new QueryBuilderContext($this->m_oNestedQuery, null, null, null, null); + return new NestedQueryExpression($queryBuilderContext); + } +} class FunctionExpression extends Expression { protected $m_sVerb; diff --git a/core/oql/oql-parser.y b/core/oql/oql-parser.y index 4feb66319d..96dcf5ec7b 100644 --- a/core/oql/oql-parser.y +++ b/core/oql/oql-parser.y @@ -125,10 +125,14 @@ expression_prio3(A) ::= expression_prio3(X) operator3(Y) expression_prio2(Z). { expression_prio4(A) ::= expression_prio3(X). { A = X; } expression_prio4(A) ::= expression_prio4(X) operator4(Y) expression_prio3(Z). { A = new BinaryOqlExpression(X, Y, Z); } - list(A) ::= PAR_OPEN list_items(X) PAR_CLOSE. { A = new ListOqlExpression(X); } +//added for IN (SELECT..) +list(A) ::= PAR_OPEN query(X) PAR_CLOSE. { + A = new NestedQueryOqlExpression(X); + } + list_items(A) ::= expression_prio4(X). { A = array(X); } @@ -318,4 +322,4 @@ class OQLParser extends OQLParserRaw } } -} +} \ No newline at end of file diff --git a/core/oql/oqlquery.class.inc.php b/core/oql/oqlquery.class.inc.php index 3747c01f49..cf2ec549b5 100644 --- a/core/oql/oqlquery.class.inc.php +++ b/core/oql/oqlquery.class.inc.php @@ -188,6 +188,28 @@ class ScalarOqlExpression extends ScalarExpression implements CheckableExpressio } } +class NestedQueryOqlExpression extends NestedQueryExpression implements CheckableExpression{ + + /*Here $m_oNestedQuery is an OQLObjectQuery*/ + public function __construct($oNestedQuery) + { + //OQLObjectQuery + $this->m_oNestedQuery = $oNestedQuery; + $this->m_sQuery=""; + } + /** + * Recursively check the validity of the expression with regard to the data model + * and the query in which it is used + * + * @param ModelReflection $oModelReflection MetaModel to consider + * @throws OqlNormalizeException + */ + public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery) + { + $this->m_oNestedQuery-> Check($oModelReflection, "", $aAliases); + } +} + class FieldOqlExpression extends FieldExpression implements CheckableExpression { protected $m_oParent; @@ -399,7 +421,7 @@ class OqlObjectQuery extends OqlQuery * @param ModelReflection $oModelReflection MetaModel to consider * @throws OqlNormalizeException */ - public function Check(ModelReflection $oModelReflection, $sSourceQuery) + public function Check(ModelReflection $oModelReflection, $sSourceQuery, $aParentAliases = array()) { $sClass = $this->GetClass($oModelReflection); $sClassAlias = $this->GetClassAlias(); @@ -409,7 +431,7 @@ class OqlObjectQuery extends OqlQuery throw new UnknownClassOqlException($sSourceQuery, $this->GetClassDetails(), $oModelReflection->GetClasses()); } - $aAliases = array($sClassAlias => $sClass); + $aAliases = array_merge(array($sClassAlias => $sClass),$aParentAliases); $aJoinSpecs = $this->GetJoins(); if (is_array($aJoinSpecs)) diff --git a/test/core/OQLParserTest.php b/test/core/OQLParserTest.php new file mode 100644 index 0000000000..28fbaeab72 --- /dev/null +++ b/test/core/OQLParserTest.php @@ -0,0 +1,55 @@ +debug($sQuery); + $oDbObjectSearch = DBObjectSearch::FromOQL($sQuery); + $sOql=$oDbObjectSearch->ToOQL(); + $this->debug($sOql); + self::assertEquals($sQuery,$sOql); + } + + public function NestedQueryProvider() + { + return array( + array('SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` WHERE (`Organization`.`id` = `UserRequest`.`org_id`)))'), + array('SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` WHERE 1))'), + array('SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE (`UserRequest`.`org_id` NOT IN (SELECT `Organization` FROM Organization AS `Organization` WHERE 1))'), + array('SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` JOIN Organization AS `Organization1` ON `Organization`.parent_id BELOW `Organization1`.id WHERE (`Organization1`.`id` = \'3\')))'), + array('SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` WHERE (`Organization`.`id` = \'3\')))'), + array("SELECT `L`, `P` FROM Location AS `L` JOIN Person AS `P` ON `P`.location_id = `L`.id WHERE 1"), + array("SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE ((`UserRequest`.`agent_id` = :current_contact_id) AND (`UserRequest`.`status` NOT IN ('closed', 'resolved')))"), + array("SELECT `L` FROM Person AS `P` JOIN Location AS `L` ON `P`.location_id = `L`.id WHERE 1"), + ); + } + +} diff --git a/test/core/OQLTest.php b/test/core/OQLTest.php index 10cda6b854..a2a118d869 100644 --- a/test/core/OQLTest.php +++ b/test/core/OQLTest.php @@ -62,6 +62,8 @@ class OQLTest extends ItopDataTestCase public function NestedQueryProvider() { return array( + array('SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE `UserRequest`.org_id IN (SELECT id FROM Organization AS `Organization` JOIN Organization AS `Organization1` ON `Organization`.parent_id BELOW `Organization1`.id WHERE (`Organization1`.`id` = \'3\'))'), + array('SELECT `UserRequest` FROM UserRequest AS `UserRequest` WHERE (`UserRequest`.`org_id` IN (SELECT `Organization` FROM Organization AS `Organization` WHERE `Organization`.`id`=`UserRequest`.`org_id`))'), array('SELECT toto WHERE id NOT IN (aaa,2,3)'), array('SELECT toto WHERE id IN (SELECT titi)'), array('SELECT toto WHERE a=1'),