diff --git a/modules/aggregator/aggregator.pages.inc b/modules/aggregator/aggregator.pages.inc
index 3ca084a..0b5b572 100644
--- a/modules/aggregator/aggregator.pages.inc
+++ b/modules/aggregator/aggregator.pages.inc
@@ -89,30 +89,61 @@ function aggregator_page_category_form($form, $form_state, $category) {
 }
 
 /**
- * Load feed items
+ * Loads and optionally filters feed items.
  *
  * @param $type
- *   The filter for the items. Possible values: 'sum', 'source', 'category'
+ *   The type of filter for the items. Possible values are:
+ *   - sum: No filtering.
+ *   - source: Filter the feed items, limiting the result to items from a
+ *     single source.
+ *   - category: Filter the feed items by category.
  * @param $data
- *   Feed or category data for filtering
+ *   Feed or category data used for filtering. The type and value of $data
+ *   depends on $type:
+ *   - source: $data is an object with $data->fid identifying the feed used to
+ *     as filter.
+ *   - category: $data is an array with $data['cid'] being the category id to
+ *     filter on.
+ *   The $data parameter is not used when $type is 'sum'.
+ *
  * @return
  *   An array of the feed items.
  */
 function aggregator_load_feed_items($type, $data = NULL) {
   $items = array();
-  $range_limit = 20;
   switch ($type) {
     case 'sum':
-      $result = db_query_range('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_item} i INNER JOIN {aggregator_feed} f ON i.fid = f.fid ORDER BY i.timestamp DESC, i.iid DESC', 0, $range_limit);
+      $query = db_select('aggregator_item', 'i');
+      $query->join('aggregator_feed', 'f', 'i.fid = f.fid');
+      $query->fields('i');
+      $query->addField('f', 'title', 'ftitle');
+      $query->addField('f', 'link', 'flink');
       break;
     case 'source':
-      $result = db_query_range('SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC', 0, $range_limit, array(':fid' => $data->fid));
+      $query = db_select('aggregator_item', 'i');
+      $query
+        ->fields('i')
+        ->condition('i.fid', $data->fid);
       break;
     case 'category':
-      $result = db_query_range('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = :cid ORDER BY timestamp DESC, i.iid DESC', 0, $range_limit, array(':cid' => $data['cid']));
+      $query = db_select('aggregator_category_item', 'c');
+      $query->leftJoin('aggregator_item', 'i', 'c.iid = i.iid');
+      $query->leftJoin('aggregator_feed', 'f', 'i.fid = f.fid');
+      $query
+        ->fields('i')
+        ->condition('cid', $data['cid']);
+      $query->addField('f', 'title', 'ftitle');
+      $query->addField('f', 'link', 'flink');
       break;
   }
 
+  $result = $query
+    ->extend('PagerDefault')
+    ->limit(20)
+    ->orderBy('i.timestamp', 'DESC')
+    ->orderBy('i.iid', 'DESC')
+    ->execute();
+
   foreach ($result as $item) {
     $item->categories = db_query('SELECT c.title, c.cid FROM {aggregator_category_item} ci LEFT JOIN {aggregator_category} c ON ci.cid = c.cid WHERE ci.iid = :iid ORDER BY c.title', array(':iid' => $item->iid))->fetchAll();
     $items[] = $item;
diff --git a/modules/aggregator/aggregator.test b/modules/aggregator/aggregator.test
index 1ab12dc..96517b1 100644
--- a/modules/aggregator/aggregator.test
+++ b/modules/aggregator/aggregator.test
@@ -266,10 +266,16 @@ EOF;
     return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_atom.xml';
   }
 
-  function createSampleNodes() {
+  /**
+   * Creates sample article nodes.
+   *
+   * @param $count
+   *   (optional) The number of nodes to generate.
+   */
+  function createSampleNodes($count = 5) {
     $langcode = LANGUAGE_NONE;
-    // Post 5 articles.
-    for ($i = 0; $i < 5; $i++) {
+    // Post $count article nodes.
+    for ($i = 0; $i < $count; $i++) {
       $edit = array();
       $edit['title'] = $this->randomName();
       $edit["body[$langcode][0][value]"] = $this->randomName();
@@ -807,6 +813,28 @@ class AggregatorRenderingTestCase extends AggregatorTestCase {
     $correct_titles = $this->xpath('//h1[normalize-space(text())=:title]', array(':title' => $feed->title));
     $this->assertFalse(empty($correct_titles), t('Aggregator feed page is available and has the correct title.'));
   }
+
+  /**
+   * Create a feed and check that feed's page.
+   */
+  public function testFeedPage() {
+    // Increase the number of items published in the rss.xml feed so we have
+    // enough articles to test paging.
+    variable_set('feed_default_items', 30);
+
+    // Create a feed with 30 items.
+    $this->createSampleNodes(30);
+    $feed = $this->createFeed();
+    $this->updateFeedItems($feed, 30);
+
+    // Check for the presence of a pager.
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $elements = $this->xpath("//ul[@class=:class]", array(':class' => 'pager'));
+    $this->assertTrue(!empty($elements), t('Individual source page contains a pager.'));
+
+    // Reset the number of items in rss.xml to the default value.
+    variable_set('feed_default_items', 10);
+  }
 }
 
 /**
@@ -856,4 +884,3 @@ class FeedParserTestCase extends AggregatorTestCase {
     $this->assertEqual('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', db_query('SELECT guid FROM {aggregator_item} WHERE link = :link', array(':link' => 'http://example.org/2003/12/13/atom03'))->fetchField(), 'Atom entry id element is parsed correctly.');
   }
 }
-
