summaryrefslogtreecommitdiff
path: root/app/finders/members_finder.rb
diff options
context:
space:
mode:
authorAndreas Brandl <abrandl@gitlab.com>2018-02-19 18:28:11 +0100
committerAndreas Brandl <abrandl@gitlab.com>2018-02-20 18:07:39 +0100
commitc2ae4a6399903c30686aa8e8e6ed52fcd8d8214c (patch)
treeb41dab2f75a9d9d684917cd586ba877eee5fa551 /app/finders/members_finder.rb
parentdf7d65a7aa213834b25f9480d6debc22c6315630 (diff)
downloadgitlab-ce-c2ae4a6399903c30686aa8e8e6ed52fcd8d8214c.tar.gz
Use DISTINCT ON and translate to MySQL.
Realized window functions are not available in older MySQL versions either. Falling back to DISTINCT ON for postgresql and a convoluted translation for MySQL.
Diffstat (limited to 'app/finders/members_finder.rb')
-rw-r--r--app/finders/members_finder.rb50
1 files changed, 34 insertions, 16 deletions
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
index fc6ca463a90..271d846b269 100644
--- a/app/finders/members_finder.rb
+++ b/app/finders/members_finder.rb
@@ -17,22 +17,7 @@ class MembersFinder
union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false)
- # We're interested in a list of members without duplicates by user_id.
- # We prefer project members over group members, project members should go first.
- #
- # We could have used a DISTINCT ON here, but MySQL does not support this.
- sql = <<-SQL
- SELECT member_numbered.*
- FROM (
- SELECT
- member_union.*,
- ROW_NUMBER() OVER (
- PARTITION BY user_id ORDER BY CASE WHEN type = 'ProjectMember' THEN 1 WHEN type = 'GroupMember' THEN 2 ELSE 3 END
- ) AS row_number
- FROM (#{union.to_sql}) AS member_union
- ) AS member_numbered
- WHERE row_number = 1
- SQL
+ sql = distinct_on(union)
Member.from("(#{sql}) AS #{Member.table_name}")
else
@@ -43,4 +28,37 @@ class MembersFinder
def can?(*args)
Ability.allowed?(*args)
end
+
+ private
+
+ def distinct_on(union)
+ # We're interested in a list of members without duplicates by user_id.
+ # We prefer project members over group members, project members should go first.
+ if Gitlab::Database.postgresql?
+ <<~SQL
+ SELECT DISTINCT ON (user_id, invite_email) member_union.*
+ FROM (#{union.to_sql}) AS member_union
+ ORDER BY
+ user_id, invite_email,
+ CASE WHEN type = 'ProjectMember' THEN 1 WHEN type = 'GroupMember' THEN 2 ELSE 3 END
+ SQL
+ else
+ # Older versions of MySQL do not support window functions (and DISTINCT ON is postgres-specific).
+ <<~SQL
+ SELECT t1.*
+ FROM (#{union.to_sql}) AS t1
+ JOIN (
+ SELECT
+ COALESCE(user_id, -1) AS user_id,
+ COALESCE(invite_email, 'NULL') AS invite_email,
+ MIN(CASE WHEN type = 'ProjectMember' THEN 1 WHEN type = 'GroupMember' THEN 2 ELSE 3 END) AS type_number
+ FROM
+ (#{union.to_sql}) AS t3
+ GROUP BY COALESCE(user_id, -1), COALESCE(invite_email, 'NULL')
+ ) AS t2 ON COALESCE(t1.user_id, -1) = t2.user_id
+ AND COALESCE(t1.invite_email, 'NULL') = t2.invite_email
+ AND CASE WHEN t1.type = 'ProjectMember' THEN 1 WHEN t1.type = 'GroupMember' THEN 2 ELSE 3 END = t2.type_number
+ SQL
+ end
+ end
end