16007: Use table instead of materialized view for permissions
[arvados.git] / services / api / db / migrate / 20200501150153_permission_table.rb
1 class PermissionTable < ActiveRecord::Migration[5.0]
2   def up
3     create_table :materialized_permissions, :id => false do |t|
4       t.string :user_uuid
5       t.string :target_uuid
6       t.integer :perm_level
7       t.string :target_owner_uuid
8       t.integer :trashed
9     end
10
11     ActiveRecord::Base.connection.execute %{
12 create or replace function compute_permission_table ()
13 returns table(user_uuid character varying (27),
14               target_uuid character varying (27),
15               perm_level smallint,
16               target_owner_uuid character varying(27),
17               trashed smallint)
18 VOLATILE
19 language SQL
20 as $$
21  WITH RECURSIVE perm_value(name, val) AS (
22          VALUES ('can_read'::text,(1)::smallint), ('can_login'::text,1), ('can_write'::text,2), ('can_manage'::text,3)
23         ), perm_edges(tail_uuid, head_uuid, val, follow, trashed) AS (
24          SELECT links.tail_uuid,
25             links.head_uuid,
26             pv.val,
27             ((pv.val = 3) OR (groups.uuid IS NOT NULL)) AS follow,
28             (0)::smallint AS trashed,
29             (0)::smallint AS followtrash
30            FROM ((public.links
31              LEFT JOIN perm_value pv ON ((pv.name = (links.name)::text)))
32              LEFT JOIN public.groups ON (((pv.val < 3) AND ((groups.uuid)::text = (links.head_uuid)::text))))
33           WHERE ((links.link_class)::text = 'permission'::text)
34         UNION ALL
35          SELECT groups.owner_uuid,
36             groups.uuid,
37             3,
38             true AS bool,
39                 CASE
40                     WHEN ((groups.trash_at IS NOT NULL) AND (groups.trash_at < clock_timestamp())) THEN 1
41                     ELSE 0
42                 END AS "case",
43             1
44            FROM public.groups
45         ), perm(val, follow, user_uuid, target_uuid, trashed) AS (
46          SELECT (3)::smallint AS val,
47             true AS follow,
48             (users.uuid)::character varying(32) AS user_uuid,
49             (users.uuid)::character varying(32) AS target_uuid,
50             (0)::smallint AS trashed
51            FROM public.users
52         UNION
53          SELECT (LEAST((perm_1.val)::integer, edges.val))::smallint AS val,
54             edges.follow,
55             perm_1.user_uuid,
56             (edges.head_uuid)::character varying(32) AS target_uuid,
57             ((GREATEST((perm_1.trashed)::integer, edges.trashed) * edges.followtrash))::smallint AS trashed
58            FROM (perm perm_1
59              JOIN perm_edges edges ON ((perm_1.follow AND ((edges.tail_uuid)::text = (perm_1.target_uuid)::text))))
60         )
61  SELECT perm.user_uuid,
62     perm.target_uuid,
63     max(perm.val) AS perm_level,
64     CASE perm.follow
65        WHEN true THEN perm.target_uuid
66        ELSE NULL::character varying
67     END AS target_owner_uuid,
68     max(perm.trashed) AS trashed
69    FROM perm
70   GROUP BY perm.user_uuid, perm.target_uuid,
71         CASE perm.follow
72             WHEN true THEN perm.target_uuid
73             ELSE NULL::character varying
74         END
75 $$;
76 }
77
78     ActiveRecord::Base.connection.execute "DROP MATERIALIZED VIEW IF EXISTS materialized_permission_view;"
79
80   end
81   def down
82     drop_table :materialized_permissions
83   end
84 end