Custom roles and group project sharing work well
What does this MR do and why?
Ensuring that custom roles work as expected when in use with project group sharing. UI bugs have surfaced associated with imported users looking like they've still retained a custom role from their source project, even when the target project doesn't have custom roles enabled.
- backfill spec to ensure we've stopped the bleeding with imports
- add spec to ee/member_finder for ultimate/free scenario
- set CAST(NULL AS integer) AS member_role_id
part of: https://gitlab.com/gitlab-org/gitlab/-/issues/572286
Screenshots or screen recordings
Before | After |
---|---|
![]() |
![]() |
How to set up and validate locally
Instance settings
GITLAB_SIMULATE_SAAS: 1
::Gitlab::CurrentSettings.update!(check_namespace_plan: true)
- Instance license should be "Ultimate"
Scenario set up
Group A
- Give "Group A" namespace ultimate plan: http://127.0.0.1:3000/admin/groups/GROUP_SLUG/edit
- Create custom role for "Group A": http://127.0.0.1:3000/groups/GROUP_SLUG/-/settings/roles_and_permissions/new
- Assign "Group A" member a custom role: http://127.0.0.1:3000/groups/GROUP_SLUG/-/group_members
Group B
- Invite "Group A" to project owned by "Group B": http://localhost:3000/GROUP_B_SLUG/PROJECT_B_SLUG/-/project_members
Validation
- Verify that member from "Group A" does not have custom role listed in members page: http://localhost:3000/GROUP_B_SLUG/PROJECT_B_SLUG/-/project_members
Database Review
Queries
The only query change should be the addition of CAST(NULL AS integer) AS member_role_id
.
Query plan: https://console.postgres.ai/shared/4a5b3cfd-f9f6-4da9-9730-3e016363b547
Before SQL
SELECT
"members\".\ "id ",
"members\".\ "access_level ",
"members\".\ "source_id ",
"members\".\ "source_type ",
"members\".\ "user_id ",
"members\".\ "notification_level ",
"members\".\ "type ",
"members\".\ "created_at ",
"members\".\ "updated_at ",
"members\".\ "created_by_id ",
"members\".\ "invite_email ",
"members\".\ "invite_token ",
"members\".\ "invite_accepted_at ",
"members\".\ "requested_at ",
"members\".\ "expires_at ",
"members\".\ "ldap ",
"members\".\ "override ",
"members\".\ "state ",
"members\".\ "invite_email_success ",
"members\".\ "member_namespace_id ",
"members\".\ "member_role_id ",
"members\".\ "expiry_notified_at ",
"members\".\ "request_accepted_at "
FROM ( SELECT DISTINCT ON (user_id, invite_email)
member_union.id,
COALESCE(project_authorizations.access_level, member_union.access_level) access_level,
member_union.source_id,
member_union.source_type,
member_union.user_id,
member_union.notification_level,
member_union.type,
member_union.created_at,
member_union.updated_at,
member_union.created_by_id,
member_union.invite_email,
member_union.invite_token,
member_union.invite_accepted_at,
member_union.requested_at,
member_union.expires_at,
member_union.ldap,
member_union.override,
member_union.state,
member_union.invite_email_success,
member_union.member_namespace_id,
member_union.member_role_id,
member_union.expiry_notified_at,
member_union.request_accepted_at nFROM ((
SELECT
"members\".\ "id ", "members\".\ "access_level ", "members\".\ "source_id ", "members\".\ "source_type ", "members\".\ "user_id ", "members\".\ "notification_level ", "members\".\ "type ", "members\".\ "created_at ", "members\".\ "updated_at ", "members\".\ "created_by_id ", "members\".\ "invite_email ", "members\".\ "invite_token ", "members\".\ "invite_accepted_at ", "members\".\ "requested_at ", "members\".\ "expires_at ", "members\".\ "ldap ", "members\".\ "override ", "members\".\ "state ", "members\".\ "invite_email_success ", "members\".\ "member_namespace_id ", "members\".\ "member_role_id ", "members\".\ "expiry_notified_at ", "members\".\ "request_accepted_at "
FROM ( SELECT DISTINCT ON (user_id, invite_email)
*
FROM "members "
WHERE
"members\".\ "type " = 'GroupMember'
AND "members\".\ "source_type " = 'Namespace'
AND "members\".\ "requested_at " IS NULL
AND "members\".\ "source_id " IN (
SELECT
"namespaces\".\ "id "
FROM (
SELECT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_encrypted ", "namespaces\".\ "custom_project_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_notification_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
AND "namespaces\".\ "id " = 9) namespaces
WHERE
"namespaces\".\ "type " = 'Group')
AND (members.access_level > 5)
ORDER BY user_id, invite_email, nCASE
WHEN source_id = 9
AND source_type = 'Namespace' nTHEN access_level + 1
ELSE
access_level
END DESC, nmember_role_id ASC, expires_at DESC, created_at ASC n) members
WHERE
"members\".\ "type " = 'GroupMember'
AND "members\".\ "invite_token " IS NULL
AND (membe rs.access_level > 5)) nUNION ALL n (
SELECT
"members\".\ "id ", "members\".\ "access_level ", "members\".\ "source_id ", "members\".\ "source_type ", "members\".\ "user_id ", "members\".\ "notification_level ", "members\".\ "type ", "members\".\ "created_at ", "members\".\ "updated_at ", "members\".\ "created_by_id ", "members\".\ "invite_email ", "members\".\ "invite_token ", "members\".\ "invite_accepted_at ", "members\".\ "requested_at ", "members\".\ "expires_at ", "members\".\ "ldap ", "members\".\ "override ", "members\".\ "state ", "members\".\ "invite_email_success ", "members\".\ "member_namespace_id ", "members\".\ "member_role_id ", "members\".\ "expiry_notified_at ", "members\".\ "request_accepted_at "
FROM "members "
WHERE
"members\".\ "type " = 'GroupMember'
AND "members\".\ "source_type " = 'Namespace'
AND "members\".\ "source_id " IN ( WITH "base_ancestors_cte " AS MATERIALIZED (
SELECT
"namespaces\".\ "traversal_ids "
FROM "namespaces "
INNER JOIN "project_group_links " ON "namespaces\".\ "id " = "project_group_links\".\ "group_id "
WHERE
"namespaces\".\ "type " = 'Group'
AND "project_group_links\".\ "project_id " = 3)
SELECT
"id "
FROM "namespaces "
INNER JOIN ( SELECT DISTINCT
unnest("base_ancestors_cte\".\ "traversal_ids ")
FROM base_ancestors_cte) AS ancestors (ancestor_id) ON namespaces.id = ancestors.ancestor_id
WHERE
"namespaces\".\ "type " = 'Group'
AND ("namespaces\".\ "visibility_level " IN (10, 20)
OR EXISTS (
SELECT
1
FROM (
SELECT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_encrypted ", "namespaces\".\ "custom_project_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_notification_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM (( WITH "direct_groups " AS MATERIALIZED (
SELECT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_encrypted ", "namespaces\".\ "custom_project_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_noti fication_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM "namespaces "
INNER JOIN "members " ON "namespaces\".\ "id " = "members\".\ "source_id "
WHERE
"members\".\ "type " = 'GroupMember'
AND "members\".\ "source_type " = 'Namespace'
AND "namespaces\".\ "type " = 'Group'
AND "members\".\ "user_id " = 9
AND "members\".\ "requested_at " IS NULL
AND (
access_level >= 10
))
SELECT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_encrypted ", "namespaces\".\ "custom_project_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_notification_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM (( WITH "descendants_base_cte " AS MATERIALIZED (
SELECT
"id ", "traversal_ids "
FROM "direct_groups " "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
), "superset " AS (
SELECT
d1.traversal_ids nFROM descendants_base_cte d1 nWHERE NOT EXISTS (n
SELECT
1 n
FROM descendants_base_cte d2 n
WHERE
d2.id = ANY (d1.traversal_ids) n
AND d2.id <> d1.id n) n)
SELECT DISTINCT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_encrypted ", "namespaces\".\ "custom_project_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_notification_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM "superset ", "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
AND next_traversal_ids_sibling ("superset\".\ "traversal_ids ") > "namespaces\".\ "traversal_ids "
AND "superset\".\ "traversal_ids " <= "namespaces\".\ "traversal_ids ") nUNION n ( WITH "base_ancestors_cte " AS MATERIALIZED (
SELECT
"namespaces\".\ "traversal_ids "
FROM "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
AND "namespaces\".\ "id " IN (
SELECT
"projects\".\ "namespace_id "
FROM "projects "
INNER JOIN "project_authorizations " ON "projects\".\ "id " = "project_authorizations\".\ "project_id "
WHERE
"project_authorizations\".\ "user_id " = 9
))
SELECT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_encrypted ", "namespaces\".\ "custom_project_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_notification_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM "namespaces "
INNER JOIN ( SELECT DISTINCT
unnest("base_ancestors_cte\".\ "traversal_ids ")
FROM base_ancestors_cte) AS ancestors (ancestor_id) ON namespaces.id = ancestors.ancestor_id
WHERE
"namespaces\".\ "type " = 'Group') nUNION n ( WITH "descendants_base_cte " AS MATERIALIZED (
SELECT
"namespaces\".\ "id ", "namespaces\".\ "traversal_ids "
FROM "namespaces "
INNER JOIN "group_group_links " ON "group_group_links\".\ "shared_group_id " = "namespaces\".\ "id "
WHERE
"namespaces\".\ "type " = 'Group'
AND "group_group_links\".\ "shared_with_group_id " IN (
SELECT
"namespaces\".\ "id "
FROM "direct_groups " "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
)
), "superset " AS (
SELECT
d1.traversal_ids nFROM descendants_base_cte d1 nWHERE NOT EXISTS (n
SELECT
1 n
FROM descendants_base_cte d2 n
WHERE
d2.id = ANY (d1.traversal_ids) n
AND d2.id <> d1.id n) n)
SELECT DISTINCT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_encrypted ", "namespaces\".\ "custom_pr oject_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_notification_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM "superset ", "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
AND next_traversal_ids_sibling ("superset\".\ "traversal_ids ") > "namespaces\".\ "traversal_ids "
AND "superset\".\ "traversal_ids " <= "namespaces\".\ "traversal_ids ")) namespaces
WHERE
"namespaces\".\ "type " = 'Group')) namespaces
WHERE
"namespaces\".\ "type " = 'Group') authorized
WHERE
authorized. "id " = "namespaces\".\ "id ")))
AND (members.access_level > 5))) AS member_union nLEFT
JOIN project_authorizations ON project_authorizations.user_id = member_union.user_id n
AND n project_authorizations.project_id = 3 nORDER BY user_id, n invite_email, n CASE n
WHEN type = 'ProjectMember' THEN
1 n
WHEN type = 'GroupMember' THEN
2 n
ELSE
3 n
END n) AS members
After SQL
SELECT
"members\".\ "id\", \"members\".\"access_level\", \"members\".\"source_id\", \"members\".\"source_type\", \"members\".\"user_id\", \"members\".\"notification_level\", \"members\".\"type\", \"members\".\"created_at\", \"members\".\"updated_at\", \"members\".\"created_by_id\", \"members\".\"invite_email\", \"members\".\"invite_token\", \"members\".\"invite_accepted_at\", \"members\".\"requested_at\", \"members\".\"expires_at\", \"members\".\"ldap\", \"members\".\"override\", \"members\".\"state\", \"members\".\"invite_email_success\", \"members\".\"member_namespace_id\", \"members\".\"member_role_id\", \"members\".\"expiry_notified_at\", \"members\".\"request_accepted_at\" FROM (SELECT DISTINCT ON (user_id, invite_email) member_union.id, COALESCE(project_authorizations.access_level, member_union.access_level) AS access_level, member_union.source_id, member_union.source_type, member_union.user_id, member_union.notification_level, member_union.type, member_union.created_at, member_union.updated_at, member_union.created_by_id, member_union.invite_email, member_union.invite_token, member_union.invite_accepted_at, member_union.requested_at, member_union.expires_at, member_union.ldap, member_union.override, member_union.state, member_union.invite_email_success, member_union.member_namespace_id, CAST(NULL AS integer) AS member_role_id, member_union.expiry_notified_at, member_union.request_accepted_at\nFROM ((SELECT \"members\".\"id\", \"members\".\"access_level\", \"members\".\"source_id\", \"members\".\"source_type\", \"members\".\"user_id\", \"members\".\"notification_level\", \"members\".\"type\", \"members\".\"created_at\", \"members\".\"updated_at\", \"members\".\"created_by_id\", \"members\".\"invite_email\", \"members\".\"invite_token\", \"members\".\"invite_accepted_at\", \"members\".\"requested_at\", \"members\".\"expires_at\", \"members\".\"ldap\", \"members\".\"override\", \"members\".\"state\", \"members\".\"invite_email_success\", \"members\".\"member_namespace_id\", \"members\".\"member_role_id\", \"members\".\"expiry_notified_at\", \"members\".\"request_accepted_at\" FROM (SELECT DISTINCT ON (user_id, invite_email) * FROM \"members\" WHERE \"members\".\"type\" = 'GroupMember' AND \"members\".\"source_type\" = 'Namespace' AND \"members\".\"requested_at\" IS NULL AND \"members\".\"source_id\" IN (SELECT \"namespaces\".\"id\" FROM (SELECT \"namespaces\".\"id\", \"namespaces\".\"name\", \"namespaces\".\"path\", \"namespaces\".\"owner_id\", \"namespaces\".\"created_at\", \"namespaces\".\"updated_at\", \"namespaces\".\"type\", \"namespaces\".\"avatar\", \"namespaces\".\"membership_lock\", \"namespaces\".\"share_with_group_lock\", \"namespaces\".\"visibility_level\", \"namespaces\".\"request_access_enabled\", \"namespaces\".\"ldap_sync_status\", \"namespaces\".\"ldap_sync_error\", \"namespaces\".\"ldap_sync_last_update_at\", \"namespaces\".\"ldap_sync_last_successful_update_at\", \"namespaces\".\"ldap_sync_last_sync_at\", \"namespaces\".\"lfs_enabled\", \"namespaces\".\"parent_id\", \"namespaces\".\"shared_runners_minutes_limit\", \"namespaces\".\"repository_size_limit\", \"namespaces\".\"require_two_factor_authentication\", \"namespaces\".\"two_factor_grace_period\", \"namespaces\".\"project_creation_level\", \"namespaces\".\"runners_token\", \"namespaces\".\"file_template_project_id\", \"namespaces\".\"saml_discovery_token\", \"namespaces\".\"runners_token_encrypted\", \"namespaces\".\"custom_project_templates_group_id\", \"namespaces\".\"auto_devops_enabled\", \"namespaces\".\"extra_shared_runners_minutes_limit\", \"namespaces\".\"last_ci_minutes_notification_at\", \"namespaces\".\"last_ci_minutes_usage_notification_level\", \"namespaces\".\"subgroup_creation_level\", \"namespaces\".\"max_pages_size\", \"namespaces\".\"max_artifacts_size\", \"namespaces\".\"mentions_disabled\", \"namespaces\".\"default_branch_protection\", \"namespaces\".\"max_personal_access_token_lifetime\", \"namespaces\".\"push_rule_id\", \"namespaces\".\"shared_runners_enabled\", \"namespaces\".\"allow_descendants_override_disabled_shared_runners\", \"namespaces\".\"traversal_ids\", \"namespaces\".\"organization_id\", \"namespaces\".\"state\" FROM \"namespaces\" WHERE \"namespaces\".\"type\" = 'Group' AND \"namespaces\".\"id\" = 5) namespaces WHERE \"namespaces\".\"type\" = 'Group') AND (members.access_level > 5) ORDER BY user_id, invite_email,\nCASE WHEN source_id = 5 and source_type = 'Namespace'\nTHEN access_level + 1 ELSE access_level END DESC,\nmember_role_id ASC, expires_at DESC, created_at ASC\n) members WHERE \"members\".\"type\" = 'GroupMember' AND \"members\
"."invite_token " IS NULL
AND (members.access_level > 5)) nUNION ALL n (
SELECT
"members\".\ "id ",
"members\".\ "access_level ",
"members\".\ "source_id ",
"members\".\ "source_type ",
"members\".\ "user_id ",
"members\".\ "notification_level ",
"members\".\ "type ",
"members\".\ "created_at ",
"members\".\ "updated_at ",
"members\".\ "created_by_id ",
"members\".\ "invite_email ",
"members\".\ "invite_token ",
"members\".\ "invite_accepted_at ",
"members\".\ "requested_at ",
"members\".\ "expires_at ",
"members\".\ "ldap ",
"members\".\ "override ",
"members\".\ "state ",
"members\".\ "invite_email_success ",
"members\".\ "member_namespace_id ",
"members\".\ "member_role_id ",
"members\".\ "expiry_notified_at ",
"members\".\ "request_accepted_at "
FROM
"members "
WHERE
"members\".\ "type " = 'GroupMember'
AND "members\".\ "source_type " = 'Namespace'
AND "members\".\ "source_id " IN ( WITH "base_ancestors_cte " AS MATERIALIZED (
SELECT
"namespaces\".\ "traversal_ids "
FROM
"namespaces "
INNER JOIN "project_group_links " ON "namespaces\".\ "id " = "project_group_links\".\ "group_id "
WHERE
"namespaces\".\ "type " = 'Group'
AND "project_group_links\".\ "project_id " = 2
)
SELECT
"id "
FROM
"namespaces "
INNER JOIN ( SELECT DISTINCT
unnest("base_ancestors_cte\".\ "traversal_ids ")
FROM base_ancestors_cte) AS ancestors (ancestor_id) ON namespaces.id = ancestors.ancestor_id
WHERE
"namespaces\".\ "type " = 'Group'
AND ("namespaces\".\ "visibility_level " IN (10, 20)
OR EXISTS (
SELECT
1
FROM (
SELECT
"namespaces\".\ "id ",
"namespaces\".\ "name ",
"namespaces\".\ "path ",
"namespaces\".\ "owner_id ",
"namespaces\".\ "created_at ",
"namespaces\".\ "updated_at ",
"namespaces\".\ "type ",
"namespaces\".\ "avatar ",
"namespaces\".\ "membership_lock ",
"namespaces\".\ "share_with_group_lock ",
"namespaces\".\ "visibility_level ",
"namespaces\".\ "request_access_enabled ",
"namespaces\".\ "ldap_sync_status ",
"namespaces\".\ "ldap_sync_error ",
"namespaces\".\ "ldap_sync_last_update_at ",
"namespaces\".\ "ldap_sync_last_successful_update_at ",
"namespaces\".\ "ldap_sync_last_sync_at ",
"namespaces\".\ "lfs_enabled ",
"namespaces\".\ "parent_id ",
"namespaces\".\ "shared_runners_minutes_limit ",
"namespaces\".\ "repository_size_limit ",
"namespaces\".\ "require_two_factor_authentication ",
"namespaces\".\ "two_factor_grace_period ",
"namespaces\".\ "project_creation_level ",
"namespaces\".\ "runners_token ",
"namespaces\".\ "file_template_project_id ",
"namespaces\".\ "saml_discovery_token ",
"namespaces\".\ "runners_token_encrypted ",
"namespaces\".\ "custom_project_templates_group_id ",
"namespaces\".\ "auto_devops_enabled ",
"namespaces\".\ "extra_shared_runners_minutes_limit ",
"namespaces\".\ "last_ci_minutes_notification_at ",
"namespaces\".\ "last_ci_minutes_usage_notification_level ",
"namespaces\".\ "subgroup_creation_level ",
"namespaces\".\ "max_pages_size ",
"namespaces\".\ "max_artifacts_size ",
"namespaces\".\ "mentions_disabled ",
"namespaces\".\ "default_branch_protection ",
"namespaces\".\ "max_personal_access_token_lifetime ",
"namespaces\".\ "push_rule_id ",
"namespaces\".\ "shared_runners_enabled ",
"namespaces\".\ "allow_descendants_override_disabled_shared_runners ",
"namespaces\".\ "traversal_ids ",
"namespaces\".\ "organization_id ",
"namespaces\".\ "state "
FROM (( WITH "direct_groups " AS MATERIALIZED (
SELECT
"namespaces\".\ "id ",
"namespaces\".\ "name ",
"namespaces\".\ "path ",
"namespaces\".\ "owner_id ",
"namespaces\".\ "created_at ",
"namespaces\".\ "updated_at ",
"namespaces\".\ "type ",
"namespaces\".\ "avatar ",
"namespaces\".\ "membership_lock ",
"namespaces\".\ "share_with_group_lock ",
"namespaces\".\ "visibility_level ",
"namespaces\".\ "request_access_enabled ",
"namespaces\".\ "ldap_sync_status ",
"namespaces\".\ "ldap_sync_error ",
"namespaces\".\ "ldap_sync_last_update_at ",
"namespaces\".\ "ldap_sync_last_successful_update_at ",
"namespaces\".\ "ldap_sync_last_sync_at ",
"namespaces\".\ "lfs_enabled ",
"namespaces\".\ "parent_id ",
"namespaces\".\ "shared_runners_minutes_limit ",
"namespaces\".\ "repository_size_limit ",
"namespaces\".\ "require_two_factor_authentication ",
"namespaces\".\ "two_factor_grace_period ",
"namespaces\".\ "project_creation_level ",
"namespaces\".\ "runners_token ",
"namespaces\".\ "file_template_project_id ",
"namespaces\".\ "saml_discovery_token ",
"namespaces\".\ "runners_token_encrypted ",
"namespaces\".\ "custom_project_templates_group_id ",
"namespaces\".\ "auto_devops_enabled ",
"namespaces\".\ "extra_shared_runners_minutes_limit ",
"namespaces\".\ "last_ci_minutes_notification_at ",
"name spaces ". "last_ci_minutes_usage_notification_level ",
"namespaces\".\ "subgroup_creation_level ",
"namespaces\".\ "max_pages_size ",
"namespaces\".\ "max_artifacts_size ",
"namespaces\".\ "mentions_disabled ",
"namespaces\".\ "default_branch_protection ",
"namespaces\".\ "max_personal_access_token_lifetime ",
"namespaces\".\ "push_rule_id ",
"namespaces\".\ "shared_runners_enabled ",
"namespaces\".\ "allow_descendants_override_disabled_shared_runners ",
"namespaces\".\ "traversal_ids ",
"namespaces\".\ "organization_id ",
"namespaces\".\ "state "
FROM
"namespaces "
INNER JOIN "members " ON "namespaces\".\ "id " = "members\".\ "source_id "
WHERE
"members\".\ "type " = 'GroupMember'
AND "members\".\ "source_type " = 'Namespace'
AND "namespaces\".\ "type " = 'Group'
AND "members\".\ "user_id " = 6
AND "members\".\ "requested_at " IS NULL
AND (
access_level >= 10
))
SELECT
"namespaces\".\ "id ",
"namespaces\".\ "name ",
"namespaces\".\ "path ",
"namespaces\".\ "owner_id ",
"namespaces\".\ "created_at ",
"namespaces\".\ "updated_at ",
"namespaces\".\ "type ",
"namespaces\".\ "avatar ",
"namespaces\".\ "membership_lock ",
"namespaces\".\ "share_with_group_lock ",
"namespaces\".\ "visibility_level ",
"namespaces\".\ "request_access_enabled ",
"namespaces\".\ "ldap_sync_status ",
"namespaces\".\ "ldap_sync_error ",
"namespaces\".\ "ldap_sync_last_update_at ",
"namespaces\".\ "ldap_sync_last_successful_update_at ",
"namespaces\".\ "ldap_sync_last_sync_at ",
"namespaces\".\ "lfs_enabled ",
"namespaces\".\ "parent_id ",
"namespaces\".\ "shared_runners_minutes_limit ",
"namespaces\".\ "repository_size_limit ",
"namespaces\".\ "require_two_factor_authentication ",
"namespaces\".\ "two_factor_grace_period ",
"namespaces\".\ "project_creation_level ",
"namespaces\".\ "runners_token ",
"namespaces\".\ "file_template_project_id ",
"namespaces\".\ "saml_discovery_token ",
"namespaces\".\ "runners_token_encrypted ",
"namespaces\".\ "custom_project_templates_group_id ",
"namespaces\".\ "auto_devops_enabled ",
"namespaces\".\ "extra_shared_runners_minutes_limit ",
"namespaces\".\ "last_ci_minutes_notification_at ",
"namespaces\".\ "last_ci_minutes_usage_notification_level ",
"namespaces\".\ "subgroup_creation_level ",
"namespaces\".\ "max_pages_size ",
"namespaces\".\ "max_artifacts_size ",
"namespaces\".\ "mentions_disabled ",
"namespaces\".\ "default_branch_protection ",
"namespaces\".\ "max_personal_access_token_lifetime ",
"namespaces\".\ "push_rule_id ",
"namespaces\".\ "shared_runners_enabled ",
"namespaces\".\ "allow_descendants_override_disabled_shared_runners ",
"namespaces\".\ "traversal_ids ",
"namespaces\".\ "organization_id ",
"namespaces\".\ "state "
FROM (( WITH "descendants_base_cte " AS MATERIALIZED (
SELECT
"id ",
"traversal_ids "
FROM
"direct_groups " "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
),
"superset " AS (
SELECT
d1.traversal_ids nFROM descendants_base_cte d1 nWHERE NOT EXISTS (n
SELECT
1 n
FROM
descendants_base_cte d2 n
WHERE
d2.id = ANY (d1.traversal_ids) n
AND d2.id <> d1.id n) n
)
SELECT DISTINCT
"namespaces\".\ "id ",
"namespaces\".\ "name ",
"namespaces\".\ "path ",
"namespaces\".\ "owner_id ",
"namespaces\".\ "created_at ",
"namespaces\".\ "updated_at ",
"namespaces\".\ "type ",
"namespaces\".\ "avatar ",
"namespaces\".\ "membership_lock ",
"namespaces\".\ "share_with_group_lock ",
"namespaces\".\ "visibility_level ",
"namespaces\".\ "request_access_enabled ",
"namespaces\".\ "ldap_sync_status ",
"namespaces\".\ "ldap_sync_error ",
"namespaces\".\ "ldap_sync_last_update_at ",
"namespaces\".\ "ldap_sync_last_successful_update_at ",
"namespaces\".\ "ldap_sync_last_sync_at ",
"namespaces\".\ "lfs_enabled ",
"namespaces\".\ "parent_id ",
"namespaces\".\ "shared_runners_minutes_limit ",
"namespaces\".\ "repository_size_limit ",
"namespaces\".\ "require_two_factor_authentication ",
"namespaces\".\ "two_factor_grace_period ",
"namespaces\".\ "project_creation_level ",
"namespaces\".\ "runners_token ",
"namespaces\".\ "file_template_project_id ",
"namespaces\".\ "saml_discovery_token ",
"namespaces\".\ "runners_token_encrypted ",
"namespaces\".\ "custom_project_templates_group_id ",
"namespaces\".\ "auto_devops_enabled ",
"namespaces\".\ "extra_shared_runners_minutes_limit ",
"namespaces\".\ "last_ci_minutes_notification_at ",
"namespaces\".\ "last_ci_minutes_usage_notification_level ",
"namespaces\".\ "subgroup_creation_level ",
"namespaces\".\ "max_pages_size ",
"namespaces\".\ "max_artifacts_size ",
"namespaces\".\ "mentions_disabled ",
"namespaces\".\ "default_branch_protection ",
"namespaces\".\ "max_personal_access_token_life time ",
"namespaces\".\ "push_rule_id ",
"namespaces\".\ "shared_runners_enabled ",
"namespaces\".\ "allow_descendants_override_disabled_shared_runners ",
"namespaces\".\ "traversal_ids ",
"namespaces\".\ "organization_id ",
"namespaces\".\ "state "
FROM
"superset ",
"namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
AND next_traversal_ids_sibling ("superset\".\ "traversal_ids ") > "namespaces\".\ "traversal_ids "
AND "superset\".\ "traversal_ids " <= "namespaces\".\ "traversal_ids ") nUNION n ( WITH "base_ancestors_cte " AS MATERIALIZED (
SELECT
"namespaces\".\ "traversal_ids "
FROM "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
AND "namespaces\".\ "id " IN (
SELECT
"projects\".\ "namespace_id "
FROM "projects "
INNER JOIN "project_authorizations " ON "projects\".\ "id " = "project_authorizations\".\ "project_id "
WHERE
"project_authorizations\".\ "user_id " = 6
))
SELECT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_encrypted ", "namespaces\".\ "custom_project_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_notification_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM "namespaces "
INNER JOIN ( SELECT DISTINCT
unnest("base_ancestors_cte\".\ "traversal_ids ")
FROM base_ancestors_cte) AS ancestors (ancestor_id) ON namespaces.id = ancestors.ancestor_id
WHERE
"namespaces\".\ "type " = 'Group') nUNION n ( WITH "descendants_base_cte " AS MATERIALIZED (
SELECT
"namespaces\".\ "id ", "namespaces\".\ "traversal_ids "
FROM "namespaces "
INNER JOIN "group_group_links " ON "group_group_links\".\ "shared_group_id " = "namespaces\".\ "id "
WHERE
"namespaces\".\ "type " = 'Group'
AND "group_group_links\".\ "shared_with_group_id " IN (
SELECT
"namespaces\".\ "id "
FROM "direct_groups " "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
)
), "superset " AS (
SELECT
d1.traversal_ids nFROM descendants_base_cte d1 nWHERE NOT EXISTS (n
SELECT
1 n
FROM descendants_base_cte d2 n
WHERE
d2.id = ANY (d1.traversal_ids) n
AND d2.id <> d1.id n) n)
SELECT DISTINCT
"namespaces\".\ "id ", "namespaces\".\ "name ", "namespaces\".\ "path ", "namespaces\".\ "owner_id ", "namespaces\".\ "created_at ", "namespaces\".\ "updated_at ", "namespaces\".\ "type ", "namespaces\".\ "avatar ", "namespaces\".\ "membership_lock ", "namespaces\".\ "share_with_group_lock ", "namespaces\".\ "visibility_level ", "namespaces\".\ "request_access_enabled ", "namespaces\".\ "ldap_sync_status ", "namespaces\".\ "ldap_sync_error ", "namespaces\".\ "ldap_sync_last_update_at ", "namespaces\".\ "ldap_sync_last_successful_update_at ", "namespaces\".\ "ldap_sync_last_sync_at ", "namespaces\".\ "lfs_enabled ", "namespaces\".\ "parent_id ", "namespaces\".\ "shared_runners_minutes_limit ", "namespaces\".\ "repository_size_limit ", "namespaces\".\ "require_two_factor_authentication ", "namespaces\".\ "two_factor_grace_period ", "namespaces\".\ "project_creation_level ", "namespaces\".\ "runners_token ", "namespaces\".\ "file_template_project_id ", "namespaces\".\ "saml_discovery_token ", "namespaces\".\ "runners_token_en crypted ", "namespaces\".\ "custom_project_templates_group_id ", "namespaces\".\ "auto_devops_enabled ", "namespaces\".\ "extra_shared_runners_minutes_limit ", "namespaces\".\ "last_ci_minutes_notification_at ", "namespaces\".\ "last_ci_minutes_usage_notification_level ", "namespaces\".\ "subgroup_creation_level ", "namespaces\".\ "max_pages_size ", "namespaces\".\ "max_artifacts_size ", "namespaces\".\ "mentions_disabled ", "namespaces\".\ "default_branch_protection ", "namespaces\".\ "max_personal_access_token_lifetime ", "namespaces\".\ "push_rule_id ", "namespaces\".\ "shared_runners_enabled ", "namespaces\".\ "allow_descendants_override_disabled_shared_runners ", "namespaces\".\ "traversal_ids ", "namespaces\".\ "organization_id ", "namespaces\".\ "state "
FROM "superset ", "namespaces "
WHERE
"namespaces\".\ "type " = 'Group'
AND next_traversal_ids_sibling ("superset\".\ "traversal_ids ") > "namespaces\".\ "traversal_ids "
AND "superset\".\ "traversal_ids " <= "namespaces\".\ "traversal_ids ")) namespaces
WHERE
"namespaces\".\ "type " = 'Group')) namespaces
WHERE
"namespaces\".\ "type " = 'Group') authorized
WHERE
authorized. "id " = "namespaces\".\ "id ")))
AND (members.access_level > 5))) AS member_union nLEFT
JOIN project_authorizations ON project_authorizations.user_id = member_union.user_id n
AND n project_authorizations.project_id = 2 nORDER BY
user_id,
n invite_email,
n CASE n
WHEN type = 'ProjectMember' THEN
1 n
WHEN type = 'GroupMember' THEN
2 n
ELSE
3 n
END n) AS members
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Edited by Jay