2
SET client_min_messages=ERROR;
5
TRUNCATE BugSummaryJournal;
7
DROP INDEX bugsummary__dimensions__unique;
8
DROP INDEX bugsummary__full__idx;
10
ALTER TABLE BugSummary
11
-- Add a missing foreign key constraint we were unable to add live.
12
-- Person table is always being read, so locks are never acquired.
13
ADD CONSTRAINT bugsummaryjournal_viewed_by_fkey
14
FOREIGN KEY(viewed_by) REFERENCES Person ON DELETE CASCADE,
15
ADD COLUMN importance integer NOT NULL,
16
ADD COLUMN has_patch boolean NOT NULL,
17
ADD COLUMN fixed_upstream boolean NOT NULL;
19
ALTER TABLE BugSummaryJournal
20
ADD COLUMN importance integer NOT NULL,
21
ADD COLUMN has_patch boolean NOT NULL,
22
ADD COLUMN fixed_upstream boolean NOT NULL;
24
DROP VIEW CombinedBugSummary;
25
CREATE VIEW CombinedBugSummary AS (
27
id, count, product, productseries, distribution, distroseries,
28
sourcepackagename, viewed_by, tag, status, milestone,
29
importance, has_patch, fixed_upstream
33
-id as id, count, product, productseries, distribution, distroseries,
34
sourcepackagename, viewed_by, tag, status, milestone,
35
importance, has_patch, fixed_upstream
36
FROM BugSummaryJournal);
39
INSERT INTO BugSummary (
40
count, product, productseries, distribution, distroseries,
41
sourcepackagename, viewed_by, tag, status, importance, has_patch,
42
fixed_upstream, milestone)
45
relevant_bug AS (SELECT * FROM bug where duplicateof is NULL),
47
-- (bug.id, tag) for all bug-tag pairs plus (bug.id, NULL) for all bugs
49
SELECT relevant_bug.id, NULL::text AS tag FROM relevant_bug
51
SELECT relevant_bug.id, tag
52
FROM relevant_bug INNER JOIN bugtag ON relevant_bug.id=bugtag.bug),
54
-- (bug.id, NULL) for all public bugs + (bug.id, viewer) for all
55
-- (subscribers+assignee) on private bugs
57
SELECT relevant_bug.id, NULL::integer AS person
58
FROM relevant_bug WHERE NOT relevant_bug.private
60
SELECT relevant_bug.id, assignee AS person
62
INNER JOIN bugtask ON relevant_bug.id=bugtask.bug
63
WHERE relevant_bug.private and bugtask.assignee IS NOT NULL
65
SELECT relevant_bug.id, bugsubscription.person
66
FROM relevant_bug INNER JOIN bugsubscription
67
ON bugsubscription.bug=relevant_bug.id WHERE relevant_bug.private),
69
-- (bugtask.(bug, product, productseries, distribution, distroseries,
70
-- sourcepackagename, status, milestone) for all bugs + the same with
71
-- sourcepackage squashed to NULL)
74
bug, product, productseries, distribution, distroseries,
75
sourcepackagename, status, importance,
78
FROM BugTask AS RelatedBugTask
79
WHERE RelatedBugTask.bug = BugTask.bug
80
AND RelatedBugTask.id != BugTask.id
81
AND ((RelatedBugTask.bugwatch IS NOT NULL
82
AND RelatedBugTask.status IN (17, 25, 30))
83
OR (RelatedBugTask.product IS NOT NULL
84
AND RelatedBugTask.bugwatch IS NULL
85
AND RelatedBugTask.status IN (25, 30))))
86
) as fixed_upstream, milestone
90
bug, product, productseries, distribution, distroseries,
91
sourcepackagename, milestone)
92
bug, product, productseries, distribution, distroseries,
93
NULL::integer as sourcepackagename,
97
FROM BugTask AS RelatedBugTask
98
WHERE RelatedBugTask.bug = BugTask.bug
99
AND RelatedBugTask.id != BugTask.id
100
AND ((RelatedBugTask.bugwatch IS NOT NULL
101
AND RelatedBugTask.status IN (17, 25, 30))
102
OR (RelatedBugTask.product IS NOT NULL
103
AND RelatedBugTask.bugwatch IS NULL
104
AND RelatedBugTask.status IN (25, 30))))
105
) as fixed_upstream, milestone
106
FROM bugtask where sourcepackagename IS NOT NULL)
110
count(*), product, productseries, distribution, distroseries,
111
sourcepackagename, person, tag, status, importance,
112
latest_patch_uploaded IS NOT NULL AS has_patch, fixed_upstream,
115
INNER JOIN bug_tags ON relevant_bug.id=bug_tags.id
116
INNER JOIN bug_viewers ON relevant_bug.id=bug_viewers.id
117
INNER JOIN tasks on tasks.bug=relevant_bug.id
119
product, productseries, distribution, distroseries,
120
sourcepackagename, person, tag, status, importance, has_patch,
121
fixed_upstream, milestone;
124
CREATE INDEX bugsummary__full__idx ON BugSummary (
125
tag, status, product, productseries, distribution,
126
distroseries, sourcepackagename, viewed_by, milestone,
127
importance, has_patch, fixed_upstream);
128
CREATE UNIQUE INDEX bugsummary__product__unique
130
product, status, importance, has_patch, fixed_upstream,
131
COALESCE(tag, ''), COALESCE(milestone, -1), COALESCE(viewed_by, -1))
132
WHERE product IS NOT NULL;
133
CREATE UNIQUE INDEX bugsummary__productseries__unique
135
productseries, status, importance, has_patch, fixed_upstream,
136
COALESCE(tag, ''), COALESCE(milestone, -1), COALESCE(viewed_by, -1))
137
WHERE productseries IS NOT NULL;
138
CREATE UNIQUE INDEX bugsummary__distribution__unique
140
distribution, status, importance, has_patch, fixed_upstream,
141
COALESCE(sourcepackagename, -1),
142
COALESCE(tag, ''), COALESCE(milestone, -1), COALESCE(viewed_by, -1))
143
WHERE distribution IS NOT NULL;
144
CREATE UNIQUE INDEX bugsummary__distroseries__unique
146
distroseries, status, importance, has_patch, fixed_upstream,
147
COALESCE(sourcepackagename, -1),
148
COALESCE(tag, ''), COALESCE(milestone, -1), COALESCE(viewed_by, -1))
149
WHERE distroseries IS NOT NULL;
152
CREATE OR REPLACE FUNCTION bugsummary_journal_ins(d bugsummary)
158
INSERT INTO BugSummaryJournal (
159
count, product, productseries, distribution,
160
distroseries, sourcepackagename, viewed_by, tag,
162
importance, has_patch, fixed_upstream)
164
d.count, d.product, d.productseries, d.distribution,
165
d.distroseries, d.sourcepackagename, d.viewed_by, d.tag,
166
d.status, d.milestone,
167
d.importance, d.has_patch, d.fixed_upstream);
172
COMMENT ON FUNCTION bugsummary_journal_ins(bugsummary) IS
173
'Add an entry into BugSummaryJournal';
176
CREATE OR REPLACE FUNCTION bugsummary_rollup_journal() RETURNS VOID
177
LANGUAGE plpgsql VOLATILE
178
SECURITY DEFINER SET search_path TO public AS
181
d bugsummary%ROWTYPE;
184
-- Lock so we don't content with other invokations of this
185
-- function. We can happily lock the BugSummary table for writes
186
-- as this function is the only thing that updates that table.
187
-- BugSummaryJournal remains unlocked so nothing should be blocked.
188
LOCK TABLE BugSummary IN ROW EXCLUSIVE MODE;
190
SELECT MAX(id) INTO max_id FROM BugSummaryJournal;
208
FROM BugSummaryJournal
211
product, productseries, distribution, distroseries,
212
sourcepackagename, viewed_by, tag, status, milestone,
213
importance, has_patch, fixed_upstream
214
HAVING sum(count) <> 0
217
PERFORM bug_summary_dec(d);
218
ELSIF d.count > 0 THEN
219
PERFORM bug_summary_inc(d);
223
DELETE FROM BugSummaryJournal WHERE id <= max_id;
227
CREATE OR REPLACE FUNCTION bug_summary_dec(bugsummary) RETURNS VOID
230
-- We own the row reference, so in the absence of bugs this cannot
231
-- fail - just decrement the row.
232
UPDATE BugSummary SET count = count + $1.count
234
product IS NOT DISTINCT FROM $1.product
235
AND productseries IS NOT DISTINCT FROM $1.productseries
236
AND distribution IS NOT DISTINCT FROM $1.distribution
237
AND distroseries IS NOT DISTINCT FROM $1.distroseries
238
AND sourcepackagename IS NOT DISTINCT FROM $1.sourcepackagename
239
AND viewed_by IS NOT DISTINCT FROM $1.viewed_by
240
AND tag IS NOT DISTINCT FROM $1.tag
241
AND status IS NOT DISTINCT FROM $1.status
242
AND milestone IS NOT DISTINCT FROM $1.milestone
243
AND importance IS NOT DISTINCT FROM $1.importance
244
AND has_patch IS NOT DISTINCT FROM $1.has_patch
245
AND fixed_upstream IS NOT DISTINCT FROM $1.fixed_upstream;
246
-- gc the row (perhaps should be garbo but easy enough to add here:
247
DELETE FROM bugsummary
250
AND product IS NOT DISTINCT FROM $1.product
251
AND productseries IS NOT DISTINCT FROM $1.productseries
252
AND distribution IS NOT DISTINCT FROM $1.distribution
253
AND distroseries IS NOT DISTINCT FROM $1.distroseries
254
AND sourcepackagename IS NOT DISTINCT FROM $1.sourcepackagename
255
AND viewed_by IS NOT DISTINCT FROM $1.viewed_by
256
AND tag IS NOT DISTINCT FROM $1.tag
257
AND status IS NOT DISTINCT FROM $1.status
258
AND milestone IS NOT DISTINCT FROM $1.milestone
259
AND importance IS NOT DISTINCT FROM $1.importance
260
AND has_patch IS NOT DISTINCT FROM $1.has_patch
261
AND fixed_upstream IS NOT DISTINCT FROM $1.fixed_upstream;
262
-- If its not found then someone else also dec'd and won concurrently.
265
CREATE OR REPLACE FUNCTION bug_summary_inc(d bugsummary) RETURNS VOID
269
-- Shameless adaption from postgresql manual
271
-- first try to update the row
272
UPDATE BugSummary SET count = count + d.count
274
product IS NOT DISTINCT FROM d.product
275
AND productseries IS NOT DISTINCT FROM d.productseries
276
AND distribution IS NOT DISTINCT FROM d.distribution
277
AND distroseries IS NOT DISTINCT FROM d.distroseries
278
AND sourcepackagename IS NOT DISTINCT FROM d.sourcepackagename
279
AND viewed_by IS NOT DISTINCT FROM d.viewed_by
280
AND tag IS NOT DISTINCT FROM d.tag
281
AND status IS NOT DISTINCT FROM d.status
282
AND milestone IS NOT DISTINCT FROM d.milestone
283
AND importance IS NOT DISTINCT FROM d.importance
284
AND has_patch IS NOT DISTINCT FROM d.has_patch
285
AND fixed_upstream IS NOT DISTINCT FROM d.fixed_upstream;
289
-- not there, so try to insert the key
290
-- if someone else inserts the same key concurrently,
291
-- we could get a unique-key failure
293
INSERT INTO BugSummary(
294
count, product, productseries, distribution,
295
distroseries, sourcepackagename, viewed_by, tag,
297
importance, has_patch, fixed_upstream)
299
d.count, d.product, d.productseries, d.distribution,
300
d.distroseries, d.sourcepackagename, d.viewed_by, d.tag,
301
d.status, d.milestone,
302
d.importance, d.has_patch, d.fixed_upstream);
304
EXCEPTION WHEN unique_violation THEN
305
-- do nothing, and loop to try the UPDATE again
311
COMMENT ON FUNCTION bugsummary_rollup_journal() IS
312
'Collate and migrate rows from BugSummaryJournal to BugSummary';
315
CREATE OR REPLACE FUNCTION ensure_bugsummary_temp_journal() RETURNS VOID
316
LANGUAGE plpgsql VOLATILE AS
320
CREATE TEMPORARY TABLE bugsummary_temp_journal (
321
LIKE bugsummary ) ON COMMIT DROP;
322
ALTER TABLE bugsummary_temp_journal ALTER COLUMN id DROP NOT NULL;
324
WHEN duplicate_table THEN
329
COMMENT ON FUNCTION ensure_bugsummary_temp_journal() IS
330
'Create a temporary table bugsummary_temp_journal if it does not exist.';
333
CREATE OR REPLACE FUNCTION bug_summary_temp_journal_ins(d bugsummary)
334
RETURNS VOID LANGUAGE plpgsql AS
337
INSERT INTO BugSummary_Temp_Journal(
338
count, product, productseries, distribution,
339
distroseries, sourcepackagename, viewed_by, tag,
340
status, milestone, importance, has_patch, fixed_upstream)
342
d.count, d.product, d.productseries, d.distribution,
343
d.distroseries, d.sourcepackagename, d.viewed_by, d.tag,
344
d.status, d.milestone, d.importance, d.has_patch, d.fixed_upstream);
349
COMMENT ON FUNCTION bug_summary_temp_journal_ins(bugsummary) IS
350
'Insert a BugSummary into the temporary journal';
353
DROP FUNCTION bug_summary_temp_journal_dec(bugsummary);
354
DROP FUNCTION bug_summary_temp_journal_inc(bugsummary);
357
CREATE OR REPLACE FUNCTION bug_summary_flush_temp_journal() RETURNS VOID
358
LANGUAGE plpgsql VOLATILE AS
361
d bugsummary%ROWTYPE;
363
-- may get called even though no summaries were made (for simplicity in the
365
PERFORM ensure_bugsummary_temp_journal();
368
NULL::integer AS id, SUM(count), product, productseries,
369
distribution, distroseries, sourcepackagename,
370
viewed_by, tag, status, milestone,
371
importance, has_patch, fixed_upstream
372
FROM BugSummary_temp_journal
374
product, productseries,
375
distribution, distroseries, sourcepackagename,
376
viewed_by, tag, status, milestone, importance,
377
has_patch, fixed_upstream
378
HAVING SUM(count) <> 0
381
PERFORM bug_summary_dec(d);
383
PERFORM bug_summary_inc(d);
386
TRUNCATE bugsummary_temp_journal;
390
COMMENT ON FUNCTION bug_summary_flush_temp_journal() IS
391
'flush the temporary bugsummary journal into the bugsummary table';
394
CREATE OR REPLACE FUNCTION unsummarise_bug(BUG_ROW bug) RETURNS VOID
395
LANGUAGE plpgsql VOLATILE AS
398
d bugsummary%ROWTYPE;
400
PERFORM ensure_bugsummary_temp_journal();
401
FOR d IN SELECT * FROM bugsummary_locations(BUG_ROW) LOOP
403
PERFORM bug_summary_temp_journal_ins(d);
408
CREATE OR REPLACE FUNCTION summarise_bug(BUG_ROW bug) RETURNS VOID
409
LANGUAGE plpgsql VOLATILE AS
412
d bugsummary%ROWTYPE;
414
PERFORM ensure_bugsummary_temp_journal();
415
FOR d IN SELECT * FROM bugsummary_locations(BUG_ROW) LOOP
417
PERFORM bug_summary_temp_journal_ins(d);
423
CREATE OR REPLACE FUNCTION bug_maintain_bug_summary() RETURNS TRIGGER
424
LANGUAGE plpgsql VOLATILE SECURITY DEFINER SET search_path TO public AS
427
-- There is no INSERT logic, as a bug will not have any summary
428
-- information until BugTask rows have been attached.
429
IF TG_OP = 'UPDATE' THEN
430
IF OLD.duplicateof IS DISTINCT FROM NEW.duplicateof
431
OR OLD.private IS DISTINCT FROM NEW.private
432
OR (OLD.latest_patch_uploaded IS NULL)
433
<> (NEW.latest_patch_uploaded IS NULL) THEN
434
PERFORM unsummarise_bug(OLD);
435
PERFORM summarise_bug(NEW);
438
ELSIF TG_OP = 'DELETE' THEN
439
PERFORM unsummarise_bug(OLD);
442
PERFORM bug_summary_flush_temp_journal();
443
RETURN NULL; -- Ignored - this is an AFTER trigger
448
CREATE OR REPLACE FUNCTION bugtask_maintain_bug_summary() RETURNS TRIGGER
449
LANGUAGE plpgsql VOLATILE SECURITY DEFINER SET search_path TO public AS
452
-- This trigger only works if we are inserting, updating or deleting
453
-- a single row per statement.
455
-- Unlike bug_maintain_bug_summary, this trigger does not have access
456
-- to the old bug when invoked as an AFTER trigger. To work around this
457
-- we install this trigger as both a BEFORE and an AFTER trigger.
458
IF TG_OP = 'INSERT' THEN
459
IF TG_WHEN = 'BEFORE' THEN
460
PERFORM unsummarise_bug(bug_row(NEW.bug));
462
PERFORM summarise_bug(bug_row(NEW.bug));
464
PERFORM bug_summary_flush_temp_journal();
467
ELSIF TG_OP = 'DELETE' THEN
468
IF TG_WHEN = 'BEFORE' THEN
469
PERFORM unsummarise_bug(bug_row(OLD.bug));
471
PERFORM summarise_bug(bug_row(OLD.bug));
473
PERFORM bug_summary_flush_temp_journal();
477
IF (OLD.product IS DISTINCT FROM NEW.product
478
OR OLD.productseries IS DISTINCT FROM NEW.productseries
479
OR OLD.distribution IS DISTINCT FROM NEW.distribution
480
OR OLD.distroseries IS DISTINCT FROM NEW.distroseries
481
OR OLD.sourcepackagename IS DISTINCT FROM NEW.sourcepackagename
482
OR OLD.status IS DISTINCT FROM NEW.status
483
OR OLD.importance IS DISTINCT FROM NEW.importance
484
OR OLD.bugwatch IS DISTINCT FROM NEW.bugwatch
485
OR OLD.milestone IS DISTINCT FROM NEW.milestone) THEN
487
IF TG_WHEN = 'BEFORE' THEN
488
PERFORM unsummarise_bug(bug_row(OLD.bug));
489
IF OLD.bug <> NEW.bug THEN
490
PERFORM unsummarise_bug(bug_row(NEW.bug));
493
PERFORM summarise_bug(bug_row(OLD.bug));
494
IF OLD.bug <> NEW.bug THEN
495
PERFORM summarise_bug(bug_row(NEW.bug));
499
PERFORM bug_summary_flush_temp_journal();
506
CREATE OR REPLACE FUNCTION bugsummary_locations(BUG_ROW bug)
507
RETURNS SETOF bugsummary LANGUAGE plpgsql AS
510
IF BUG_ROW.duplicateof IS NOT NULL THEN
515
CAST(NULL AS integer) AS id,
516
CAST(1 AS integer) AS count,
517
product, productseries, distribution, distroseries,
518
sourcepackagename, person AS viewed_by, tag, status, milestone,
520
BUG_ROW.latest_patch_uploaded IS NOT NULL AS has_patch,
522
SELECT TRUE FROM BugTask AS RBT
525
-- This would just be 'RBT.id <> tasks.id', except
526
-- that the records from tasks are summaries and not
527
-- real bugtasks, and do not have an id.
528
AND (RBT.product IS DISTINCT FROM tasks.product
530
IS DISTINCT FROM tasks.productseries
531
OR RBT.distribution IS DISTINCT FROM tasks.distribution
532
OR RBT.distroseries IS DISTINCT FROM tasks.distroseries
533
OR RBT.sourcepackagename
534
IS DISTINCT FROM tasks.sourcepackagename)
535
-- Flagged as INVALID, FIXCOMMITTED or FIXRELEASED
536
-- via a bugwatch, or FIXCOMMITTED or FIXRELEASED on
538
AND ((bugwatch IS NOT NULL AND status IN (17, 25, 30))
539
OR (bugwatch IS NULL AND product IS NOT NULL
540
AND status IN (25, 30))))
541
)::boolean AS fixed_upstream
542
FROM bugsummary_tasks(BUG_ROW) AS tasks
543
JOIN bugsummary_tags(BUG_ROW) AS bug_tags ON TRUE
544
LEFT OUTER JOIN bugsummary_viewers(BUG_ROW) AS bug_viewers ON TRUE;
548
COMMENT ON FUNCTION bugsummary_locations(bug) IS
549
'Calculate what BugSummary rows should exist for a given Bug.';
552
CREATE OR REPLACE FUNCTION bugsummary_tasks(BUG_ROW bug)
553
RETURNS SETOF bugtask LANGUAGE plpgsql STABLE AS
561
-- One row only for each target permutation - need to ignore other fields
562
-- like date last modified to deal with conjoined masters and multiple
563
-- sourcepackage tasks in a distro.
566
product, productseries, distribution, distroseries,
567
sourcepackagename, status, milestone, importance, bugwatch
568
FROM BugTask WHERE bug=BUG_ROW.id
569
UNION -- Implicit DISTINCT
571
product, productseries, distribution, distroseries,
572
NULL, status, milestone, importance, bugwatch
573
FROM BugTask WHERE bug=BUG_ROW.id AND sourcepackagename IS NOT NULL
575
bt.product = r.product;
576
bt.productseries = r.productseries;
577
bt.distribution = r.distribution;
578
bt.distroseries = r.distroseries;
579
bt.sourcepackagename = r.sourcepackagename;
580
bt.status = r.status;
581
bt.milestone = r.milestone;
582
bt.importance = r.importance;
583
bt.bugwatch = r.bugwatch;
589
COMMENT ON FUNCTION bugsummary_tasks(bug) IS
590
'Return all tasks for the bug + all sourcepackagename tasks again with the sourcepackagename squashed';
594
INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 75, 0);