According to #2805:
The SEEK method is currently only partially implemented, i.e. it allows only for fetching the "next" page. In order to fetch the "previous" page, a similar seekBefore() method could be introduced.
But what it generates is:
SELECT * FROM table
WHERE id < 5
ORDER BY id ASC
LIMIT 3
compared to seekAfter:
SELECT * FROM table
WHERE id > 5
ORDER BY id ASC
LIMIT 3
As you can see it only inverts the condition from > to <.
Let's assume that the table contains rows with id from 1 to 10.
Using seekAfter(5).limit(3) will return 6, 7 and 8,
but seekBefore(5).limit(3) will return 1, 2 and 3.
I think it really should return 2, 3 and 4.
I'd expect something like:
SELECT * FROM (
SELECT * FROM table
WHERE id < 5
ORDER BY id DESC
LIMIT 3
) AS t
ORDER BY id ASC
Thank you very much for reporting. We'll look into this ASAP
You're right, the expected behaviour is the one you're describing, and the one that was described in the original issue #2805. This slipped through because the integration tests for seekBefore() paginated back from page 2 to page 1, or they paginated without LIMIT, in case of which the current behaviour is accidentally correct.
Even if this is a behaviourally backwards-incompatible change, I'll assume that no one would want the current behaviour. Also, the change does not violate the Javadoc, but extend it, as the Javadoc currently doesn't make an example using LIMIT.
We'll fix this in jOOQ 3.10. Given the backwards incompatibility, the fix won't be merged to 3.9.4 or older releases.
For anyone who has the issue right now, there is a workaround if you need the behaviour of seekBefore as it is specified:
orderBy clause, e.g. A.asc(), B.desc() should become A.desc(), B.asc()seekAfter instead of seekBeforeCollections.reverseI'm afraid, this won't fit in jOOQ 3.10 anymore. There are some delicate details related to applying this with queries that involve unions to get right...
I think it's OK given that a workaround exists.
Looking into fixing this now. Things to consider:
sql
SELECT a FROM x ORDER BY b SEEK BEFORE 10 LIMIT 5
sql
SELECT a
FROM (
SELECT a, b -- b needs to be projected in order to be able to order it
FROM x
WHERE b < 10
ORDER BY b DESC
LIMIT 5
)
ORDER BY b ASC
DISTINCT makes everything even more complicatedSEEK BEFORE and SEEK AFTER. Other databases don't have this problem as they all have had a way of doing LIMIT / TOP / FETCH for ages. Perhaps, we won't fix this.UNION still don't work correctly with SEEK BEFORE and SEEK AFTER: #7459This is a really challenging change. Unfortunately, these things haven't been considered properly, prior to implementing this feature. It seems that deprecating it might be the best way forward.
The workaround you've documented is one option for simple cases. Another option is to manually wrap a SEEK query in a derived table and order it again.
Deprecation: #7461. it will be lifted again, once this is fixed.
@lukaseder
Another option is to manually wrap a SEEK query in a derived table and order it again.
How can we implement seekBack in terms of a derived table without knowing the column names? I want to implement seekBack once in a generic manner instead of having to reimplement once per query but then the implementation can't know the table, column or ordering that needs to be reversed ahead of time. Any ideas?
How can we implement seekBack in terms of a derived table without knowing the column names?
You can rename the columns in a derived table by using derived column lists. SQL syntax: (...) as t (a, b, c). jOOQ syntax: Table.as(String, String...). This may be necessary anyway, in case you have duplicate column names in your derived table (allowed in top level selects, but not in subqueries)
Alternatively, when you don't rename the columns, you can still access them by using Table.fields(). Hope this helps.
Most helpful comment
You're right, the expected behaviour is the one you're describing, and the one that was described in the original issue #2805. This slipped through because the integration tests for
seekBefore()paginated back from page 2 to page 1, or they paginated withoutLIMIT, in case of which the current behaviour is accidentally correct.Even if this is a behaviourally backwards-incompatible change, I'll assume that no one would want the current behaviour. Also, the change does not violate the Javadoc, but extend it, as the Javadoc currently doesn't make an example using
LIMIT.We'll fix this in jOOQ 3.10. Given the backwards incompatibility, the fix won't be merged to 3.9.4 or older releases.