APIv4 - Fix setting offset with no limit
authorColeman Watts <coleman@civicrm.org>
Mon, 13 Apr 2020 02:34:32 +0000 (22:34 -0400)
committerColeman Watts <coleman@civicrm.org>
Mon, 13 Apr 2020 02:34:38 +0000 (22:34 -0400)
The API treats 0 as "no limit" but mysql does not.
This allows setting an offset with no limit but applying the maximum possible row count, as mysql does not allow LIMIT NULL.
See https://stackoverflow.com/questions/255517/mysql-offset-infinite-rows

Civi/Api4/Query/Api4SelectQuery.php
tests/phpunit/api/v4/Action/ContactGetTest.php

index 34c27ee671f2e54da0b2a3543451569a35e823e9..75dd6d14870836294d56325e947651e209e2b892 100644 (file)
@@ -241,7 +241,8 @@ class Api4SelectQuery extends SelectQuery {
    */
   protected function buildLimit() {
     if (!empty($this->limit) || !empty($this->offset)) {
-      $this->query->limit($this->limit, $this->offset);
+      // If limit is 0, mysql will actually return 0 results. Instead set to maximum possible.
+      $this->query->limit($this->limit ?: '18446744073709551615', $this->offset);
     }
   }
 
index 78d78eb3752eb61f1ce76e23313e85f9758cbc90..e1be3ca566cd962a1091ccc175bf794c4e82d56e 100644 (file)
@@ -58,4 +58,25 @@ class ContactGetTest extends \api\v4\UnitTestCase {
     $this->assertContains($del['id'], $contacts->column('id'));
   }
 
+  public function testGetWithLimit() {
+    $last_name = uniqid('getWithLimitTest');
+
+    $bob = Contact::create()
+      ->setValues(['first_name' => 'Bob', 'last_name' => $last_name])
+      ->execute()->first();
+
+    $jan = Contact::create()
+      ->setValues(['first_name' => 'Jan', 'last_name' => $last_name])
+      ->execute()->first();
+
+    $dan = Contact::create()
+      ->setValues(['first_name' => 'Dan', 'last_name' => $last_name])
+      ->execute()->first();
+
+    $num = Contact::get()->setCheckPermissions(FALSE)->selectRowCount()->execute()->count();
+    $this->assertCount($num - 1, Contact::get()->setCheckPermissions(FALSE)->setLimit(0)->setOffset(1)->execute());
+    $this->assertCount($num - 2, Contact::get()->setCheckPermissions(FALSE)->setLimit(0)->setOffset(2)->execute());
+    $this->assertCount(2, Contact::get()->setCheckPermissions(FALSE)->setLimit(2)->setOffset(0)->execute());
+  }
+
 }