]> git.openstreetmap.org Git - rails.git/blob - lib/database_functions.rb
Merge remote-tracking branch 'upstream/pull/4968'
[rails.git] / lib / database_functions.rb
1 module DatabaseFunctions
2   API_RATE_LIMIT = %(
3     CREATE OR REPLACE FUNCTION api_rate_limit(user_id int8)
4       RETURNS int4
5       AS $$
6     DECLARE
7       min_changes_per_hour int4 := #{Settings.min_changes_per_hour};
8       initial_changes_per_hour int4 := #{Settings.initial_changes_per_hour};
9       max_changes_per_hour int4 := #{Settings.max_changes_per_hour};
10       days_to_max_changes int4 := #{Settings.days_to_max_changes};
11       importer_changes_per_hour int4 := #{Settings.importer_changes_per_hour};
12       moderator_changes_per_hour int4 := #{Settings.moderator_changes_per_hour};
13       roles text[];
14       last_block timestamp without time zone;
15       first_change timestamp without time zone;
16       active_reports int4;
17       time_since_first_change double precision;
18       max_changes double precision;
19       recent_changes int4;
20     BEGIN
21       SELECT ARRAY_AGG(user_roles.role) INTO STRICT roles FROM user_roles WHERE user_roles.user_id = api_rate_limit.user_id;
22
23       IF 'moderator' = ANY(roles) THEN
24         max_changes := moderator_changes_per_hour;
25       ELSIF 'importer' = ANY(roles) THEN
26         max_changes := importer_changes_per_hour;
27       ELSE
28         SELECT user_blocks.created_at INTO last_block FROM user_blocks WHERE user_blocks.user_id = api_rate_limit.user_id ORDER BY user_blocks.created_at DESC LIMIT 1;
29
30         IF FOUND THEN
31           SELECT changesets.created_at INTO first_change FROM changesets WHERE changesets.user_id = api_rate_limit.user_id AND changesets.created_at > last_block ORDER BY changesets.created_at LIMIT 1;
32         ELSE
33           SELECT changesets.created_at INTO first_change FROM changesets WHERE changesets.user_id = api_rate_limit.user_id ORDER BY changesets.created_at LIMIT 1;
34         END IF;
35
36         IF NOT FOUND THEN
37           first_change := CURRENT_TIMESTAMP AT TIME ZONE 'UTC';
38         END IF;
39
40         SELECT COUNT(*) INTO STRICT active_reports
41         FROM issues INNER JOIN reports ON reports.issue_id = issues.id
42         WHERE issues.reported_user_id = api_rate_limit.user_id AND issues.status = 'open' AND reports.updated_at >= COALESCE(issues.resolved_at, '1970-01-01');
43
44         time_since_first_change := EXTRACT(EPOCH FROM CURRENT_TIMESTAMP AT TIME ZONE 'UTC' - first_change);
45
46         max_changes := max_changes_per_hour * POWER(time_since_first_change, 2) / POWER(days_to_max_changes * 24 * 60 * 60, 2);
47         max_changes := GREATEST(initial_changes_per_hour, LEAST(max_changes_per_hour, FLOOR(max_changes)));
48         max_changes := max_changes / POWER(2, active_reports);
49         max_changes := GREATEST(min_changes_per_hour, LEAST(max_changes_per_hour, max_changes));
50       END IF;
51
52       SELECT COALESCE(SUM(changesets.num_changes), 0) INTO STRICT recent_changes FROM changesets WHERE changesets.user_id = api_rate_limit.user_id AND changesets.created_at >= CURRENT_TIMESTAMP AT TIME ZONE 'UTC' - '1 hour'::interval;
53
54       RETURN max_changes - recent_changes;
55     END;
56     $$ LANGUAGE plpgsql STABLE;
57   ).freeze
58
59   API_SIZE_LIMIT = %(
60     CREATE OR REPLACE FUNCTION api_size_limit(user_id int8)
61       RETURNS int8
62       AS $$
63     DECLARE
64       min_size_limit int8 := #{Settings.min_size_limit};
65       initial_size_limit int8 := #{Settings.initial_size_limit};
66       max_size_limit int8 := #{Settings.max_size_limit};
67       days_to_max_size_limit int4 := #{Settings.days_to_max_size_limit};
68       importer_size_limit int8 := #{Settings.importer_size_limit};
69       moderator_size_limit int8 := #{Settings.moderator_size_limit};
70       roles text[];
71       last_block timestamp without time zone;
72       first_change timestamp without time zone;
73       active_reports int4;
74       time_since_first_change double precision;
75       size_limit int8;
76     BEGIN
77       SELECT ARRAY_AGG(user_roles.role) INTO STRICT roles FROM user_roles WHERE user_roles.user_id = api_size_limit.user_id;
78
79       IF 'moderator' = ANY(roles) THEN
80         size_limit := moderator_size_limit;
81       ELSIF 'importer' = ANY(roles) THEN
82         size_limit := importer_size_limit;
83       ELSE
84         SELECT user_blocks.created_at INTO last_block FROM user_blocks WHERE user_blocks.user_id = api_size_limit.user_id ORDER BY user_blocks.created_at DESC LIMIT 1;
85
86         IF FOUND THEN
87           SELECT changesets.created_at INTO first_change FROM changesets WHERE changesets.user_id = api_size_limit.user_id AND changesets.created_at > last_block ORDER BY changesets.created_at LIMIT 1;
88         ELSE
89           SELECT changesets.created_at INTO first_change FROM changesets WHERE changesets.user_id = api_size_limit.user_id ORDER BY changesets.created_at LIMIT 1;
90         END IF;
91
92         IF NOT FOUND THEN
93           first_change := CURRENT_TIMESTAMP AT TIME ZONE 'UTC';
94         END IF;
95
96         SELECT COUNT(*) INTO STRICT active_reports
97         FROM issues INNER JOIN reports ON reports.issue_id = issues.id
98         WHERE issues.reported_user_id = api_size_limit.user_id AND issues.status = 'open' AND reports.updated_at >= COALESCE(issues.resolved_at, '1970-01-01');
99
100         time_since_first_change := EXTRACT(EPOCH FROM CURRENT_TIMESTAMP AT TIME ZONE 'UTC' - first_change);
101
102         size_limit := max_size_limit * POWER(time_since_first_change, 2) / POWER(days_to_max_size_limit * 24 * 60 * 60, 2);
103         size_limit := GREATEST(initial_size_limit, LEAST(max_size_limit, FLOOR(size_limit)));
104         size_limit := size_limit / POWER(2, active_reports);
105         size_limit := GREATEST(min_size_limit, LEAST(max_size_limit, size_limit));
106       END IF;
107
108       RETURN size_limit;
109     END;
110     $$ LANGUAGE plpgsql STABLE;
111   ).freeze
112 end