mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
N°1213 - Allow NOT IN SELECT in OQL syntax
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
55
test/core/OQLParserTest.php
Normal file
55
test/core/OQLParserTest.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Eric
|
||||
* Date: 31/08/2018
|
||||
* Time: 17:03
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use DBObjectSearch;
|
||||
use Exception;
|
||||
use OqlInterpreter;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class OQLParserTest extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider NestedQueryProvider
|
||||
*
|
||||
* @param $sQuery
|
||||
*
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function testGoodNestedQueryQueryParser($sQuery)
|
||||
{
|
||||
$this->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"),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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'),
|
||||
|
||||
Reference in New Issue
Block a user