Disclosure: This bug was found by @mattlibby and pinned down by Claude Code.

Problem/Motivation

With group_permissions enabled, loading the Revisions tab of a group entity (or running any access-checked entity query against group revisions) throws:

Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42S22]: Column not found:
1054 Unknown column 'base_table.type' in 'WHERE': SELECT "base_table"."revision_id" ...
FROM "groups_revision" "base_table"
INNER JOIN "groups_field_data" "groups_field_data" ON "groups_field_data"."id" = "base_table"."id"
LEFT OUTER JOIN "group_relationship_field_data" "gcfd"
ON base_table.id=gcfd.gid AND gcfd.plugin_id='group_membership' AND gcfd.entity_id=:account_id
WHERE ("groups_field_data"."id" = :db_condition_placeholder_0)
AND ((("base_table"."type" IN (:db_condition_placeholder_1)) AND ("gcfd"."entity_id" IS NULL))
OR (("groups_field_data"."type" IN (:db_condition_placeholder_2)) AND ("gcfd"."entity_id" IS NOT NULL)))
ORDER BY "base_table"."revision_id" DESC

Note the asymmetry: the outsider branch references `base_table.type` while the insider branch
references `groups_field_data.type`. The base table here is `groups_revision`, which has no `type` column — so the first branch is invalid SQL.

The group module's own `\Drupal\group\QueryAccess\GroupQueryAlter` does not have this problem,
because `group_permissions` replaces it with `GroupPermissionsGroupQueryAlter` for access-checked group queries.

Root cause

In `src/QueryAccess/GroupPermissionsGroupQueryAlter.php::addSynchronizedConditions()`, `getTableWithType()`
is called before `ensureMembershipJoin()`:

if (!empty($allowed_ids)) {
  $table_with_type = $this->getTableWithType();   // <-- called first
  $allowed_condition->condition("$table_with_type.type", array_unique($allowed_ids), 'IN');
}
...
$membership_alias = $this->ensureMembershipJoin();  // <-- called afterwards

`getTableWithType()` decides between the base table and the data table using `$this->isRevisionTable`:

protected function getTableWithType() {
  if ($this->dataTableAlias || $this->isRevisionTable) {
    return $this->ensureDataTable();
  }
  return $this->ensureBaseTable();
}

But `$this->isRevisionTable` is only set as a side effect of `ensureBaseTable()` (reached via `ensureMembershipJoin()` → `getMembershipJoinTable()`). On the first synchronized-scope iteration, `ensureBaseTable()` has not run yet, so `isRevisionTable === FALSE` and `getTableWithType()` returns the bare revision base table (`groups_revision`) — which lacks a `type` column. The call to `ensureBaseTable()` during that same `getTableWithType()` then sets `isRevisionTable = TRUE`, so the second iteration correctly uses the data table. Hence the asymmetric WHERE clause.

The group module's `GroupQueryAlter::addSynchronizedConditions()` avoids this by calling `ensureMembershipJoin()` first, which populates `isRevisionTable` before `getTableWithType()` is used. `group_permissions` reordered these calls (membership-join check moved after the type condition) and reintroduced the revision-table bug that group fixed.

Steps to reproduce

  1. Install `group` (3.3.x) + `group_permissions`, with `group_support_revisions` enabled.
  2. Create a group type whose members get access via synchronized outsider/insider scopes.
  3. As a user with view/edit access to a group, visit the group's Revisions tab, or run:
\Drupal::entityTypeManager()->getStorage('group')->getQuery()
  ->allRevisions()
  ->accessCheck(TRUE)
  ->condition('id', $group_id)
  ->execute();

→ `SQLSTATE[42S22] ... Unknown column 'base_table.type'`.

Proposed resolution

Make `getTableWithType()` self-sufficient by detecting the base table (and thus `isRevisionTable`) before branching, independent of call order:

protected function getTableWithType() {
  // Make sure the base table has been detected first, since that is what flags
  // $this->isRevisionTable. This class calls getTableWithType() before
  // ensureMembershipJoin() in addSynchronizedConditions(), so without this the
  // first invocation would see isRevisionTable === FALSE and return the bare
  // revision base table (groups_revision), which has no "type" column.
  $base_table = $this->ensureBaseTable();

  if ($this->dataTableAlias || $this->isRevisionTable) {
    return $this->ensureDataTable();
  }
  return $base_table;
}

`ensureBaseTable()` is idempotent (guarded by `$this->baseTableAlias === FALSE`) and was already invoked in both branches, so this only guarantees the revision flag is set before the branch decision. Verified against group 3.3.5 / group_permissions 2.0.0-alpha9 / Drupal 11.3.10: the revisions query now succeeds and non-revision group access queries are unaffected.

(An equivalent fix is to reorder `addSynchronizedConditions()` to call `ensureMembershipJoin()` before `getTableWithType()`, matching `\Drupal\group\QueryAccess\GroupQueryAlter`. The `getTableWithType()` fix is preferred because it is robust regardless of caller order.)

Remaining tasks

apply the working patch to a fork and make an MR.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

benstallings created an issue. See original summary.

benstallings’s picture

Issue summary: View changes
benstallings’s picture

Issue summary: View changes

lobsterr’s picture

Hm, strange. I have a fresh instance and I couldn't reproduce it there.

I have double checked the logic of group module and and logic of group permissions logic and I don't see the difference in terms of detection of the tables.

I tried both options your provided, with query and checking the revisions tab.

lobsterr’s picture

Assigned: benstallings » lobsterr
Status: Active » Needs review

ok, I am able to reproduce, I will check it

lobsterr’s picture

Assigned: lobsterr » Unassigned

I found the issue, I change the order of ensureMembershipJoin and that was causing the issue. I rolled back your changes and moved ensureMembershipJoin on the top.

Could you please check it?

benstallings’s picture

Status: Needs review » Reviewed & tested by the community

I like your changes! Thanks, @lobsterr.

lobsterr’s picture

Status: Reviewed & tested by the community » Fixed

Thank you for your contribution

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

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