The default value of $element in pager.inc is NULL.
And, if it stays null (nobody calls ->element($element)), we just generate the element number ourselves.
However, the following empty check is wrong:

  /**
   * Ensure that there is an element associated with this query.
   *
   * After running this query, access $this->element to get the element for this
   * query.
   */
  protected function ensureElement() {
    if (!empty($this->element)) {
      return;
    }

    $this->element = self::$maxElement++;
  }

The problem is that empty() is true if $this->element == 0, and 0 is the first pager element, which is perfectly valid. This means that even though we specified a valid element (0), the code generates it's own instead of returning at the beginning of the function.

This becomes a problem when writing tests, I'm creating multiple EntityFieldQuery objects (now with pager support), and EntityFieldQuery calls the pager, which instead of using element 0 for each test increments it, and tries using element 1, element 2, element 3, which fails...

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

bojanz’s picture

Status: Active » Needs review
FileSize
530 bytes

Here's a patch. empty() is replaced with is_null().

sun’s picture

Status: Needs review » Needs work

is_null() is always isset()

bojanz’s picture

Status: Needs work » Needs review

Edit: Okay, did a search, isset is faster and thus recommended. The fact that core uses is_null in some places does not make it cool.

bojanz’s picture

FileSize
527 bytes

New patch.

bojanz’s picture

FileSize
1.54 KB

Talked with chx on IRC, and he found another problem:
self::$maxElement is not incremented when $element is set via element().

The problem illustrated:
1) Add three pagers, specify elements (0, 1, 2)
2) Now add a pager, don't specify the element. Since $maxElement was not updated, it tries to use 0, which is wrong.
This works for any multiple number of pagers.

I'm including a better patch. Ideally, this should have tests, but I'm not sure how, since $element is a protected property (the pager tests could use some loving in any case...)

sun’s picture

+++ includes/pager.inc	6 Sep 2010 18:28:10 -0000
@@ -149,10 +149,16 @@ class PagerDefault extends SelectQueryEx
+    if ($element >= self::$maxElement) {
+      self::$maxElement = ++$element;

I think this should be just >, and we want to set $maxElement = $element

Or something along those lines.

I wonder whether one of those functions shouldn't call the other, so there's just one place where the value of $maxElement is managed?

Powered by Dreditor.

bojanz’s picture

Well, ensureElement() uses the current value of $maxElement, and THEN increments it.
That way the $element can start from 0 and not 1.

So if I set ->element(5) with your suggested changes in place, ensureElement would try to use 5 as the next element, which is wrong.

Btw, the patch is still not ready, talked with chx about how we could test this, and the tests are totally doable.

chx’s picture

    if ($element >= self::$maxElement) {
      self::$maxElement = ++$element;
    }

can just be self::$maxElement = $element + 1; you do not use $element later why increment it.

bojanz’s picture

FileSize
2.96 KB

True.
Changed that, and added a test. Confirmed that test fails with old pager.inc, succeeds with the new one.

bojanz’s picture

FileSize
3.42 KB

Made the tests more complete, this should be it.

bojanz’s picture

Title: Empty check in pager.inc is wrong » Multiple pager support is partially broken.

Changing the title to better reflect our problem.

chx’s picture

Component: base system » database system

Moving to database hoping it will invoke Crell, it's his tests anyways. I can review a bit later too.

Crell’s picture

module_invoke_all('Crell', $patch);

#10 makes sense to me. The only place I can think of where it would still break is if you explicitly set an element value that has already been used. What do we want to happen there? Blindly use it and hope the developer knows what he's using? (That's what it's doing now, and arguably what we've always done there.) Return a different element instead? (I'm sure chx will balk at that since that's the reason for the non-chainability of some SelectQuery methods.)

bojanz’s picture

Thanks for the quick review.

The only place I can think of where it would still break is if you explicitly set an element value that has already been used. What do we want to happen there? Blindly use it and hope the developer knows what he's using?

Yes. If you specify a used element, the $maxElement counter doesn't get incremented, and it's used blindly.
It does exactly what is expected, no need to work around incorrect usage.
I actually use it that way in the EFQ (pager/tablesort) tests, since there's no need to have 10 elements for 10 tests.

That's what it's doing now, and arguably what we've always done there.

Then we agree that it's an okay approach to take here?

Crell’s picture

Let's document that fact in the docblock, just to be explicit about it. Then we should be good to go.

bojanz’s picture

FileSize
3.52 KB

Here you go.

Crell’s picture

"Note that no auto-correction is done when setting an existing element ID, it is used as is." - That's a comma splice. :-) I'd suggest something like:

"Note that no collision detection is done when setting an element ID explicitly so it is possible for two pagers to end up using the same ID if both are set explicitly."

That warns people of exactly what the net effect is.

bojanz’s picture

FileSize
3.61 KB

I was wandering how to make that clearer, but couldn't find you on IRC.
Updated the patch.

An alternative could be "Note that no collision detection is done when setting an element ID explicitly, so it is possible for two pagers to end up using the same ID." which sounds less redundant, but it doesn't matter much.

bojanz’s picture

FileSize
3.57 KB

I really like my alternative from above, so I rolled a patch with that one too.
Pick the one you like best, and lets get this RTBC ;)

Crell’s picture

Status: Needs review » Reviewed & tested by the community

I like the extra clause in #18. Let's use that one. :-)

Thanks, Bojan!

webchick’s picture

Status: Reviewed & tested by the community » Fixed

This looks like a good clean-up, and adding some comments to explain this pager funkiness is always welcome.

Committed #18 to HEAD. Thanks, bojanz! :)

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.