From 37a4a3eb47f2899f0ce3e14dfe6554c70e9529a9 Mon Sep 17 00:00:00 2001 From: jbostoen <6421683+jbostoen@users.noreply.github.com> Date: Mon, 1 Feb 2021 16:21:10 +0100 Subject: [PATCH 01/10] :globe_with_meridians: Fix typo in Dutch translations (#189) Co-authored-by: jbostoen <-> --- dictionaries/nl.dictionary.itop.ui.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dictionaries/nl.dictionary.itop.ui.php b/dictionaries/nl.dictionary.itop.ui.php index 584757e4e..25b17bc37 100644 --- a/dictionaries/nl.dictionary.itop.ui.php +++ b/dictionaries/nl.dictionary.itop.ui.php @@ -813,7 +813,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'UI:Delete:WillBeDeletedAutomatically' => 'Zal automatisch verwijderd worden', 'UI:Delete:MustBeDeletedManually' => 'Moet handmatig verwijderd worden', 'UI:Delete:CannotUpdateBecause_Issue' => 'Zou automatisch moeten geüpdatet worden, maar: %1$s', - 'UI:Delete:WillAutomaticallyUpdate_Fields' => 'Zal automatisch aangeapst worden (reset: %1$s)', + 'UI:Delete:WillAutomaticallyUpdate_Fields' => 'Zal automatisch aangepast worden (reset: %1$s)', 'UI:Delete:Count_Objects/LinksReferencing_Object' => '%1$d objecten/links verwijzen naar %2$s', 'UI:Delete:Count_Objects/LinksReferencingTheObjects' => '%1$d objecten/links verwijzen naar sommige objecten die verwijderd worden', 'UI:Delete:ReferencesMustBeDeletedToEnsureIntegrity' => 'Elke verdere verwijzing moet verwijderd worden om de integriteit van de database te verzekeren', From bc024d9ed0c4e291428bd3843644cfb605167e09 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 2 Feb 2021 17:30:03 +0100 Subject: [PATCH 02/10] =?UTF-8?q?N=C2=B03660=20-=20Fix=20JOIN=20without=20?= =?UTF-8?q?condition=20on=20child=20joined=20table=20is=20ignored?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/legacy/dbobjectsearchlegacy.class.php | 13 ++++++ core/oqlclasstreebuilder.class.inc.php | 9 ++++ test/core/OQLTest.php | 53 ++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/core/legacy/dbobjectsearchlegacy.class.php b/core/legacy/dbobjectsearchlegacy.class.php index 24e67db2c..f4d3e5e93 100644 --- a/core/legacy/dbobjectsearchlegacy.class.php +++ b/core/legacy/dbobjectsearchlegacy.class.php @@ -2602,5 +2602,18 @@ class DBObjectSearch extends DBSearch return $oExpression; } + /** + * @param array $aAttCodes array of attCodes to search into + * @param string $sNeedle one word to be searched + * + * @throws \CoreException + */ + public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle) + { + } + public function ListParameters() + { + return $this->GetCriteria()->ListParameters(); + } } diff --git a/core/oqlclasstreebuilder.class.inc.php b/core/oqlclasstreebuilder.class.inc.php index a397ab3db..236f9d684 100644 --- a/core/oqlclasstreebuilder.class.inc.php +++ b/core/oqlclasstreebuilder.class.inc.php @@ -294,6 +294,15 @@ class OQLClassTreeBuilder $oClassExpr = new FieldExpression($sClassAttCode, $this->sClassAlias); $oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); $oQBContextExpressions->AddCondition($oClassRestriction); + } elseif (($oKeyAttDef instanceof AttributeExternalKey) && $sKeyClass != $oKeyAttDef->GetTargetClass()) { + $sClassAttCode = 'finalclass'; + if (MetaModel::IsValidAttCode($sKeyClass, $sClassAttCode)) { + $oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sKeyClass, + ENUM_CHILD_CLASSES_ALL)); + $oClassExpr = new FieldExpression($sClassAttCode, $sKeyClassAlias); + $oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); + $oQBContextExpressions->AddCondition($oClassRestriction); + } } // Translate prior to recursing diff --git a/test/core/OQLTest.php b/test/core/OQLTest.php index d7b3939a3..d419f46b3 100644 --- a/test/core/OQLTest.php +++ b/test/core/OQLTest.php @@ -15,7 +15,10 @@ use Combodo\iTop\Test\UnitTest\ItopDataTestCase; use DBObjectSearch; use DBSearch; use Exception; +use MetaModel; use OqlInterpreter; +use QueryBuilderContext; +use SQLObjectQueryBuilder; use utils; /** @@ -401,4 +404,54 @@ class OQLTest extends ItopDataTestCase ); } + + /** + * @dataProvider GetOQLClassTreeProvider + * @param $sOQL + * @param $sExpectedOQL + */ + public function testGetOQLClassTree($sOQL, $sExpectedOQL) + { + $oFilter = DBSearch::FromOQL($sOQL); + $aCountAttToLoad = array(); + $sMainClass = null; + foreach ($oFilter->GetSelectedClasses() as $sClassAlias => $sClass) + { + $aCountAttToLoad[$sClassAlias] = array(); + if (empty($sMainClass)) + { + $sMainClass = $sClass; + } + } + $aModifierProperties = MetaModel::MakeModifierProperties($oFilter); + $oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oFilter); + $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties, null, null, null, $aCountAttToLoad); + $sResultOQL = $oSQLObjectQueryBuilder->DebugOQLClassTree($oBuild); + + static::assertEquals($sExpectedOQL, $sResultOQL); + } + + public function GetOQLClassTreeProvider() + { + return [ + 'Bug 3660 1' => [ + "SELECT UserRequest AS U + JOIN lnkContactToTicket AS l ON l.ticket_id=U.id + JOIN Team AS T ON l.contact_id=T.id", + "SELECT `U` FROM `UserRequest` AS `U` + INNER JOIN `lnkContactToTicket` AS `l` + ON `U`.`id` = `l`.`ticket_id` + INNER JOIN `Contact` AS `T_Contact` + ON `l`.`contact_id` = `T_Contact`.`id`", + ], + 'Bug 3660 2' => [ + "SELECT UserRequest AS U + JOIN lnkContactToTicket AS l ON l.ticket_id=U.id + JOIN Contact AS C ON l.contact_id=C.id", + "SELECT `U` FROM `UserRequest` AS `U` + INNER JOIN `lnkContactToTicket` AS `l` + ON `U`.`id` = `l`.`ticket_id`", + ], + ]; + } } From 765560d1f58b86ca8fa02ff09c9739c0ed2dbf6b Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Tue, 2 Feb 2021 17:57:40 +0100 Subject: [PATCH 03/10] ItopTestCase : helpers to call invisble methods --- test/ItopTestCase.php | 24 +++++++++++++++++++ test/core/DBSearchUpdateRealiasingMapTest.php | 12 +++------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/test/ItopTestCase.php b/test/ItopTestCase.php index 82adec874..6dc33b279 100644 --- a/test/ItopTestCase.php +++ b/test/ItopTestCase.php @@ -93,7 +93,31 @@ class ItopTestCase extends TestCase { $sId = str_replace('"', '', $this->getName()); $sId = str_replace(' ', '_', $sId); + return $sId; } + public function InvokeInvisibleStaticMethod($sObjectClass, $sMethodName, $aArgs) + { + return $this->InvokeInvisibleMethod($sObjectClass, $sMethodName, null, $aArgs); + } + + /** + * @param string $sObjectClass for example DBObject::class + * @param string $sMethodName + * @param object $oObject + * @param array $aArgs + * + * @return mixed method result + * + * @throws \ReflectionException + */ + public function InvokeInvisibleMethod($sObjectClass, $sMethodName, $oObject, $aArgs) + { + $class = new \ReflectionClass($sObjectClass); + $method = $class->getMethod($sMethodName); + $method->setAccessible(true); + + return $method->invokeArgs($oObject, $aArgs); + } } \ No newline at end of file diff --git a/test/core/DBSearchUpdateRealiasingMapTest.php b/test/core/DBSearchUpdateRealiasingMapTest.php index ece5dd312..8d363869c 100644 --- a/test/core/DBSearchUpdateRealiasingMapTest.php +++ b/test/core/DBSearchUpdateRealiasingMapTest.php @@ -33,7 +33,9 @@ class DBSearchUpdateRealiasingMapTest extends ItopDataTestCase */ public function testUpdateRealiasingMap($aRealiasingMap, $aAliasTranslation, $aExpectedRealiasingMap) { - $this->UpdateRealiasingMap($aRealiasingMap, $aAliasTranslation); + $oObject = new DBObjectSearch('Organization'); + $aArgs = [&$aRealiasingMap, $aAliasTranslation]; + $this->InvokeInvisibleMethod(DBObjectSearch::class, 'UpdateRealiasingMap', $oObject, $aArgs); $this->assertEquals($aExpectedRealiasingMap, $aRealiasingMap); } @@ -77,12 +79,4 @@ class DBSearchUpdateRealiasingMapTest extends ItopDataTestCase ], ]; } - - private function UpdateRealiasingMap(&$aRealiasingMap, $aAliasTranslation) - { - $class = new \ReflectionClass(DBObjectSearch::class); - $method = $class->getMethod('UpdateRealiasingMap'); - $method->setAccessible(true); - $method->invokeArgs(new DBObjectSearch('Organization'), [&$aRealiasingMap, $aAliasTranslation]); - } } From 7115a6ae7d266fc55b84193b96c4906fcf48dfa5 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 3 Feb 2021 09:07:59 +0100 Subject: [PATCH 04/10] =?UTF-8?q?N=C2=B03660=20-=20Fix=20JOIN=20without=20?= =?UTF-8?q?condition=20on=20child=20joined=20table=20is=20ignored=20(remov?= =?UTF-8?q?e=20unnecessary=20check)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/oqlclasstreebuilder.class.inc.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/oqlclasstreebuilder.class.inc.php b/core/oqlclasstreebuilder.class.inc.php index 236f9d684..2e25fb1dd 100644 --- a/core/oqlclasstreebuilder.class.inc.php +++ b/core/oqlclasstreebuilder.class.inc.php @@ -296,13 +296,11 @@ class OQLClassTreeBuilder $oQBContextExpressions->AddCondition($oClassRestriction); } elseif (($oKeyAttDef instanceof AttributeExternalKey) && $sKeyClass != $oKeyAttDef->GetTargetClass()) { $sClassAttCode = 'finalclass'; - if (MetaModel::IsValidAttCode($sKeyClass, $sClassAttCode)) { - $oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sKeyClass, - ENUM_CHILD_CLASSES_ALL)); - $oClassExpr = new FieldExpression($sClassAttCode, $sKeyClassAlias); - $oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); - $oQBContextExpressions->AddCondition($oClassRestriction); - } + $oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sKeyClass, + ENUM_CHILD_CLASSES_ALL)); + $oClassExpr = new FieldExpression($sClassAttCode, $sKeyClassAlias); + $oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); + $oQBContextExpressions->AddCondition($oClassRestriction); } // Translate prior to recursing From 2f0e7c6d2956c024a97a2deafc980b21c87e78f8 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 3 Feb 2021 09:15:56 +0100 Subject: [PATCH 05/10] =?UTF-8?q?N=C2=B03586=20-=20Fix=20login=20window=20?= =?UTF-8?q?not=20correctly=20displayed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/login/login.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/login/login.html.twig b/templates/login/login.html.twig index e58eb3b36..5cdc95fb6 100644 --- a/templates/login/login.html.twig +++ b/templates/login/login.html.twig @@ -12,7 +12,7 @@ {% if bFailedLogin %}

{{ sMessage }}

{% else %} -

{{ sMessage }}

+

{{ sMessage|raw }}

{% endif %} {% endblock login_title %} From e06996a2e4d8497f75e8e59c8cf1113bc6d9763e Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 3 Feb 2021 15:58:54 +0100 Subject: [PATCH 06/10] =?UTF-8?q?N=C2=B03660=20-=20Fix=20JOIN=20without=20?= =?UTF-8?q?condition=20on=20child=20joined=20table=20is=20ignored=20(check?= =?UTF-8?q?=20done=20in=20optimizer)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/oqlclassnode.class.inc.php | 8 ++++++++ core/oqlclasstreebuilder.class.inc.php | 7 ------- core/oqlclasstreeoptimizer.class.inc.php | 11 +++++++++-- test/core/OQLTest.php | 4 ++-- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/core/oqlclassnode.class.inc.php b/core/oqlclassnode.class.inc.php index da157f588..3c078ab14 100644 --- a/core/oqlclassnode.class.inc.php +++ b/core/oqlclassnode.class.inc.php @@ -331,4 +331,12 @@ class OQLJoin return $this->sRightField; } + /** + * @return string + */ + public function GetLeftField() + { + return $this->sLeftField; + } + } diff --git a/core/oqlclasstreebuilder.class.inc.php b/core/oqlclasstreebuilder.class.inc.php index 2e25fb1dd..a397ab3db 100644 --- a/core/oqlclasstreebuilder.class.inc.php +++ b/core/oqlclasstreebuilder.class.inc.php @@ -294,13 +294,6 @@ class OQLClassTreeBuilder $oClassExpr = new FieldExpression($sClassAttCode, $this->sClassAlias); $oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); $oQBContextExpressions->AddCondition($oClassRestriction); - } elseif (($oKeyAttDef instanceof AttributeExternalKey) && $sKeyClass != $oKeyAttDef->GetTargetClass()) { - $sClassAttCode = 'finalclass'; - $oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sKeyClass, - ENUM_CHILD_CLASSES_ALL)); - $oClassExpr = new FieldExpression($sClassAttCode, $sKeyClassAlias); - $oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); - $oQBContextExpressions->AddCondition($oClassRestriction); } // Translate prior to recursing diff --git a/core/oqlclasstreeoptimizer.class.inc.php b/core/oqlclasstreeoptimizer.class.inc.php index 0472863e7..1e12384e9 100644 --- a/core/oqlclasstreeoptimizer.class.inc.php +++ b/core/oqlclasstreeoptimizer.class.inc.php @@ -50,8 +50,15 @@ class OQLClassTreeOptimizer { if ($oJoin->IsOutbound()) { - // The join is not used, remove from tree - $oCurrentClassNode->RemoveJoin($sLeftKey, $index); + // If joined class in not the same class than the external key target class + // then the join cannot be removed because it is used to filter the request + $sJoinedClass = $oJoin->GetOOQLClassNode()->GetNodeClass(); + $sExtKeyAttCode = $oJoin->GetLeftField(); + $oExtKeyAttDef = MetaModel::GetAttributeDef($oCurrentClassNode->GetNodeClass(), $sExtKeyAttCode); + if ($sJoinedClass == $oExtKeyAttDef->GetTargetClass()) { + // The join is not used, remove from tree + $oCurrentClassNode->RemoveJoin($sLeftKey, $index); + } } else { diff --git a/test/core/OQLTest.php b/test/core/OQLTest.php index d419f46b3..54b0293d7 100644 --- a/test/core/OQLTest.php +++ b/test/core/OQLTest.php @@ -441,8 +441,8 @@ class OQLTest extends ItopDataTestCase "SELECT `U` FROM `UserRequest` AS `U` INNER JOIN `lnkContactToTicket` AS `l` ON `U`.`id` = `l`.`ticket_id` - INNER JOIN `Contact` AS `T_Contact` - ON `l`.`contact_id` = `T_Contact`.`id`", + INNER JOIN `Team` AS `T` + ON `l`.`contact_id` = `T`.`id`", ], 'Bug 3660 2' => [ "SELECT UserRequest AS U From a5894c1a4c332c8468328fe67f245a50e61779e7 Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Thu, 4 Feb 2021 09:41:56 +0100 Subject: [PATCH 07/10] Rename \Combodo\iTop\Test\UnitTest\ItopTestCase::InvokeInvisible* to InvokeNonPublic* --- test/ItopTestCase.php | 6 +++--- test/core/DBSearchUpdateRealiasingMapTest.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/ItopTestCase.php b/test/ItopTestCase.php index 6dc33b279..16b9a2f1f 100644 --- a/test/ItopTestCase.php +++ b/test/ItopTestCase.php @@ -97,9 +97,9 @@ class ItopTestCase extends TestCase return $sId; } - public function InvokeInvisibleStaticMethod($sObjectClass, $sMethodName, $aArgs) + public function InvokeNonPublicStaticMethod($sObjectClass, $sMethodName, $aArgs) { - return $this->InvokeInvisibleMethod($sObjectClass, $sMethodName, null, $aArgs); + return $this->InvokeNonPublicMethod($sObjectClass, $sMethodName, null, $aArgs); } /** @@ -112,7 +112,7 @@ class ItopTestCase extends TestCase * * @throws \ReflectionException */ - public function InvokeInvisibleMethod($sObjectClass, $sMethodName, $oObject, $aArgs) + public function InvokeNonPublicMethod($sObjectClass, $sMethodName, $oObject, $aArgs) { $class = new \ReflectionClass($sObjectClass); $method = $class->getMethod($sMethodName); diff --git a/test/core/DBSearchUpdateRealiasingMapTest.php b/test/core/DBSearchUpdateRealiasingMapTest.php index 8d363869c..dd090bdc9 100644 --- a/test/core/DBSearchUpdateRealiasingMapTest.php +++ b/test/core/DBSearchUpdateRealiasingMapTest.php @@ -35,7 +35,7 @@ class DBSearchUpdateRealiasingMapTest extends ItopDataTestCase { $oObject = new DBObjectSearch('Organization'); $aArgs = [&$aRealiasingMap, $aAliasTranslation]; - $this->InvokeInvisibleMethod(DBObjectSearch::class, 'UpdateRealiasingMap', $oObject, $aArgs); + $this->InvokeNonPublicMethod(DBObjectSearch::class, 'UpdateRealiasingMap', $oObject, $aArgs); $this->assertEquals($aExpectedRealiasingMap, $aRealiasingMap); } From f07f0ba1c710298f2d6c7a8dcba5509727e13097 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 5 Feb 2021 10:15:14 +0100 Subject: [PATCH 08/10] =?UTF-8?q?N=C2=B03618=20-=20Count=20on=20union=20wi?= =?UTF-8?q?th=20different=20conditions=20fails=20(Fix=20multi-column=20att?= =?UTF-8?q?ributes=20sql=20generation)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/oql/expression.class.inc.php | 29 ++++++++++ core/querybuilderexpressions.class.inc.php | 8 +++ core/sqlobjectquerybuilder.class.inc.php | 24 +++------ test/core/OQLTest.php | 62 +++++++++++++++++++--- 4 files changed, 101 insertions(+), 22 deletions(-) diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index 141bf4198..2291b339a 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -1672,6 +1672,35 @@ class FieldExpression extends UnaryExpression // Has been resolved into an SQL expression class FieldExpressionResolved extends FieldExpression { + protected $m_aAdditionalExpressions; + + public function __construct($mExpression, $sParent = '') + { + $this->m_aAdditionalExpressions = array(); + if (is_array($mExpression)) + { + foreach ($mExpression as $sSuffix => $sExpression) + { + if ($sSuffix == '') + { + $sName = $sExpression; + } + $this->m_aAdditionalExpressions[$sSuffix] = new FieldExpressionResolved($sExpression, $sParent); + } + } + else + { + $sName = $mExpression; + } + + parent::__construct($sName, $sParent); + } + + public function AdditionalExpressions() + { + return $this->m_aAdditionalExpressions; + } + public function GetUnresolvedFields($sAlias, &$aUnresolved) { } diff --git a/core/querybuilderexpressions.class.inc.php b/core/querybuilderexpressions.class.inc.php index 5f3aca1cf..c713610a8 100644 --- a/core/querybuilderexpressions.class.inc.php +++ b/core/querybuilderexpressions.class.inc.php @@ -178,6 +178,14 @@ class QueryBuilderExpressions foreach ($this->m_aSelectExpr as $sColAlias => $oExpr) { $this->m_aSelectExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved); + if ($this->m_aSelectExpr[$sColAlias] instanceof FieldExpressionResolved) + { + // Split the field with the relevant alias + foreach ($this->m_aSelectExpr[$sColAlias]->AdditionalExpressions() as $sSuffix => $oAdditionalExpr) + { + $this->m_aSelectExpr[$sColAlias.$sSuffix] = $oAdditionalExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved); + } + } } if ($this->m_aGroupByExpr) { diff --git a/core/sqlobjectquerybuilder.class.inc.php b/core/sqlobjectquerybuilder.class.inc.php index b1ecb1d43..4d85480ba 100644 --- a/core/sqlobjectquerybuilder.class.inc.php +++ b/core/sqlobjectquerybuilder.class.inc.php @@ -239,24 +239,16 @@ class SQLObjectQueryBuilder continue; } $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); - foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr) + $oFieldSQLExp = new FieldExpressionResolved($oAttDef->GetSQLExpressions(), $sClassAlias); + /** + * @var string $sPluginClass + * @var iQueryModifier $oQueryModifier + */ + foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier) { - if (!empty($sColId)) - { - // Multi column attributes - $oBuild->m_oQBExpressions->AddSelect($sSelectedClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias)); - } - $oFieldSQLExp = new FieldExpressionResolved($sSQLExpr, $sClassAlias); - /** - * @var string $sPluginClass - * @var iQueryModifier $oQueryModifier - */ - foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier) - { - $oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sClass, $sAttCode, $sColId, $oFieldSQLExp, $oBaseSQLQuery); - } - $aTranslation[$sClassAlias][$sAttCode.$sColId] = $oFieldSQLExp; + $oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sClass, $sAttCode, '', $oFieldSQLExp, $oBaseSQLQuery); } + $aTranslation[$sClassAlias][$sAttCode] = $oFieldSQLExp; } // Translate the selected columns diff --git a/test/core/OQLTest.php b/test/core/OQLTest.php index 54b0293d7..3a3f0cf9d 100644 --- a/test/core/OQLTest.php +++ b/test/core/OQLTest.php @@ -50,6 +50,7 @@ class OQLTest extends ItopDataTestCase /** * @dataProvider NestedQueryProvider + * @depends testOQLSetup * * @param $sQuery * @@ -435,9 +436,7 @@ class OQLTest extends ItopDataTestCase { return [ 'Bug 3660 1' => [ - "SELECT UserRequest AS U - JOIN lnkContactToTicket AS l ON l.ticket_id=U.id - JOIN Team AS T ON l.contact_id=T.id", + "SELECT UserRequest AS U JOIN lnkContactToTicket AS l ON l.ticket_id=U.id JOIN Team AS T ON l.contact_id=T.id", "SELECT `U` FROM `UserRequest` AS `U` INNER JOIN `lnkContactToTicket` AS `l` ON `U`.`id` = `l`.`ticket_id` @@ -445,13 +444,64 @@ class OQLTest extends ItopDataTestCase ON `l`.`contact_id` = `T`.`id`", ], 'Bug 3660 2' => [ - "SELECT UserRequest AS U - JOIN lnkContactToTicket AS l ON l.ticket_id=U.id - JOIN Contact AS C ON l.contact_id=C.id", + "SELECT UserRequest AS U JOIN lnkContactToTicket AS l ON l.ticket_id=U.id JOIN Contact AS C ON l.contact_id=C.id", "SELECT `U` FROM `UserRequest` AS `U` INNER JOIN `lnkContactToTicket` AS `l` ON `U`.`id` = `l`.`ticket_id`", ], ]; } + + /** + * @dataProvider MakeSelectQueryForCountProvider + * + * @param $sOQL + * @param $sExpectedSQL + * + * @throws \CoreException + * @throws \MissingQueryArgument + * @throws \OQLException + */ + public function testMakeSelectQueryForCount($sOQL, $sExpectedSQL) + { + $oFilter = DBSearch::FromOQL($sOQL); + // Avoid adding all the fields for counts or "group by" requests + $aCountAttToLoad = array(); + $sMainClass = null; + foreach ($oFilter->GetSelectedClasses() as $sClassAlias => $sClass) { + $aCountAttToLoad[$sClassAlias] = array(); + if (empty($sMainClass)) { + $sMainClass = $sClass; + } + } + $sSQL = $oFilter->MakeSelectQuery([], [], $aCountAttToLoad, null, 0, 0, true); + static::assertEquals($sExpectedSQL, $sSQL); + } + + public function MakeSelectQueryForCountProvider() + { + return [ + 'Bug 3618' => [ + "SELECT UserRequest WHERE private_log LIKE '%Auteur : %' UNION SELECT Problem", + "SELECT COUNT(*) AS COUNT FROM (SELECT + 1 + FROM ( +SELECT + DISTINCT `UserRequest_Ticket`.`id` AS `UserRequestid` + FROM + `ticket` AS `UserRequest_Ticket` + WHERE ((`UserRequest_Ticket`.`private_log` LIKE '%Auteur : %') AND COALESCE((`UserRequest_Ticket`.`finalclass` IN ('UserRequest')), 1)) + + UNION + SELECT + DISTINCT `Problem`.`id` AS `Problemid` + FROM + `ticket_problem` AS `Problem` + WHERE 1 + +) as __selects__ +) AS _union_alderaan_", + ], + ]; + } } From dbb6e437512bdb7b282fbdffd32a69c7b46c1e55 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 5 Feb 2021 11:28:40 +0100 Subject: [PATCH 09/10] =?UTF-8?q?N=C2=B03618=20-=20Count=20on=20union=20wi?= =?UTF-8?q?th=20different=20conditions=20fails=20(Fix=20unit=20tests)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/core/OQLTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/core/OQLTest.php b/test/core/OQLTest.php index 3a3f0cf9d..6eef2a0e6 100644 --- a/test/core/OQLTest.php +++ b/test/core/OQLTest.php @@ -482,7 +482,7 @@ class OQLTest extends ItopDataTestCase { return [ 'Bug 3618' => [ - "SELECT UserRequest WHERE private_log LIKE '%Auteur : %' UNION SELECT Problem", + "SELECT UserRequest WHERE private_log LIKE '%Auteur : %' UNION SELECT UserRequest", "SELECT COUNT(*) AS COUNT FROM (SELECT 1 FROM ( @@ -494,9 +494,9 @@ SELECT UNION SELECT - DISTINCT `Problem`.`id` AS `Problemid` + DISTINCT `UserRequest`.`id` AS `UserRequestid` FROM - `ticket_problem` AS `Problem` + `ticket_request` AS `UserRequest` WHERE 1 ) as __selects__ From 6b76e5a8536fe41e0221e65dea3c042adacd76bd Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 8 Feb 2021 09:39:24 +0100 Subject: [PATCH 10/10] =?UTF-8?q?N=C2=B03618=20-=20Count=20on=20union=20wi?= =?UTF-8?q?th=20different=20conditions=20fails=20(php=20doc)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/oql/expression.class.inc.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index 2291b339a..ac23de24c 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -1696,6 +1696,10 @@ class FieldExpressionResolved extends FieldExpression parent::__construct($sName, $sParent); } + /** + * @return array of additional expressions for muti-column attributes + * @since 2.7.4 + */ public function AdditionalExpressions() { return $this->m_aAdditionalExpressions;