Problem/Motivation

1. Install latest HEAD (8.7.x) with minimal profile.
2. Enable workspaces, toolbar modules
3. Visit admin/content
4. Change workspace from 'live' to 'stage' (user get redirected to user/1 page)
5. Try to visit admin/content again
Expected:
6. See content listing page

Actual:
6. See 'The website encountered an unexpected error.' After including settings.local.php, would get:


The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Drupal\Core\Database\DatabaseExceptionWrapper</em>: SQLSTATE[42000]: Syntax error or access violation: 1140 Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause: SELECT min(node_field_data.nid) AS expression, COALESCE(workspace_association.target_entity_revision_id, base_table.vid) AS vid, base_table.nid AS nid
FROM 
{node} base_table
LEFT OUTER JOIN {workspace_association} workspace_association ON workspace_association.target_entity_type_id = &#039;node&#039; AND workspace_association.target_entity_id = base_table.nid AND workspace_association.workspace = &#039;stage&#039;
LEFT JOIN {node_field_data} node_field_data ON node_field_data.nid = base_table.nid
ORDER BY expression ASC
LIMIT 50 OFFSET 0; Array
(
)
 in <em class="placeholder">Drupal\Core\Entity\EntityListBuilder-&gt;getEntityIds()</em> (line <em class="placeholder">104</em> of <em class="placeholder">core/lib/Drupal/Core/Entity/EntityListBuilder.php</em>). <pre class="backtrace">Drupal\Core\Database\Statement-&gt;execute(Array, Array) (Line: 625)
Drupal\Core\Database\Connection-&gt;query(&#039;SELECT min(node_field_data.nid) AS expression, COALESCE(workspace_association.target_entity_revision_id, base_table.vid) AS vid, base_table.nid AS nid
FROM 
{node} base_table
LEFT OUTER JOIN {workspace_association} workspace_association ON workspace_association.target_entity_type_id = &#039;node&#039; AND workspace_association.target_entity_id = base_table.nid AND workspace_association.workspace = &#039;stage&#039;
LEFT JOIN {node_field_data} node_field_data ON node_field_data.nid = base_table.nid
ORDER BY expression ASC
LIMIT 50 OFFSET 0&#039;, Array, Array) (Line: 87)
Drupal\Core\Database\Driver\mysql\Connection-&gt;query(&#039;SELECT min(node_field_data.nid) AS expression, COALESCE(workspace_association.target_entity_revision_id, base_table.vid) AS vid, base_table.nid AS nid
FROM 
{node} base_table
LEFT OUTER JOIN {workspace_association} workspace_association ON workspace_association.target_entity_type_id = &#039;node&#039; AND workspace_association.target_entity_id = base_table.nid AND workspace_association.workspace = &#039;stage&#039;
LEFT JOIN {node_field_data} node_field_data ON node_field_data.nid = base_table.nid
ORDER BY expression ASC
LIMIT 50 OFFSET 0&#039;, Array, Array) (Line: 510)
Drupal\Core\Database\Query\Select-&gt;execute() (Line: 257)
Drupal\Core\Entity\Query\Sql\Query-&gt;result() (Line: 76)
Drupal\Core\Entity\Query\Sql\Query-&gt;execute() (Line: 104)
Drupal\Core\Entity\EntityListBuilder-&gt;getEntityIds() (Line: 86)
Drupal\Core\Entity\EntityListBuilder-&gt;load() (Line: 234)
Drupal\Core\Entity\EntityListBuilder-&gt;render() (Line: 23)
Drupal\Core\Entity\Controller\EntityListController-&gt;listing(&#039;node&#039;)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;Drupal\Core\EventSubscriber\{closure}() (Line: 582)
Drupal\Core\Render\Renderer-&gt;executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;Drupal\Core\EventSubscriber\{closure}() (Line: 151)
Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel-&gt;handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle-&gt;handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache-&gt;pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware-&gt;handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware-&gt;handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel-&gt;handle(Object, 1, 1) (Line: 665)
Drupal\Core\DrupalKernel-&gt;handle(Object) (Line: 19)
</pre>

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

vijaycs85 created an issue. See original summary.

vijaycs85’s picture

vijaycs85’s picture

Status: Needs review » Needs work

The last submitted patch, 3: 2987956-3-test-only.patch, failed testing. View results

amateescu’s picture

The bug here is caused by our entity query alterations, not by the workspace switching functionality :)

amateescu’s picture

Issue tags: +Workflow Initiative
amateescu’s picture

Title: Workspace switch throws MySQL[42000] on admin/content page » Entity queries with sorting and ordering are not working in a non-default workspace
mariancalinro’s picture

Here's an attempt to fix this, let's see what the tests have to say.

My approach is to take the fields we are adding to the query (id and revision fields) and add them to the GROUP BY clause too, if the query has something in the GROUP BY clause already. At the moment this only happens when sorting complex queries, but I didn't wat to use sorting as a condition, as in the future we may find there are other ways for the query to have grouping.

Status: Needs review » Needs work

The last submitted patch, 8: 2987956-8.patch, failed testing. View results

mariancalinro’s picture

Status: Needs work » Needs review
FileSize
2.28 KB

Another atemp, it seems on some queries the GROUP BY is empty even if the aggregation functions MIN/MAX are used for sorting. For these cases we need another condition to detect if we should add the fields to the GROUP BY clause. We check if the query has sorting and it's not a simple query, specifically the case where the error happens.

Status: Needs review » Needs work

The last submitted patch, 10: 2987956-9.patch, failed testing. View results

mariancalinro’s picture

I missed the fact that the sorting processing happens after we prepare the query, so I moved the checks to the addSort() method, after the parent method is executed. At this point we should know if we need to add or not the id fields to the GROUP BY clause.
Let's see what the tests have to say.

mariancalinro’s picture

Status: Needs work » Needs review

The tests passed :)

mheip’s picture

When I applied above patch, the issue with my entity query was indeed resolved.
The fatal error disappeared.

However, I noticed that there were a couple of issues with my entity list overviews.
Several entities were missing.

I found the issue and I'll try to explain:

Say I have 3 entities with following ids, reference ids:
|id|reference_id|
|1|1|
|2|2|
|3|5|

When I do not add sorting, the result I get is a keyed array:

$ids = [
 '1' => '1',
 '2' => '2',
 '5' => '3',
];

Entities can then be used by doing a loadMultiple($ids);
When the EntityQuery runs it's result function, the following piece of code is called:

return $this->sqlQuery->execute()->fetchAllKeyed();

The FetchAllKeyed method will use index 0 as it's key index, and index 1 as it's value index.

When the SQL query is altered by workspaces, 2 expressions are added in our Workspaces Query (Drupal\workspaces\EntityQuery\Query):

$this->sqlExpressions[$revision_field] = "COALESCE(workspace_association.target_entity_revision_id, base_table.$revision_field)";
$this->sqlExpressions[$id_field] = "base_table.$id_field";

And these are attached in the "finish" function:

protected function finish() {
    foreach ($this->sqlExpressions as $alias => $expression) {
      $this->sqlQuery->addExpression($expression, $alias);
    }
    return parent::finish();
}

When you don't have a sort, the result key will be the "revision" expression, and the result value will be the "id" expression.

However, when a query has a sort, this will also add an expression in the "addSort" method. And this expression will be added before the above 2 expressions. Because the expression is added in the "addSort" method instead of the "finish" method.

When you for example sort on "id" => "ASC", the following expression will be added:

min({entity}.id) AS expression

Your raw result set will suddenly be:
|1|1|1|
|2|2|2|
|3|5|3|

Your keyed results will then be:

$ids = [
 '1' => '1',
 '2' => '2',
 '3' => '5',
];

Meaning the last entity will not be found as it looks for an entity with an id of 5, which will not exist.

I have fixed this in the attached patch by adding the expressions immediately in the prepare function. It also contains the patch functionality in #12 by mariancalinro, as this is needed to avoid the fatal error.

This patch needs review and was tested on 8.6.5.

mallezie’s picture

Status: Needs review » Needs work

In the latest patch, it seems the test coverage from #12 is missing.

amateescu’s picture

Status: Needs work » Postponed

Thanks @mariancalinro and @mheip, the patches and the explanation from #14 are excellent detective work on the internals of entity query :)

I've just proposed a patch that changes the way entity query generates its SQL query in #2950869-30: Entity queries querying the latest revision very slow with lots of revisions. That patch also fixes this problem in Workspaces, so I've credited both of you in that issue for the helpful comments and solution here. If that patch gets in, the only goal of this issue would be to add the additional test coverage.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

amateescu’s picture

Version: 8.8.x-dev » 8.7.x-dev
Priority: Normal » Major
Status: Postponed » Needs review
FileSize
5.01 KB

Since #2950869: Entity queries querying the latest revision very slow with lots of revisions doesn't have a chance of getting into 8.7.0 anymore, let's get back to fixing this in Workspaces for now.

Reading all the comments and patches again, I think we should combine the approaches from #10 and #14. The problem with sorting described in #14 can be solved by providing an override for the isSimpleQuery() method, and can be tested by asserting the result array of the entity query.

At the moment, this bug prevents Workspaces from loading the node edit page, so I'm raising the priority to major.

dixon_’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
118.71 KB

The patch looks good and is quite simple! The additional tests adds great coverage too!

Manual testing is trivial - the node form was broken on non-live workspaces (like stage), and it's no longer broken :)

Screenshot

plach’s picture

Manually tested this as well, I wasn't able to get a broken form but the STR mentioned in the OP worked for me. I was a bit concerned about always adding the group by clause with complex queries but then I realized that the parent class does the same, so this change shouldn't have a performance impact.

Great detective work, guys!

  • plach committed 07c4fcc on 8.7.x
    Issue #2987956 by mariancalinro, amateescu, vijaycs85, mheip, mallezie:...

  • plach committed 073cc74 on 8.8.x
    Issue #2987956 by mariancalinro, amateescu, vijaycs85, mheip, mallezie:...

  • plach committed 53ce334 on 8.7.x
    Revert "Issue #2987956 by mariancalinro, amateescu, vijaycs85, mheip,...
  • plach committed 92b385e on 8.7.x
    Issue #2987956 by mariancalinro, amateescu, vijaycs85, mheip, mallezie:...
plach’s picture

Committed 073cc74 and pushed to 8.8.x and backported to 8.7.x. Thanks!

plach’s picture

Status: Reviewed & tested by the community » Fixed

I had to revert the initial commit as I erroneously committed/pushed to 8.7.x first instead of cherry-picking from 8.8.x. The following two commits fix this and the latter has a nice reference to the 8.8.x commit, as it should. Sorry for the confusion :)

Status: Fixed » Closed (fixed)

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