Problem/Motivation

Using entityQuery() with node as a base table in combination with node access grants and revisions results in a fatal error. Coming from #2781423: Skip access checking during update., where this was thought to be a Paragraphs or View unpublished issue, but it appears to be a core bug.

Steps to reproduce

  • On a fresh D8 (Standard profile), enable a Node Access module (I used View Unpublished for this scenario).
  • Add an unpublished article
  • Rebuild node access permissions
  • As non-admin, run the following code:
    $entity_ids = \Drupal::entityQuery('node')
    ->condition('comment', NULL, 'IS NOT NULL')
    ->allRevisions()
    ->execute();
    

Executing this query will result in the exception below being thrown.

This is thought to be a core issue rather than View Unpublished since the hardcoded 'nid' column is added through node_query_node_access_alter() and NodeGrantDatabaseStorage::alterQuery()

Exception

The website encountered an unexpected error. Please try again later.

Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'node_revision__comment.nid' in 'where clause': SELECT base_table.vid AS vid, base_table.nid AS nid FROM {node_revision} base_table INNER JOIN {node_revision__comment} node_revision__comment ON node_revision__comment.revision_id = base_table.vid WHERE (node_revision__comment.comment_status IS NOT NULL) AND (EXISTS (SELECT na.nid AS nid FROM {node_access} na WHERE ((gid IN (:db_condition_placeholder_0)) AND (realm = :db_condition_placeholder_1)) AND (na.grant_view >= :db_condition_placeholder_2) AND ((node_revision__comment.nid = na.nid)))); Array ( [:db_condition_placeholder_0] => 0 [:db_condition_placeholder_1] => all [:db_condition_placeholder_2] => 1 ) in eval() (line 4 of modules/devel/src/Form/ExecutePHP.php(55) : eval()'d code).
Drupal\Core\Database\Statement->execute(Array, Array) (Line: 624)
Drupal\Core\Database\Connection->query('SELECT base_table.vid AS vid, base_table.nid AS nid
FROM 
{node_revision} base_table
INNER JOIN {node_revision__comment} node_revision__comment ON node_revision__comment.revision_id = base_table.vid
WHERE (node_revision__comment.comment_status IS NOT NULL) AND (EXISTS (SELECT na.nid AS nid
FROM 
{node_access} na
WHERE ((gid IN (:db_condition_placeholder_0)) AND (realm = :db_condition_placeholder_1)) AND (na.grant_view >= :db_condition_placeholder_2) AND ((node_revision__comment.nid = na.nid))))', Array, Array) (Line: 87)
Drupal\Core\Database\Driver\mysql\Connection->query('SELECT base_table.vid AS vid, base_table.nid AS nid
FROM 
{node_revision} base_table
INNER JOIN {node_revision__comment} node_revision__comment ON node_revision__comment.revision_id = base_table.vid
WHERE (node_revision__comment.comment_status IS NOT NULL) AND (EXISTS (SELECT na.nid AS nid
FROM 
{node_access} na
WHERE ((gid IN (:db_condition_placeholder_0)) AND (realm = :db_condition_placeholder_1)) AND (na.grant_view >= :db_condition_placeholder_2) AND ((node_revision__comment.nid = na.nid))))', Array, Array) (Line: 493)
Drupal\Core\Database\Query\Select->execute() (Line: 250)
Drupal\Core\Entity\Query\Sql\Query->result() (Line: 77)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 4)
eval() (Line: 55)
Drupal\devel\Form\ExecutePHP->submitForm(Array, Object)
call_user_func_array(Array, Array) (Line: 111)
Drupal\Core\Form\FormSubmitter->executeSubmitHandlers(Array, Object) (Line: 51)
Drupal\Core\Form\FormSubmitter->doSubmitForm(Array, Object) (Line: 585)
Drupal\Core\Form\FormBuilder->processForm('devel_execute_form', Array, Object) (Line: 314)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 74)
Drupal\Core\Controller\FormController->getContentResult(Object, Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array) (Line: 139)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 62)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 654)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

Comments

mr.baileys created an issue. See original summary.

mr.baileys’s picture

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

phenaproxima’s picture

StatusFileSize
new1.34 KB

Here's a fail patch proving the problem exists. It only happens, for me, when you combine an allRevisions() query with a field that has a dedicated table. It tries to join the field table on the 'nid' column, which field tables don't have.

timmillwood’s picture

Status: Active » Needs review
phenaproxima’s picture

Version: 8.5.x-dev » 8.6.x-dev

Also...8.6.x please!

Status: Needs review » Needs work

The last submitted patch, 6: 2823957-6-FAIL.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 6: 2823957-6-FAIL.patch, failed testing. View results

szato’s picture

Same error with paragraphs (entity_reference_revisions), on revision delete:
Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'node_revision__[ENTITY_REFERENCE_REVISION_FIELD].nid' in 'where clause': SELECT base_table.vid AS vid, base_table.nid AS nid FROM {node_revision} base_table INNER JOIN {node_revision__[ENTITY_REFERENCE_REVISION_FIELD]} node_revision__[ENTITY_REFERENCE_REVISION_FIELD] ON node_revision__[ENTITY_REFERENCE_REVISION_FIELD].revision_id = base_table.vid WHERE (node_revision__[ENTITY_REFERENCE_REVISION_FIELD].[ENTITY_REFERENCE_REVISION_FIELD]_target_revision_id = :db_condition_placeholder_0) AND (EXISTS (SELECT na.nid AS nid FROM {node_access} na WHERE (((gid IN (:db_condition_placeholder_1)) AND (realm = :db_condition_placeholder_2)) OR ((gid IN (:db_condition_placeholder_3)) AND (realm = :db_condition_placeholder_4)) OR ((gid IN (:db_condition_placeholder_5)) AND (realm = :db_condition_placeholder_6)) OR ((gid IN (:db_condition_placeholder_7)) AND (realm = :db_condition_placeholder_8))) AND (na.grant_view >= :db_condition_placeholder_9) AND (na.fallback = :db_condition_placeholder_10) AND ((node_revision__[ENTITY_REFERENCE_REVISION_FIELD].nid = na.nid)))); Array ( [:db_condition_placeholder_0] => 334030 [:db_condition_placeholder_1] => 0 [:db_condition_placeholder_2] => all [:db_condition_placeholder_3] => 286 [:db_condition_placeholder_4] => view_unpublished_author [:db_condition_placeholder_5] => 1 [:db_condition_placeholder_6] => view_unpublished_published_content [:db_condition_placeholder_7] => 1 [:db_condition_placeholder_8] => view_unpublished_content [:db_condition_placeholder_9] => 1 [:db_condition_placeholder_10] => 1 ) in Drupal\entity_reference_revisions\Plugin\Field\FieldType\EntityReferenceRevisionsItem->deleteRevision() (line 341 of /home/devel/www/***/web/modules/contrib/entity_reference_revisions/src/Plugin/Field/FieldType/EntityReferenceRevisionsItem.php).

szato’s picture

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new2.16 KB

The idea was good, but does it really hurt if we hardcode the 4 table names we really want to see there? We already hardcode two of them below. we could call getBaseTable(), getDataTable(), ... I guess...

The test is a start, but IMHO it should be in a kernel test as we don't need the UI for this. We also don't actually need a cardinality unlimited field, all configurable fields have dedicated tables at the moment. And we should have actual data, so we can assert that the query doesn't just run, it also returns the expected results. Possibly combined with an actual limit applied through node grants?

phenaproxima’s picture

+++ b/core/modules/node/node.module
@@ -1077,9 +1077,7 @@ function node_query_node_access_alter(AlterableInterface $query) {
-    /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
-    $table_mapping = \Drupal::entityTypeManager()->getStorage('node')->getTableMapping();
-    $node_base_tables = $table_mapping->getTableNames();
+    $node_base_tables = ['node', 'node_field_data', 'node_revision', 'node_revision_data'];

As you pointed out, we can make this a bit more flexible by loading the node entity type definition and calling getBaseTable(), getDataTable(), etc., rather than hard-coding. It's probably a better idea to use the abstraction layer.

...all configurable fields have dedicated tables at the moment.

"At the moment"? What if this changes later? Would it be more future-proof to try with fields of varying cardinality?

The test is a start, but IMHO it should be in a kernel test as we don't need the UI for this.

I put it into an existing test so that we could take advantage of existing node access-related test code (the node_access_test module in particular). It'd take a lot more effort and set-up code to stand up a kernel test that will reproduce this bug, and browser tests aren't that slow anymore since they're not installing a gigantic profile with a kazillion modules.

And we should have actual data, so we can assert that the query doesn't just run, it also returns the expected results. Possibly combined with an actual limit applied through node grants?

Yet another reason to keep the test in the functional space, to take advantage of BrowserTestBase's "realness".

berdir’s picture

Are you sure any of that code is actually needed, it might be enough to just have a node query access module enabled that adds grants?

Functional tests are still easily 5x slower than kernel tests and it is worth to keep tests kernel tests if they do not need a UI.

> Yet another reason to keep the test in the functional space, to take advantage of BrowserTestBase's "realness".

Don't agree with that. Nothing prevents us from adding data in a kernel test. The primary entity query test coverage (\Drupal\KernelTests\Core\Entity\EntityQuery*Test) are kernel tests.

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

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

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.

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

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new2.15 KB

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

shishircoolin’s picture

Status: Needs work » Postponed

The "Node Access module" is not fully compatible with Drupal 10, let alone Drupal 11.x. I'm unable to reproduce this issue for now.

acbramley’s picture

Status: Postponed » Postponed (maintainer needs more info)

Not much action here in a while, is this still reproducible?

smustgrave’s picture

Wanted to bump 1 more time if anyone still experiencing this.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

egruel’s picture

Version: main » 11.3.x-dev
Status: Postponed (maintainer needs more info) » Active
StatusFileSize
new740 bytes

I encountered this issue on Drupal 11.3.2 when combining custom hook_node_grants() implementations with entity reference revision fields (paragraphs).

Steps to reproduce:
1. Implement custom hook_node_grants() and hook_node_access_records()
2. Create content with entity reference revision fields (e.g., paragraph fields)
3. Visit a page containing this content as an anonymous user

Error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'node_revision__field_FIELDNAME.nid' in 'where clause'

Root cause:
In NodeGrantDatabaseStorage::alterQuery() (line 193), the code hardcodes $field = 'nid' for all tables. However, field revision tables (node_revision__field_*) and field data tables (node__field_*) use entity_id instead of nid
as their primary key column.

Proposed solution:
The attached patch detects the table type and uses the appropriate column name:
- entity_id for field tables (node_revision__field_* and node__field_*)
- nid for base node tables

This issue affects sites using custom node access grants with entity reference revision fields in Drupal 11.3+.

smustgrave’s picture

Version: 11.3.x-dev » main
ghost of drupal past’s picture

#13 looks much better to me at least but I think calling the four methods from the table mapping instead of hardwiring them would be best.