Fix subquery joins for host and service group views

refs #2934
This commit is contained in:
Eric Lippmann 2018-07-18 13:16:48 +02:00
parent 3a434320e2
commit c168ebfe3a
20 changed files with 121 additions and 74 deletions

View File

@ -193,7 +193,7 @@ class ContactgroupQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$this->requireVirtualTable('hosts');
@ -209,6 +209,6 @@ class ContactgroupQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -185,7 +185,7 @@ class HostcommentQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -197,6 +197,6 @@ class HostcommentQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -177,7 +177,7 @@ class HostcommenthistoryQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -189,6 +189,6 @@ class HostcommenthistoryQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -191,7 +191,7 @@ class HostdowntimeQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -203,6 +203,6 @@ class HostdowntimeQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -186,7 +186,7 @@ class HostdowntimestarthistoryQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -198,6 +198,6 @@ class HostdowntimestarthistoryQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -176,7 +176,7 @@ class HostflappingstarthistoryQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -188,6 +188,6 @@ class HostflappingstarthistoryQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -3,6 +3,8 @@
namespace Icinga\Module\Monitoring\Backend\Ido\Query;
use Icinga\Exception\NotImplementedError;
/**
* Query for host groups
*/
@ -229,9 +231,17 @@ class HostgroupQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
if (! $and) {
// IN AND NOT IN works for OR filters w/o subquery joins
throw new NotImplementedError('');
} else {
// Propagate that the "parent" query has to be filtered as well
$additionalFilter = clone $filter;
}
$this->requireVirtualTable('members');
$query->joinVirtualTable('members');
@ -245,6 +255,6 @@ class HostgroupQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -238,7 +238,7 @@ class HostnotificationQuery extends IdoQuery
return $group;
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -250,6 +250,6 @@ class HostnotificationQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -192,7 +192,7 @@ class HoststatehistoryQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -204,6 +204,6 @@ class HoststatehistoryQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -297,7 +297,7 @@ class HoststatusQuery extends IdoQuery
));
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -309,6 +309,6 @@ class HoststatusQuery extends IdoQuery
return ['s.host_object_id', 'ho.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -503,14 +503,18 @@ abstract class IdoQuery extends DbQuery
/**
* Prepare the given query so that it can be linked to the parent
*
* @param IdoQuery $query
* @param string $name
* @param IdoQuery $query
* @param string $name
* @param FilterExpression $filter The filter which initiated the sub query
* @param bool $and Whether it's an AND filter
* @param bool $negate Whether it's an != filter
* @param FilterExpression $additionalFilter Filters which should be applied to the "parent" query
*
* @return array The first value is their, the second our key column
* @return array The first value is their, the second our key column
*
* @throws NotImplementedError In case the given query is unknown
* @throws NotImplementedError In case the given query is unknown
*/
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
throw new NotImplementedError('Query "%s" is unknown', $name);
}
@ -521,7 +525,7 @@ abstract class IdoQuery extends DbQuery
* @param FilterExpression $filter
* @param string $queryName
*
* @return FilterExpression
* @return Filter
*
* @throws QueryException
*/
@ -530,15 +534,42 @@ abstract class IdoQuery extends DbQuery
$expr = $filter->getExpression();
$op = $filter->getSign();
if ($op !== '=' && ! is_array($expr) && $op === '!=') {
// Fallback to standard filter behavior
if ($op === '=' && ! is_array($expr) && $op !== '!=') {
// We're joining a subquery only if the filter is enclosed in parentheses or if it's a != filter,
// e.g. hostgroup_name=(linux...), hostgroup_name!=linux, hostgroup_name!=(linux...)
throw new NotImplementedError('');
}
$subQuery = $this->createSubQuery($queryName);
$subQuery->setIsSubQuery();
list($theirs, $ours) = $this->joinSubQuery($subQuery, $queryName);
$subQueryFilter = clone $filter;
if ($op === '!=') {
$negate = true;
if (! is_array($expr)) {
// We assume that expression is an array later on but we'll support subquery joins for != filters
// which are not enclosed in parentheses
$expr = [$expr];
}
} else {
$negate = false;
}
if (count($expr) === 1 && strpos($expr[0], '&') !== false) {
// Our current filter implementation does not specify & as a control character so the count of the
// expression array is always one in this case
$expr = explode('&', $expr[0]);
$subQueryFilter->setExpression($expr);
$and = true;
} else {
// Or filters are respected by our filter implementation. No special handling needed here
$and = false;
}
$additional = null;
list($theirs, $ours) = $this->joinSubQuery($subQuery, $queryName, $subQueryFilter, $and, $negate, $additional);
$zendSelect = $subQuery->select();
$fromPart = $zendSelect->getPart($zendSelect::FROM);
@ -573,33 +604,8 @@ abstract class IdoQuery extends DbQuery
}
}
$subQueryFilter = clone $filter;
if ($op === '!=') {
$negate = true;
if (! is_array($expr)) {
$expr = [$expr];
}
$subQueryFilter = $subQueryFilter->setSign('=');
} else {
$negate = false;
}
$subQueryFilter->setColumn(preg_replace(
'/(?<=^|\s)\w+(?=\.)/',
'sub_$0',
$subQuery->aliasToColumnName($filter->getColumn())
));
if (count($expr) === 1 && strpos($expr[0], '&') !== false) {
$expr = explode('&', $expr[0]);
$subQueryFilter->setExpression($expr);
$and = true;
} else {
$and = false;
}
if ($and || $negate && ! $and) {
// Having is only required for AND and != filters,
// e.g. hostgroup_name=(ping&linux), hostgroup_name!=ping, hostgroup_name!=(ping|linux)
$groups = $subQuery->getGroup();
$group = $groups[0];
@ -610,6 +616,17 @@ abstract class IdoQuery extends DbQuery
$subQuery->select()->having("COUNT(DISTINCT $group) >= $cnt");
}
$subQueryFilter->setColumn(preg_replace(
'/(?<=^|\s)\w+(?=\.)/',
'sub_$0',
$subQuery->aliasToColumnName($filter->getColumn())
));
if ($negate) {
// != will be NOT EXISTS later
$subQueryFilter = $subQueryFilter->setSign('=');
}
$subQueryFilter = $subQueryFilter->andFilter(Filter::where(
preg_replace('/(?<=^|\s)\w+(?=\.)/', 'sub_$0', $theirs),
new Zend_Db_Expr($ours)
@ -624,7 +641,17 @@ abstract class IdoQuery extends DbQuery
// EXISTS is the column name because without any column $this->isCustomVar() fails badly otherwise.
// Additionally it bypasses the non-required optimizations made by our filter rendering implementation.
return new FilterExpression($negate ? 'NOT EXISTS' : 'EXISTS', '', new Zend_Db_Expr($subQuery));
$exists = new FilterExpression($negate ? 'NOT EXISTS' : 'EXISTS', '', new Zend_Db_Expr($subQuery));
if ($additional !== null) {
$alias = $additional->getColumn();
$this->requireColumn($alias);
$additional->setColumn($this->aliasToColumnName($alias));
return Filter::matchAll($exists, $additional);
}
return $exists;
}
protected function requireFilterColumns(Filter $filter)

View File

@ -199,7 +199,7 @@ class ServicecommentQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$this->requireVirtualTable('services');
@ -213,6 +213,6 @@ class ServicecommentQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -175,7 +175,7 @@ class ServicecommenthistoryQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('services');
@ -187,6 +187,6 @@ class ServicecommenthistoryQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -205,7 +205,7 @@ class ServicedowntimeQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('services');
@ -217,6 +217,6 @@ class ServicedowntimeQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -184,7 +184,7 @@ class ServicedowntimestarthistoryQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('services');
@ -196,6 +196,6 @@ class ServicedowntimestarthistoryQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -175,7 +175,7 @@ class ServiceflappingstarthistoryQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('services');
@ -187,6 +187,6 @@ class ServiceflappingstarthistoryQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -3,6 +3,8 @@
namespace Icinga\Module\Monitoring\Backend\Ido\Query;
use Icinga\Exception\NotImplementedError;
class ServicegroupQuery extends IdoQuery
{
protected $groupBase = array(
@ -184,7 +186,7 @@ class ServicegroupQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$this->requireVirtualTable('members');
@ -193,11 +195,19 @@ class ServicegroupQuery extends IdoQuery
return ['so.object_id', 'so.object_id'];
} elseif ($name === 'servicegroup') {
if (! $and) {
// IN AND NOT IN for OR filters works w/o subquery joins
throw new NotImplementedError('');
} else {
// Propagate that the "parent" query has to be filtered as well
$additionalFilter = clone $filter;
}
$query->joinVirtualTable('members');
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -239,7 +239,7 @@ class ServicenotificationQuery extends IdoQuery
return $group;
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$this->requireVirtualTable('services');
@ -253,6 +253,6 @@ class ServicenotificationQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -190,7 +190,7 @@ class ServicestatehistoryQuery extends IdoQuery
);
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('services');
@ -202,6 +202,6 @@ class ServicestatehistoryQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}

View File

@ -418,7 +418,7 @@ class ServicestatusQuery extends IdoQuery
}
}
protected function joinSubQuery(IdoQuery $query, $name)
protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
{
if ($name === 'hostgroup') {
$query->joinVirtualTable('members');
@ -430,6 +430,6 @@ class ServicestatusQuery extends IdoQuery
return ['sgm.service_object_id', 'so.object_id'];
}
return parent::joinSubQuery($query, $name);
return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
}
}