From 7aa12f192a6bde7edc34cc230b37db94182b2927 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 14 May 2018 09:16:09 -0700 Subject: [PATCH] Add PhabricatorQueryIterator, for buffered iteration over a CursorPagedPolicyAwareQuery Summary: See D19446. This should make it easier to process larger, more complex result sets in constant memory. Today, `LiskMigrationIterator` takes constant memory but can't apply `needX()` reqeusts or `withY(...)` constraints. Using a raw `Query` can handle this stuff, but requires memory proportional to the size of the result set. Offer the best of both worlds: constant memory and full access to the power of `Query` classes. Test Plan: Used this script to iterate over every commit, saw sensible behavior: ```name=list-commits.php setViewer($viewer); $iterator = new PhabricatorQueryIterator($query); foreach ($iterator as $commit) { echo $commit->getID()."\n"; } ``` Reviewers: amckinley Reviewed By: amckinley Differential Revision: https://secure.phabricator.com/D19450 --- src/__phutil_library_map__.php | 2 + .../storage/lisk/PhabricatorQueryIterator.php | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/infrastructure/storage/lisk/PhabricatorQueryIterator.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e1b13dbecb..fab67c4087 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4032,6 +4032,7 @@ phutil_register_library_map(array( 'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php', 'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php', 'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php', + 'PhabricatorQueryIterator' => 'infrastructure/storage/lisk/PhabricatorQueryIterator.php', 'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php', 'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php', 'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php', @@ -9876,6 +9877,7 @@ phutil_register_library_map(array( 'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorQuery' => 'Phobject', 'PhabricatorQueryConstraint' => 'Phobject', + 'PhabricatorQueryIterator' => 'PhutilBufferedIterator', 'PhabricatorQueryOrderItem' => 'Phobject', 'PhabricatorQueryOrderTestCase' => 'PhabricatorTestCase', 'PhabricatorQueryOrderVector' => array( diff --git a/src/infrastructure/storage/lisk/PhabricatorQueryIterator.php b/src/infrastructure/storage/lisk/PhabricatorQueryIterator.php new file mode 100644 index 0000000000..c48600eb02 --- /dev/null +++ b/src/infrastructure/storage/lisk/PhabricatorQueryIterator.php @@ -0,0 +1,41 @@ +query = $query; + } + + protected function didRewind() { + $this->pager = new AphrontCursorPagerView(); + } + + public function key() { + return $this->current()->getID(); + } + + protected function loadPage() { + if (!$this->pager) { + return array(); + } + + $pager = clone $this->pager; + $query = clone $this->query; + + $results = $query->executeWithCursorPager($pager); + + // If we got less than a full page of results, this was the last set of + // results. Throw away the pager so we end iteration. + if (count($results) < $pager->getPageSize()) { + $this->pager = null; + } else { + $this->pager->setAfterID($pager->getNextPageID()); + } + + return $results; + } + +}