summaryrefslogtreecommitdiff
path: root/etc/audit.sql
blob: 11acbda1bd8526eebeed20fd49a3244a75b579ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
CREATE SEQUENCE activity_seq;

CREATE TABLE patch_audit (
	patch_id				integer not null,
	change_type				varchar not null,
	changed_fields			varchar[] not null,
	commitfest_id	        integer not null,
	original_name			varchar not null,
	from_commitfest_id		integer,
	to_commitfest_id		integer,
	commitfest_topic_id     integer not null,
	name					varchar not null,
	patch_status_id			integer not null,
	author   				varchar not null,
	reviewers				varchar not null,
	date_closed				date,
	last_updater			varchar,
	last_updated_time		timestamp with time zone not null
);
ALTER TABLE patch_audit ADD COLUMN activity_id INTEGER NOT NULL
	DEFAULT nextval('activity_seq');
ALTER TABLE patch_audit ADD COLUMN committer VARCHAR NOT NULL
	DEFAULT '';

CREATE OR REPLACE FUNCTION patch_audit() RETURNS trigger AS $$
DECLARE
	cf varchar[] := '{}'::varchar[];
	acid integer;
	cid integer;
	oname varchar;
BEGIN
	IF (TG_OP = 'DELETE') THEN
		NEW := OLD;
		NEW.last_updated_time := now();
	END IF;
	IF (TG_OP = 'UPDATE') THEN
		IF (OLD.last_updated_time = NEW.last_updated_time) THEN
			-- Some kind of system update, just ignore it.
			RETURN NULL;
		END IF;
		cf := CASE WHEN OLD.commitfest_topic_id != NEW.commitfest_topic_id
				   THEN '{commitfest_topic_id}'::varchar[]
				   ELSE '{}'::varchar[] END
		   || CASE WHEN OLD.name != NEW.name
				   THEN '{name}'::varchar[]
				   ELSE '{}'::varchar[] END
		   || CASE WHEN OLD.patch_status_id != NEW.patch_status_id
				   THEN '{patch_status_id}'::varchar[]
				   ELSE '{}'::varchar[] END
		   || CASE WHEN OLD.author != NEW.author
				   THEN '{author}'::varchar[]
				   ELSE '{}'::varchar[] END
		   || CASE WHEN OLD.reviewers != NEW.reviewers
				   THEN '{reviewers}'::varchar[]
				   ELSE '{}'::varchar[] END
		   || CASE WHEN OLD.committer != NEW.committer
				   THEN '{committer}'::varchar[]
				   ELSE '{}'::varchar[] END
		   || CASE WHEN OLD.date_closed IS DISTINCT FROM NEW.date_closed
				   THEN '{date_closed}'::varchar[]
				   ELSE '{}'::varchar[] END;
		SELECT INTO cid
			commitfest_id
		FROM
			commitfest_topic
		WHERE
			id = NEW.commitfest_topic_id;
		SELECT INTO acid
			commitfest_id
		FROM
			commitfest_topic
		WHERE
			id = OLD.commitfest_topic_id;
		oname := OLD.name;
	ELSE
		SELECT INTO cid, acid
			commitfest_id, commitfest_id
		FROM
			commitfest_topic
		WHERE
			id = NEW.commitfest_topic_id;
		oname := NEW.name;
	END IF;
	IF (TG_OP = 'INSERT') THEN
		cf := ARRAY['commitfest_id', 'commitfest_topic_id', 'name',
			'patch_status_id', 'author', 'reviewers',
			'committer', 'date_closed'];
	END IF;
	INSERT INTO patch_audit
		(patch_id, change_type, changed_fields, commitfest_id, original_name,
		 to_commitfest_id, commitfest_topic_id, name,
		 patch_status_id, author, reviewers, committer, date_closed,
		 last_updater, last_updated_time)
	VALUES
		(NEW.id, TG_OP, cf, acid, oname,
		 CASE WHEN cid != acid THEN cid ELSE NULL END,
		 NEW.commitfest_topic_id, NEW.name, NEW.patch_status_id, NEW.author,
		 NEW.reviewers, NEW.committer, NEW.date_closed, NEW.last_updater,
		 NEW.last_updated_time);
	-- For an update that changes the CommitFest, we enter two audit records,
	-- one under each CommitFest.
	IF (cid != acid) THEN
		INSERT INTO patch_audit
			(patch_id, change_type, changed_fields, commitfest_id,
			 original_name,
			 from_commitfest_id, commitfest_topic_id, name,
			 patch_status_id, author, reviewers, committer, date_closed,
			 last_updater, last_updated_time)
		VALUES
			(NEW.id, TG_OP, cf, cid, oname, acid,
			 NEW.commitfest_topic_id, NEW.name, NEW.patch_status_id, NEW.author,
			 NEW.reviewers, NEW.committer, NEW.date_closed, NEW.last_updater,
			 NEW.last_updated_time);
	END IF;
	RETURN NULL;
END
$$ LANGUAGE plpgsql;

CREATE TRIGGER patch_audit
	AFTER INSERT OR UPDATE OR DELETE ON patch
	FOR EACH ROW EXECUTE PROCEDURE patch_audit();

CREATE OR REPLACE FUNCTION patch_audit_details(patch_audit) RETURNS text AS $$
DECLARE
	v varchar[];
BEGIN
	IF ('name' = ANY($1.changed_fields)) THEN
		v := v || ('Name = ' || $1.name)::varchar;
	END IF;
	IF ($1.from_commitfest_id IS NOT NULL) THEN
		v := v || ('Moved From CommitFest = ' || COALESCE((SELECT name FROM
			commitfest WHERE id = $1.from_commitfest_id), '???'))::varchar;
	END IF;
	IF ($1.to_commitfest_id IS NOT NULL) THEN
		v := v || ('Moved To CommitFest = ' || COALESCE((SELECT name FROM
			commitfest WHERE id = $1.to_commitfest_id), '???'))::varchar;
	END IF;
	IF ('commitfest_topic_id' = ANY($1.changed_fields)) THEN
		v := v || ('Topic = ' || COALESCE((SELECT name FROM commitfest_topic
			WHERE id = $1.commitfest_topic_id), '???'))::varchar;
	END IF;
	IF ('patch_status_id' = ANY($1.changed_fields)) THEN
		v := v || ('Patch Status = ' || COALESCE((SELECT name FROM patch_status
			WHERE id = $1.patch_status_id), '???'))::varchar;
	END IF;
	IF ('author' = ANY($1.changed_fields)) THEN
		v := v || ('Author = ' || $1.author)::varchar;
	END IF;
	IF ('reviewers' = ANY($1.changed_fields)) THEN
		v := v || ('Reviewers = ' || CASE WHEN $1.reviewers = '' THEN 'Nobody'
			ELSE $1.reviewers END)::varchar;
	END IF;
	IF ('committer' = ANY($1.changed_fields)) THEN
		v := v || ('Committer = ' || CASE WHEN $1.committer = '' THEN 'Nobody'
			ELSE $1.committer END)::varchar;
	END IF;
	IF ('date_closed' = ANY($1.changed_fields)) THEN
		v := v || ('Date Closed = '
			|| COALESCE($1.date_closed::varchar, 'NULL'))::varchar;
	END IF;
	RETURN array_to_string(v, ', ');
END
$$ LANGUAGE plpgsql;

CREATE TABLE patch_comment_audit (
	id						integer not null,
	patch_id				integer not null,
	change_type				varchar not null,
	changed_fields			varchar[] not null,
	commitfest_id	        integer not null,
	patch_name				varchar not null,
	patch_comment_type_id	integer not null,
	message_id				varchar,
	content					varchar,
	last_updater			varchar,
	last_updated_time		timestamp with time zone not null
);
ALTER TABLE patch_comment_audit ADD COLUMN activity_id INTEGER NOT NULL
	DEFAULT nextval('activity_seq');

CREATE OR REPLACE FUNCTION patch_comment_audit() RETURNS trigger AS $$
DECLARE
	cf varchar[] := '{}'::varchar[];
	cid integer;
	pname varchar;
BEGIN
	IF (TG_OP = 'DELETE') THEN
		NEW := OLD;
		NEW.last_updated_time := now();
	END IF;
	IF (TG_OP = 'UPDATE') THEN
		IF (OLD.last_updated_time = NEW.last_updated_time) THEN
			-- Some kind of system update, just ignore it.
			RETURN NULL;
		END IF;
		cf := CASE WHEN OLD.patch_comment_type_id != NEW.patch_comment_type_id
				   THEN '{patch_comment_type_id}'::varchar[]
				   ELSE '{}'::varchar[] END
		   || CASE WHEN OLD.message_id != NEW.message_id
				   THEN '{message_id}'::varchar[]
				   ELSE '{}'::varchar[] END
		   || CASE WHEN OLD.content != NEW.content
				   THEN '{content}'::varchar[]
				   ELSE '{}'::varchar[] END;
	END IF;
	SELECT INTO cid, pname
		t.commitfest_id, p.name
	FROM
		patch p
		JOIN commitfest_topic t ON p.commitfest_topic_id = t.id
	WHERE
		p.id = NEW.patch_id;
	IF (TG_OP = 'INSERT') THEN
		cf := ARRAY['patch_comment_type_id', 'message_id', 'content' ];
	END IF;
	INSERT INTO patch_comment_audit
		(id, patch_id, change_type, changed_fields, commitfest_id, patch_name,
		 patch_comment_type_id, message_id, content, last_updater,
		 last_updated_time)
	VALUES
		(NEW.id, NEW.patch_id, TG_OP, cf, cid, pname,
		 NEW.patch_comment_type_id, NEW.message_id, NEW.content,
		 NEW.last_updater, NEW.last_updated_time);
	RETURN NULL;
END
$$ LANGUAGE plpgsql;

CREATE TRIGGER patch_comment_audit
	AFTER INSERT OR UPDATE OR DELETE ON patch_comment
	FOR EACH ROW EXECUTE PROCEDURE patch_comment_audit();

CREATE OR REPLACE FUNCTION patch_comment_audit_details(patch_comment_audit)
	RETURNS text AS $$
SELECT
	(SELECT name FROM patch_comment_type WHERE id = $1.patch_comment_type_id)
	|| ': ' || $1.content
	|| CASE WHEN $1.message_id != '' THEN ' (Message-ID: '
		|| $1.message_id || ')' ELSE '' END
$$ LANGUAGE sql;