Skip to content

Fix paginated association subqueries on MariaDB - #19596

Merged
markstory merged 3 commits into
cakephp:5.xfrom
oiahoon:fix/subquery-distinct-pagination
Sep 12, 2026
Merged

markstory merged 3 commits into
cakephp:5.xfrom
oiahoon:fix/subquery-distinct-pagination

Conversation

@oiahoon

@oiahoon oiahoon commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Use SELECT DISTINCT to deduplicate ungrouped MariaDB association-filter subqueries without triggering the lateral GROUP BY optimization that can apply pagination per joined row.

Explicit grouping semantics and other database drivers retain GROUP BY. Ordered columns are included in the reduced select list where MariaDB requires them.

The regression coverage verifies both the generated SQL and the contained results from a page with a nonzero offset.

Fixes #19595.

@oiahoon
oiahoon force-pushed the fix/subquery-distinct-pagination branch 2 times, most recently from 5739401 to 41431f0 Compare August 17, 2026 11:52
@alexvanniel

Copy link
Copy Markdown

Any chance of this fix being merged in the master branch? As far as I can tell this could potentially fix the issue I am seeing with MariaDB. But I do not know if this change might potentially screw up something else.

Comment thread src/ORM/Association/Loader/SelectLoader.php
Comment thread tests/TestCase/ORM/Query/QueryRegressionTest.php Outdated
@alexvanniel

Copy link
Copy Markdown

@ADmad What would it take to get this to make it to the master? What needs to be done for this to become an accepted fix? It would mean a lot to me if I would not have to resort to adding strategy => select on all my associations that are also going to be paginated.

@ADmad

ADmad commented Aug 19, 2026

Copy link
Copy Markdown
Member

@alexvanniel As you can see by my comments the review is already underway, so please be patient. Repeatedly asking "what will it take to get it merged" isn't going to speed up the process.

@alexvanniel

Copy link
Copy Markdown

@alexvanniel As you can see by my comments the review is already underway, so please be patient. Repeatedly asking "what will it take to get it merged" isn't going to speed up the process.

My apologies. I had no idea what the course of action would be on this. Rest assured I was not trying to urge anyone on to speed this up. I was merely concerned this would become stale. So, as you said, I will be patient.

@oiahoon
oiahoon force-pushed the fix/subquery-distinct-pagination branch 2 times, most recently from 0daeefc to 7268795 Compare August 21, 2026 01:56
Comment thread src/ORM/Association/Loader/SelectLoader.php
Comment thread tests/TestCase/ORM/Query/QueryRegressionTest.php

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts ORM subquery-based eager loading to avoid a MariaDB optimizer misbehavior that can return incorrect/empty associated rows on paginated pages beyond the first. It does so by switching deduplication from GROUP BY to SELECT DISTINCT for MariaDB filter subqueries, while preserving existing grouping behavior for other drivers and explicit grouping queries.

Changes:

  • Use SELECT DISTINCT (instead of GROUP BY) to deduplicate MariaDB subquery-strategy filter subqueries when the source query is not explicitly grouped.
  • Ensure ordered columns can be included in the reduced subquery select list (needed for DISTINCT + ORDER BY compatibility).
  • Update/add regression assertions around the generated SQL for the subquery join.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/ORM/Association/Loader/SelectLoader.php Switch deduplication strategy to DISTINCT for MariaDB and optionally include ORDER BY fields in the subquery select list.
tests/TestCase/ORM/Query/QueryRegressionTest.php Update regression coverage to assert MariaDB uses DISTINCT and other drivers retain GROUP BY.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/TestCase/ORM/Query/QueryRegressionTest.php
Comment thread tests/TestCase/ORM/Query/QueryRegressionTest.php
Use DISTINCT for ungrouped association filter queries so MariaDB does not apply its lateral GROUP BY optimization. Preserve explicit grouping and select ordered columns for PostgreSQL compatibility.

Refs cakephp#19595.
@oiahoon
oiahoon force-pushed the fix/subquery-distinct-pagination branch from 7268795 to daec949 Compare August 23, 2026 04:31
@ADmad
ADmad requested a review from markstory August 23, 2026 08:35
@dereuromark

Copy link
Copy Markdown
Member

I added one test on top to harden it.

@dereuromark dereuromark added defect needs squashing The pull request should be squashed before merging labels Aug 24, 2026
Comment on lines +488 to +491
$driver = $filterQuery->getDriver();
$useDistinct = !$filterQuery->clause('group')
&& $driver instanceof Mysql
&& $driver->isMariadb();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having driver sniffs in the ORM code isn't ideal. Could use one of the DriverFeatureEnum values instead? Adding another DriverFeatureEnum option would be preferable as well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how one of the existing DriverFeatureEnum cases can be used for this and adding a new case in a patch release would be a breaking change for any custom driver implementation as the new case would not be handled by its supports() method.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, lets move forward with this then, and in 5.5 we can expand DriverFeatureEnum.

@markstory
markstory merged commit 697eb35 into cakephp:5.x Sep 12, 2026
15 checks passed
markstory added a commit that referenced this pull request Sep 13, 2026
Add new cases to DriverFeatureEnum for recently added driver specific
behavior related to subquery association loading. I'd like to avoid
having driver specific logic in the ORM layer. We have
Driver::supports() which allows us to model this driver specific
behavior like we do for other driver/dialect specific feature support.

Refs #19596
Refs #19552
markstory added a commit that referenced this pull request Sep 15, 2026
…9619)

* Add DriverFeatureEnum for recently added driver specific behavior

Add new cases to DriverFeatureEnum for recently added driver specific
behavior related to subquery association loading. I'd like to avoid
having driver specific logic in the ORM layer. We have
Driver::supports() which allows us to model this driver specific
behavior like we do for other driver/dialect specific feature support.

Refs #19596
Refs #19552

* Apply suggestions from code review

Co-authored-by: othercorey <[email protected]>

---------

Co-authored-by: othercorey <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

defect needs squashing The pull request should be squashed before merging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Paginated hasMany/belongsToMany contain() with subquery strategy returns empty data on pages beyond the first (MariaDB)

7 participants