From 99c69cc461663156369ee6e3bf643529c1f94c98 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 31 Jul 2015 13:51:39 +0200 Subject: [PATCH] SimpleQuery: Add support for peek aheads refs #9661 --- library/Icinga/Data/SimpleQuery.php | 90 ++++++++++++++++++- library/Icinga/Repository/RepositoryQuery.php | 23 +++++ .../library/Monitoring/DataView/DataView.php | 23 +++++ 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Data/SimpleQuery.php b/library/Icinga/Data/SimpleQuery.php index bbc23a15f..ae6506e27 100644 --- a/library/Icinga/Data/SimpleQuery.php +++ b/library/Icinga/Data/SimpleQuery.php @@ -10,6 +10,7 @@ use Icinga\Application\Icinga; use Icinga\Application\Benchmark; use Icinga\Data\Filter\Filter; use Icinga\Exception\IcingaException; +use Icinga\Exception\ProgrammingError; use Icinga\Web\Paginator\Adapter\QueryAdapter; class SimpleQuery implements QueryInterface, Queryable, Iterator @@ -28,6 +29,13 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator */ protected $iterator; + /** + * The current position of this query's iterator + * + * @var int + */ + protected $iteratorPosition; + /** * The target you are going to query * @@ -83,6 +91,20 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator */ protected $limitOffset; + /** + * Whether to peek ahead for more results + * + * @var bool + */ + protected $peekAhead; + + /** + * Whether the query did not yield all available results + * + * @var bool + */ + protected $hasMore; + protected $filter; /** @@ -136,6 +158,7 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator } $this->iterator->rewind(); + $this->iteratorPosition = 0; Benchmark::measure('Query result iteration started'); } @@ -156,7 +179,15 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator */ public function valid() { - if (! $this->iterator->valid()) { + $valid = $this->iterator->valid(); + if ($valid && $this->peekAhead && $this->hasLimit() && $this->iteratorPosition + 1 === $this->getLimit()) { + $this->hasMore = true; + $valid = false; // We arrived at the last result, which is the requested extra row, so stop the iteration + } elseif (! $valid) { + $this->hasMore = false; + } + + if (! $valid) { Benchmark::measure('Query result iteration finished'); return false; } @@ -180,6 +211,7 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator public function next() { $this->iterator->next(); + $this->iteratorPosition += 1; } /** @@ -358,6 +390,36 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator return $this->order; } + /** + * Set whether this query should peek ahead for more results + * + * Enabling this causes the current query limit to be increased by one. The potential extra row being yielded will + * be removed from the result set. Note that this only applies when fetching multiple results of limited queries. + * + * @return $this + */ + public function peekAhead($state = true) + { + $this->peekAhead = (bool) $state; + return $this; + } + + /** + * Return whether this query did not yield all available results + * + * @return bool + * + * @throws ProgrammingError In case the query did not run yet + */ + public function hasMore() + { + if ($this->hasMore === null) { + throw new ProgrammingError('Query did not run. Cannot determine whether there are more results.'); + } + + return $this->hasMore; + } + /** * Set a limit count and offset to the query * @@ -390,7 +452,7 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator */ public function getLimit() { - return $this->limitCount; + return $this->peekAhead && $this->hasLimit() ? $this->limitCount + 1 : $this->limitCount; } /** @@ -460,6 +522,14 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator Benchmark::measure('Fetching all results started'); $results = $this->ds->fetchAll($this); Benchmark::measure('Fetching all results finished'); + + if ($this->peekAhead && $this->hasLimit() && count($results) === $this->getLimit()) { + $this->hasMore = true; + array_pop($results); + } else { + $this->hasMore = false; + } + return $results; } @@ -486,6 +556,14 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator Benchmark::measure('Fetching one column started'); $values = $this->ds->fetchColumn($this); Benchmark::measure('Fetching one column finished'); + + if ($this->peekAhead && $this->hasLimit() && count($values) === $this->getLimit()) { + $this->hasMore = true; + array_pop($values); + } else { + $this->hasMore = false; + } + return $values; } @@ -514,6 +592,14 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator Benchmark::measure('Fetching pairs started'); $pairs = $this->ds->fetchPairs($this); Benchmark::measure('Fetching pairs finished'); + + if ($this->peekAhead && $this->hasLimit() && count($pairs) === $this->getLimit()) { + $this->hasMore = true; + array_pop($pairs); + } else { + $this->hasMore = false; + } + return $pairs; } diff --git a/library/Icinga/Repository/RepositoryQuery.php b/library/Icinga/Repository/RepositoryQuery.php index aca7c1776..163bb40ff 100644 --- a/library/Icinga/Repository/RepositoryQuery.php +++ b/library/Icinga/Repository/RepositoryQuery.php @@ -344,6 +344,29 @@ class RepositoryQuery implements QueryInterface, SortRules, Iterator return $this->query->getOrder(); } + /** + * Set whether this query should peek ahead for more results + * + * Enabling this causes the current query limit to be increased by one. The potential extra row being yielded will + * be removed from the result set. Note that this only applies when fetching multiple results of limited queries. + * + * @return $this + */ + public function peekAhead($state = true) + { + return $this->query->peekAhead($state); + } + + /** + * Return whether this query did not yield all available results + * + * @return bool + */ + public function hasMore() + { + return $this->query->hasMore(); + } + /** * Limit this query's results * diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index 6a2b8a226..49f14a2e9 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -410,6 +410,29 @@ abstract class DataView implements QueryInterface, SortRules, IteratorAggregate return $this->query->count(); } + /** + * Set whether the query should peek ahead for more results + * + * Enabling this causes the current query limit to be increased by one. The potential extra row being yielded will + * be removed from the result set. Note that this only applies when fetching multiple results of limited queries. + * + * @return $this + */ + public function peekAhead($state = true) + { + return $this->query->peekAhead($state); + } + + /** + * Return whether the query did not yield all available results + * + * @return bool + */ + public function hasMore() + { + return $this->query->hasMore(); + } + /** * Set a limit count and offset *