N°1213 - Allow NOT IN SELECT in OQL syntax

This commit is contained in:
acognet
2019-11-26 15:16:14 +01:00
committed by Eric
parent b0d668b124
commit a33977251e
5 changed files with 199 additions and 6 deletions

View File

@@ -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;

View File

@@ -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
}
}
}
}

View File

@@ -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))

View 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"),
);
}
}

View File

@@ -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'),