mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-21 01:28:47 +02:00
Select multiple objects in OQL (beta, for integration within the UI)
SVN:trunk[327]
This commit is contained in:
@@ -40,11 +40,11 @@ abstract class DBObject
|
||||
private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode
|
||||
|
||||
// Use the MetaModel::NewObject to build an object (do we have to force it?)
|
||||
public function __construct($aRow = null)
|
||||
public function __construct($aRow = null, $sClassAlias = '')
|
||||
{
|
||||
if (!empty($aRow))
|
||||
{
|
||||
$this->FromRow($aRow);
|
||||
$this->FromRow($aRow, $sClassAlias);
|
||||
$this->m_bFullyLoaded = $this->IsFullyLoaded();
|
||||
return;
|
||||
}
|
||||
@@ -170,8 +170,14 @@ abstract class DBObject
|
||||
$this->m_bFullyLoaded = true;
|
||||
}
|
||||
|
||||
protected function FromRow($aRow)
|
||||
protected function FromRow($aRow, $sClassAlias = '')
|
||||
{
|
||||
if (strlen($sClassAlias) == 0)
|
||||
{
|
||||
// Default to the current class
|
||||
$sClassAlias = get_class($this);
|
||||
}
|
||||
|
||||
$this->m_iKey = null;
|
||||
$this->m_bIsInDB = true;
|
||||
$this->m_aCurrValues = array();
|
||||
@@ -180,7 +186,7 @@ abstract class DBObject
|
||||
|
||||
// Get the key
|
||||
//
|
||||
$sKeyField = "id";
|
||||
$sKeyField = $sClassAlias."id";
|
||||
if (!array_key_exists($sKeyField, $aRow))
|
||||
{
|
||||
// #@# Bug ?
|
||||
@@ -211,9 +217,10 @@ abstract class DBObject
|
||||
// then one column will be found with an empty suffix, the others have a suffix
|
||||
// Take care: the function isset will return false in case the value is null,
|
||||
// which is something that could happen on open joins
|
||||
if (array_key_exists($sAttCode, $aRow))
|
||||
$sAttRef = $sClassAlias.$sAttCode;
|
||||
if (array_key_exists($sAttRef, $aRow))
|
||||
{
|
||||
$value = $oAttDef->FromSQLToValue($aRow, $sAttCode);
|
||||
$value = $oAttDef->FromSQLToValue($aRow, $sAttRef);
|
||||
|
||||
$this->m_aCurrValues[$sAttCode] = $value;
|
||||
$this->m_aOrigValues[$sAttCode] = $value;
|
||||
|
||||
@@ -43,9 +43,8 @@ define('SIBUSQLTHISREGEXP', "/this\\.(.*)/U");
|
||||
*/
|
||||
class DBObjectSearch
|
||||
{
|
||||
private $m_sClass;
|
||||
private $m_sClassAlias;
|
||||
private $m_aClasses; // queried classes (alias => class name)
|
||||
private $m_aSelectedClasses; // selected for the output (alias => class name)
|
||||
private $m_oSearchCondition;
|
||||
private $m_aParams;
|
||||
private $m_aFullText;
|
||||
@@ -59,9 +58,10 @@ class DBObjectSearch
|
||||
assert('is_string($sClass)');
|
||||
assert('MetaModel::IsValidClass($sClass)'); // #@# could do better than an assert, or at least give the caller's reference
|
||||
// => idee d'un assert avec call stack (autre utilisation = echec sur query SQL)
|
||||
|
||||
if (empty($sClassAlias)) $sClassAlias = $sClass;
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sClassAlias = $sClassAlias;
|
||||
|
||||
$this->m_aSelectedClasses = array($sClassAlias => $sClass);
|
||||
$this->m_aClasses = array($sClassAlias => $sClass);
|
||||
$this->m_oSearchCondition = new TrueExpression;
|
||||
$this->m_aParams = array();
|
||||
@@ -71,6 +71,38 @@ class DBObjectSearch
|
||||
$this->m_aRelatedTo = array();
|
||||
}
|
||||
|
||||
public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];}
|
||||
public function GetJoinedClasses() {return $this->m_aClasses;}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return reset($this->m_aSelectedClasses);
|
||||
}
|
||||
public function GetClassAlias()
|
||||
{
|
||||
reset($this->m_aSelectedClasses);
|
||||
return key($this->m_aSelectedClasses);
|
||||
}
|
||||
|
||||
public function SetSelectedClasses($aNewSet)
|
||||
{
|
||||
$this->m_aSelectedClasses = array();
|
||||
foreach ($aNewSet as $sAlias => $sClass)
|
||||
{
|
||||
if (!array_key_exists($sAlias, $this->m_aClasses))
|
||||
{
|
||||
throw new CoreException('Unexpected class alias', array('alias'=>$sAlias, 'expected'=>$this->m_aClasses));
|
||||
}
|
||||
$this->m_aSelectedClasses[$sAlias] = $sClass;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->m_aSelectedClasses;
|
||||
}
|
||||
|
||||
|
||||
public function IsAny()
|
||||
{
|
||||
// #@# todo - if (!$this->m_oSearchCondition->IsTrue()) return false;
|
||||
@@ -173,9 +205,9 @@ class DBObjectSearch
|
||||
}
|
||||
if (!empty($sConditionDesc))
|
||||
{
|
||||
return "Objects of class '$this->m_sClass', $sConditionDesc";
|
||||
return "Objects of class '".$this->GetClass()."', $sConditionDesc";
|
||||
}
|
||||
return "Any object of class '$this->m_sClass'";
|
||||
return "Any object of class '".$this->GetClass()."'";
|
||||
}
|
||||
|
||||
protected function TransferConditionExpression($oFilter, $aTranslation)
|
||||
@@ -190,7 +222,6 @@ class DBObjectSearch
|
||||
{
|
||||
$this->m_oSearchCondition = new TrueExpression();
|
||||
// ? is that usefull/enough, do I need to rebuild the list after the subqueries ?
|
||||
// $this->m_aClasses = array($this->m_sClassAlias => $this->m_sClass);
|
||||
}
|
||||
|
||||
public function AddConditionExpression($oExpression)
|
||||
@@ -205,8 +236,8 @@ class DBObjectSearch
|
||||
// #@# todo - obsolete smoothly, first send exceptions
|
||||
// throw new CoreException('SibusQL has been obsoleted, please update your queries', array('sibusql'=>$sQuery, 'oql'=>$oFilter->ToOQL()));
|
||||
|
||||
MyHelpers::CheckKeyInArray('filter code', $sFilterCode, MetaModel::GetClassFilterDefs($this->m_sClass));
|
||||
$oFilterDef = MetaModel::GetClassFilterDef($this->m_sClass, $sFilterCode);
|
||||
MyHelpers::CheckKeyInArray('filter code', $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));
|
||||
$oFilterDef = MetaModel::GetClassFilterDef($this->GetClass(), $sFilterCode);
|
||||
|
||||
if (empty($sOpCode))
|
||||
{
|
||||
@@ -216,7 +247,7 @@ class DBObjectSearch
|
||||
|
||||
// Preserve backward compatibility - quick n'dirty way to change that API semantic
|
||||
//
|
||||
$oField = new FieldExpression($sFilterCode, $this->m_sClassAlias);
|
||||
$oField = new FieldExpression($sFilterCode, $this->GetClassAlias());
|
||||
switch($sOpCode)
|
||||
{
|
||||
case 'SameDay':
|
||||
@@ -286,14 +317,20 @@ class DBObjectSearch
|
||||
|
||||
protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
$sOrigAlias = $this->m_sClassAlias;
|
||||
$sOrigAlias = $this->GetClassAlias();
|
||||
if (array_key_exists($sOrigAlias, $aClassAliases))
|
||||
{
|
||||
$this->m_sClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $this->m_sClass);
|
||||
$sNewAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $this->GetClass());
|
||||
$this->m_aSelectedClasses[$sNewAlias] = $this->GetClass();
|
||||
unset($sOrigAlias, $this->m_aSelectedClasses[$sNewAlias]);
|
||||
|
||||
// Translate the condition expression with the new alias
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $this->m_sClassAlias;
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $sNewAlias;
|
||||
}
|
||||
|
||||
// add the alias into the filter aliases list
|
||||
$aClassAliases[$this->GetClassAlias()] = $this->GetClass();
|
||||
|
||||
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter)
|
||||
{
|
||||
$oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation);
|
||||
@@ -429,12 +466,6 @@ class DBObjectSearch
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];}
|
||||
public function GetClasses() {return $this->m_aClasses;}
|
||||
|
||||
public function GetClass() {return $this->m_sClass;}
|
||||
public function GetClassAlias() {return $this->m_sClassAlias;}
|
||||
public function GetCriteria() {return $this->m_oSearchCondition;}
|
||||
public function GetCriteria_FullText() {return $this->m_aFullText;}
|
||||
public function GetCriteria_PointingTo($sKeyAttCode = "")
|
||||
@@ -636,7 +667,10 @@ class DBObjectSearch
|
||||
$bRetrofitParams = true;
|
||||
}
|
||||
|
||||
$sRes = "SELECT ".$this->GetClass().' AS '.$this->GetClassAlias();
|
||||
$sSelectedClasses = implode(', ', array_keys($this->m_aSelectedClasses));
|
||||
$sRes = 'SELECT '.$sSelectedClasses.' FROM';
|
||||
|
||||
$sRes .= ' '.$this->GetClass().' AS '.$this->GetClassAlias();
|
||||
$sRes .= $this->ToOQL_Joins();
|
||||
$sRes .= " WHERE ".$this->m_oSearchCondition->Render($aParams, $bRetrofitParams);
|
||||
|
||||
@@ -912,6 +946,19 @@ class DBObjectSearch
|
||||
}
|
||||
}
|
||||
|
||||
// Check and prepare the select information
|
||||
$aSelected = array();
|
||||
foreach ($oOqlQuery->GetSelectedClasses() as $oClassDetails)
|
||||
{
|
||||
$sClassToSelect = $oClassDetails->GetValue();
|
||||
if (!array_key_exists($sClassToSelect, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oClassDetails, array_keys($aAliases));
|
||||
}
|
||||
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
|
||||
}
|
||||
$oResultFilter->SetSelectedClasses($aSelected);
|
||||
|
||||
$oConditionTree = $oOqlQuery->GetCondition();
|
||||
if ($oConditionTree instanceof Expression)
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ class DBObjectSet
|
||||
$this->m_aArgs = $aArgs;
|
||||
|
||||
$this->m_bLoaded = false;
|
||||
$this->m_aData = array();
|
||||
$this->m_aData = array(); // array of (row => array of (classalias) => object)
|
||||
$this->m_aId2Row = array();
|
||||
$this->m_iCurrRow = 0;
|
||||
}
|
||||
@@ -143,6 +143,11 @@ class DBObjectSet
|
||||
return $this->m_oFilter->GetClass();
|
||||
}
|
||||
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->m_oFilter->GetSelectedClasses();
|
||||
}
|
||||
|
||||
public function GetRootClass()
|
||||
{
|
||||
return MetaModel::GetRootClass($this->GetClass());
|
||||
@@ -156,11 +161,16 @@ class DBObjectSet
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return;
|
||||
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||
{
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$oObject = MetaModel::GetObjectByRow($sClass, $aRow);
|
||||
$this->AddObject($oObject);
|
||||
$aObjects = array();
|
||||
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
$oObject = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias);
|
||||
$aObjects[$sClassAlias] = $oObject;
|
||||
}
|
||||
$this->AddObjectExtended($aObjects);
|
||||
}
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
|
||||
@@ -173,7 +183,7 @@ class DBObjectSet
|
||||
return count($this->m_aData);
|
||||
}
|
||||
|
||||
public function Fetch()
|
||||
public function Fetch($sClassAlias = '')
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
@@ -181,11 +191,32 @@ class DBObjectSet
|
||||
{
|
||||
return null;
|
||||
}
|
||||
$oRetObj = $this->m_aData[$this->m_iCurrRow];
|
||||
|
||||
if (strlen($sClassAlias) == 0)
|
||||
{
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
}
|
||||
$oRetObj = $this->m_aData[$this->m_iCurrRow][$sClassAlias];
|
||||
$this->m_iCurrRow++;
|
||||
return $oRetObj;
|
||||
}
|
||||
|
||||
// Return the whole line if several classes have been specified in the query
|
||||
//
|
||||
public function FetchAssoc()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
if ($this->m_iCurrRow >= count($this->m_aData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$aRetObjects = $this->m_aData[$this->m_iCurrRow];
|
||||
$this->m_iCurrRow++;
|
||||
return $aRetObjects;
|
||||
}
|
||||
|
||||
public function Rewind()
|
||||
{
|
||||
$this->Seek(0);
|
||||
@@ -199,25 +230,36 @@ class DBObjectSet
|
||||
return $this->m_iCurrRow;
|
||||
}
|
||||
|
||||
public function AddObject($oObject)
|
||||
public function AddObject($oObject, $sClassAlias = '')
|
||||
{
|
||||
// ?usefull? if ($oObject->GetClass() != $this->GetClass()) return;
|
||||
$sObjClass = get_class($oObject);
|
||||
if (strlen($sClassAlias) == 0)
|
||||
{
|
||||
$sClassAlias = $sObjClass;
|
||||
}
|
||||
|
||||
// it is mandatory to avoid duplicates
|
||||
if (array_key_exists($oObject->GetKey(), $this->m_aId2Row)) return;
|
||||
|
||||
// Do not load here, because the load uses that method too
|
||||
$iNextPos = count($this->m_aData);
|
||||
$this->m_aData[$iNextPos] = $oObject;
|
||||
$this->m_aId2Row[$oObject->GetKey()] = $iNextPos;
|
||||
$this->m_aData[$iNextPos][$sClassAlias] = $oObject;
|
||||
$this->m_aId2Row[$sObjClass][$oObject->GetKey()] = $iNextPos;
|
||||
}
|
||||
|
||||
public function AddObjectArray($aObjects)
|
||||
protected function AddObjectExtended($aObjectArray)
|
||||
{
|
||||
$iNextPos = count($this->m_aData);
|
||||
|
||||
foreach ($aObjectArray as $sClassAlias => $oObject)
|
||||
{
|
||||
$this->m_aData[$iNextPos][$sClassAlias] = $oObject;
|
||||
$this->m_aId2Row[get_class($oObject)][$oObject->GetKey()] = $iNextPos;
|
||||
}
|
||||
}
|
||||
|
||||
public function AddObjectArray($aObjects, $sClassAlias = '')
|
||||
{
|
||||
// #@# todo - add a check on the object class ?
|
||||
foreach ($aObjects as $oObj)
|
||||
{
|
||||
$this->AddObject($oObj);
|
||||
$this->AddObject($oObj, $sClassAlias);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -30,20 +30,27 @@ result ::= query(X). { $this->my_result = X; }
|
||||
result ::= condition(X). { $this->my_result = X; }
|
||||
|
||||
query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, X, W, J);
|
||||
A = new OqlObjectQuery(X, X, W, J, array(X));
|
||||
}
|
||||
query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, Y, W, J);
|
||||
A = new OqlObjectQuery(X, Y, W, J, array(Y));
|
||||
}
|
||||
|
||||
/*
|
||||
query(A) ::= SELECT field_id(E) FROM class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlValueSetQuery(E, X, X, W, J);
|
||||
query(A) ::= SELECT class_list(E) FROM class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, X, W, J, E);
|
||||
}
|
||||
query(A) ::= SELECT field_id(E) FROM class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlValueSetQuery(E, X, Y, W, J);
|
||||
query(A) ::= SELECT class_list(E) FROM class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, Y, W, J, E);
|
||||
}
|
||||
|
||||
|
||||
class_list(A) ::= class_name(X). {
|
||||
A = array(X);
|
||||
}
|
||||
class_list(A) ::= class_list(L) COMA class_name(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
*/
|
||||
|
||||
where_statement(A) ::= WHERE condition(C). { A = C;}
|
||||
where_statement(A) ::= . { A = null;}
|
||||
|
||||
@@ -22,7 +22,8 @@ class OqlInterpreter
|
||||
$this->m_sQuery = $sQuery;
|
||||
}
|
||||
|
||||
protected function Parse()
|
||||
// Note: this function is left public for unit test purposes
|
||||
public function Parse()
|
||||
{
|
||||
$oLexer = new OQLLexer($this->m_sQuery);
|
||||
$oParser = new OQLParser($this->m_sQuery);
|
||||
@@ -45,18 +46,6 @@ class OqlInterpreter
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
/*
|
||||
public function ParseValueSetQuery()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof OqlValueSetQuery)
|
||||
{
|
||||
throw new OqlException('Expecting a value set query', $this->m_sQuery, 0, 0, get_class($oRes), array('OqlValueSetQuery'));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
*/
|
||||
|
||||
public function ParseExpression()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
|
||||
@@ -150,16 +150,22 @@ abstract class OqlQuery
|
||||
|
||||
class OqlObjectQuery extends OqlQuery
|
||||
{
|
||||
protected $m_aSelect; // array of selected classes
|
||||
protected $m_oClass;
|
||||
protected $m_oClassAlias;
|
||||
|
||||
public function __construct($oClass, $oClassAlias = '', $oCondition = null, $aJoins = null)
|
||||
public function __construct($oClass, $oClassAlias, $oCondition = null, $aJoins = null, $aSelect = null)
|
||||
{
|
||||
$this->m_aSelect = $aSelect;
|
||||
$this->m_oClass = $oClass;
|
||||
$this->m_oClassAlias = $oClassAlias;
|
||||
parent::__construct($oCondition, $aJoins);
|
||||
}
|
||||
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->m_aSelect;
|
||||
}
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_oClass->GetValue();
|
||||
@@ -179,20 +185,4 @@ class OqlObjectQuery extends OqlQuery
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class OqlValueSetQuery extends OqlObjectQuery
|
||||
{
|
||||
protected $m_oSelectExpr;
|
||||
|
||||
public function __construct($oSelectExpr, $oClass, $oClassAlias = '', $oCondition = null, $aJoins = null)
|
||||
{
|
||||
$this->m_oSelectExpr = $oSelectExpr;
|
||||
parent::__construct($oClass, $oClassAlias, $oCondition, $aJoins);
|
||||
}
|
||||
|
||||
public function GetSelectExpression()
|
||||
{
|
||||
return $this->m_oSelectExpr;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -6,7 +6,7 @@ class TestSQLQuery extends TestScenarioOnDB
|
||||
static public function GetDescription() {return 'SQLQuery does not depend on the rest of the framework, therefore it makes sense to have a separate test framework for it';}
|
||||
|
||||
static public function GetDBHost() {return 'localhost';}
|
||||
static public function GetDBUser() {return 'RomainDBLogin';}
|
||||
static public function GetDBUser() {return 'root';}
|
||||
static public function GetDBPwd() {return '';}
|
||||
static public function GetDBName() {return 'TestSQLQuery';}
|
||||
static public function GetDBSubName() {return 'taratata';}
|
||||
@@ -160,6 +160,18 @@ class TestOQLParser extends TestFunction
|
||||
"SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 + B.col2) * B.col1 = A.col2" => true,
|
||||
|
||||
'SELECT Device AS D_ JOIN Site AS S_ ON D_.site = S_.id WHERE S_.country = "Francia"' => true,
|
||||
|
||||
// Several objects in a row...
|
||||
//
|
||||
'SELECT A FROM A' => true,
|
||||
'SELECT A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
|
||||
'SELECT A FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
|
||||
'SELECT B FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
|
||||
'SELECT A,B FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
|
||||
'SELECT A, B FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
|
||||
'SELECT B,A FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
|
||||
'SELECT A, B,C FROM A JOIN B ON A.myB = B.id' => false,
|
||||
'SELECT C FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => false,
|
||||
);
|
||||
|
||||
$iErrors = 0;
|
||||
@@ -933,6 +945,11 @@ class TestQueriesOnFarm extends MyFarm
|
||||
'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true,
|
||||
'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id' => true,
|
||||
'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id JOIN Mammal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.name=\'chloe\' OR Child.name=\'bizounours\'' => true,
|
||||
// Specifying multiple objects
|
||||
'SELECT Animal FROM Animal' => true,
|
||||
'SELECT yelele FROM Animal' => false,
|
||||
'SELECT Animal FROM Animal AS A' => false,
|
||||
'SELECT A FROM Animal AS A' => true,
|
||||
);
|
||||
//$aQueries = array(
|
||||
// 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true,
|
||||
|
||||
Reference in New Issue
Block a user