Merge lp:~wgrant/launchpad/db-baseline-2210 into lp:launchpad

Proposed by William Grant
Status: Merged
Merged at revision: 18888
Proposed branch: lp:~wgrant/launchpad/db-baseline-2210
Merge into: lp:launchpad
Diff against target: 38895 lines (+17239/-16332)
3 files modified
database/schema/Makefile (+2/-2)
database/schema/launchpad-2210-00-0.sql (+17234/-16330)
database/schema/patch-2210-00-0.sql (+3/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/db-baseline-2210
Reviewer Review Type Date Requested Status
Stuart Bishop (community) Approve
Review via email: mp+363648@code.launchpad.net

Commit message

Rebaseline schema, version 2210.

To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote :

Yup

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'database/schema/Makefile'
2--- database/schema/Makefile 2018-05-14 13:11:14 +0000
3+++ database/schema/Makefile 2019-02-26 07:46:16 +0000
4@@ -61,9 +61,9 @@
5 # on production. It is generated using newbaseline.py in
6 # bzr+ssh://devpad.canonical.com/code/stub/dbascripts
7 #
8-REV=2209
9+REV=2210
10 BASELINE=launchpad-${REV}-00-0.sql
11-MD5SUM=cc7a493c924196409a22392a16443d52 launchpad-2209-00-0.sql
12+MD5SUM=36ae7078cd41916bbbd9c116b2e6aea7 launchpad-2210-00-0.sql
13
14 default: all
15
16
17=== renamed file 'database/schema/patch-2209-00-0.sql' => 'database/schema/archive/patch-2209-00-0.sql'
18=== renamed file 'database/schema/patch-2209-00-1.sql' => 'database/schema/archive/patch-2209-00-1.sql'
19=== renamed file 'database/schema/patch-2209-00-2.sql' => 'database/schema/archive/patch-2209-00-2.sql'
20=== renamed file 'database/schema/patch-2209-00-3.sql' => 'database/schema/archive/patch-2209-00-3.sql'
21=== renamed file 'database/schema/patch-2209-00-5.sql' => 'database/schema/archive/patch-2209-00-5.sql'
22=== renamed file 'database/schema/patch-2209-00-6.sql' => 'database/schema/archive/patch-2209-00-6.sql'
23=== renamed file 'database/schema/patch-2209-00-7.sql' => 'database/schema/archive/patch-2209-00-7.sql'
24=== renamed file 'database/schema/patch-2209-00-8.sql' => 'database/schema/archive/patch-2209-00-8.sql'
25=== renamed file 'database/schema/patch-2209-00-9.sql' => 'database/schema/archive/patch-2209-00-9.sql'
26=== renamed file 'database/schema/patch-2209-01-0.sql' => 'database/schema/archive/patch-2209-01-0.sql'
27=== renamed file 'database/schema/patch-2209-01-1.sql' => 'database/schema/archive/patch-2209-01-1.sql'
28=== renamed file 'database/schema/patch-2209-02-0.sql' => 'database/schema/archive/patch-2209-02-0.sql'
29=== renamed file 'database/schema/patch-2209-04-0.sql' => 'database/schema/archive/patch-2209-04-0.sql'
30=== renamed file 'database/schema/patch-2209-05-1.sql' => 'database/schema/archive/patch-2209-05-1.sql'
31=== renamed file 'database/schema/patch-2209-06-1.sql' => 'database/schema/archive/patch-2209-06-1.sql'
32=== renamed file 'database/schema/patch-2209-07-0.sql' => 'database/schema/archive/patch-2209-07-0.sql'
33=== renamed file 'database/schema/patch-2209-07-1.sql' => 'database/schema/archive/patch-2209-07-1.sql'
34=== renamed file 'database/schema/patch-2209-08-1.sql' => 'database/schema/archive/patch-2209-08-1.sql'
35=== renamed file 'database/schema/patch-2209-09-0.sql' => 'database/schema/archive/patch-2209-09-0.sql'
36=== renamed file 'database/schema/patch-2209-10-0.sql' => 'database/schema/archive/patch-2209-10-0.sql'
37=== renamed file 'database/schema/patch-2209-11-0.sql' => 'database/schema/archive/patch-2209-11-0.sql'
38=== renamed file 'database/schema/patch-2209-11-1.sql' => 'database/schema/archive/patch-2209-11-1.sql'
39=== renamed file 'database/schema/patch-2209-11-2.sql' => 'database/schema/archive/patch-2209-11-2.sql'
40=== renamed file 'database/schema/patch-2209-11-3.sql' => 'database/schema/archive/patch-2209-11-3.sql'
41=== renamed file 'database/schema/patch-2209-11-4.sql' => 'database/schema/archive/patch-2209-11-4.sql'
42=== renamed file 'database/schema/patch-2209-11-5.sql' => 'database/schema/archive/patch-2209-11-5.sql'
43=== renamed file 'database/schema/patch-2209-12-0.sql' => 'database/schema/archive/patch-2209-12-0.sql'
44=== renamed file 'database/schema/patch-2209-12-1.sql' => 'database/schema/archive/patch-2209-12-1.sql'
45=== renamed file 'database/schema/patch-2209-12-2.sql' => 'database/schema/archive/patch-2209-12-2.sql'
46=== renamed file 'database/schema/patch-2209-12-3.sql' => 'database/schema/archive/patch-2209-12-3.sql'
47=== renamed file 'database/schema/patch-2209-12-4.sql' => 'database/schema/archive/patch-2209-12-4.sql'
48=== renamed file 'database/schema/patch-2209-12-5.sql' => 'database/schema/archive/patch-2209-12-5.sql'
49=== renamed file 'database/schema/patch-2209-14-0.sql' => 'database/schema/archive/patch-2209-14-0.sql'
50=== renamed file 'database/schema/patch-2209-15-0.sql' => 'database/schema/archive/patch-2209-15-0.sql'
51=== renamed file 'database/schema/patch-2209-15-1.sql' => 'database/schema/archive/patch-2209-15-1.sql'
52=== renamed file 'database/schema/patch-2209-15-2.sql' => 'database/schema/archive/patch-2209-15-2.sql'
53=== renamed file 'database/schema/patch-2209-15-3.sql' => 'database/schema/archive/patch-2209-15-3.sql'
54=== renamed file 'database/schema/patch-2209-16-0.sql' => 'database/schema/archive/patch-2209-16-0.sql'
55=== renamed file 'database/schema/patch-2209-16-1.sql' => 'database/schema/archive/patch-2209-16-1.sql'
56=== renamed file 'database/schema/patch-2209-16-2.sql' => 'database/schema/archive/patch-2209-16-2.sql'
57=== renamed file 'database/schema/patch-2209-16-3.sql' => 'database/schema/archive/patch-2209-16-3.sql'
58=== renamed file 'database/schema/patch-2209-16-4.sql' => 'database/schema/archive/patch-2209-16-4.sql'
59=== renamed file 'database/schema/patch-2209-16-5.sql' => 'database/schema/archive/patch-2209-16-5.sql'
60=== renamed file 'database/schema/patch-2209-16-6.sql' => 'database/schema/archive/patch-2209-16-6.sql'
61=== renamed file 'database/schema/patch-2209-16-7.sql' => 'database/schema/archive/patch-2209-16-7.sql'
62=== renamed file 'database/schema/patch-2209-16-8.sql' => 'database/schema/archive/patch-2209-16-8.sql'
63=== renamed file 'database/schema/patch-2209-17-0.sql' => 'database/schema/archive/patch-2209-17-0.sql'
64=== renamed file 'database/schema/patch-2209-17-1.sql' => 'database/schema/archive/patch-2209-17-1.sql'
65=== renamed file 'database/schema/patch-2209-18-0.sql' => 'database/schema/archive/patch-2209-18-0.sql'
66=== renamed file 'database/schema/patch-2209-18-1.sql' => 'database/schema/archive/patch-2209-18-1.sql'
67=== renamed file 'database/schema/patch-2209-18-2.sql' => 'database/schema/archive/patch-2209-18-2.sql'
68=== renamed file 'database/schema/patch-2209-18-3.sql' => 'database/schema/archive/patch-2209-18-3.sql'
69=== renamed file 'database/schema/patch-2209-18-4.sql' => 'database/schema/archive/patch-2209-18-4.sql'
70=== renamed file 'database/schema/patch-2209-19-0.sql' => 'database/schema/archive/patch-2209-19-0.sql'
71=== renamed file 'database/schema/patch-2209-19-1.sql' => 'database/schema/archive/patch-2209-19-1.sql'
72=== renamed file 'database/schema/patch-2209-19-2.sql' => 'database/schema/archive/patch-2209-19-2.sql'
73=== renamed file 'database/schema/patch-2209-19-3.sql' => 'database/schema/archive/patch-2209-19-3.sql'
74=== renamed file 'database/schema/patch-2209-20-0.sql' => 'database/schema/archive/patch-2209-20-0.sql'
75=== renamed file 'database/schema/patch-2209-20-1.sql' => 'database/schema/archive/patch-2209-20-1.sql'
76=== renamed file 'database/schema/patch-2209-21-0.sql' => 'database/schema/archive/patch-2209-21-0.sql'
77=== renamed file 'database/schema/patch-2209-21-1.sql' => 'database/schema/archive/patch-2209-21-1.sql'
78=== renamed file 'database/schema/patch-2209-21-2.sql' => 'database/schema/archive/patch-2209-21-2.sql'
79=== renamed file 'database/schema/patch-2209-21-3.sql' => 'database/schema/archive/patch-2209-21-3.sql'
80=== renamed file 'database/schema/patch-2209-21-4.sql' => 'database/schema/archive/patch-2209-21-4.sql'
81=== renamed file 'database/schema/patch-2209-22-0.sql' => 'database/schema/archive/patch-2209-22-0.sql'
82=== renamed file 'database/schema/patch-2209-23-0.sql' => 'database/schema/archive/patch-2209-23-0.sql'
83=== renamed file 'database/schema/patch-2209-23-1.sql' => 'database/schema/archive/patch-2209-23-1.sql'
84=== renamed file 'database/schema/patch-2209-23-2.sql' => 'database/schema/archive/patch-2209-23-2.sql'
85=== renamed file 'database/schema/patch-2209-23-3.sql' => 'database/schema/archive/patch-2209-23-3.sql'
86=== renamed file 'database/schema/patch-2209-23-4.sql' => 'database/schema/archive/patch-2209-23-4.sql'
87=== renamed file 'database/schema/patch-2209-23-5.sql' => 'database/schema/archive/patch-2209-23-5.sql'
88=== renamed file 'database/schema/patch-2209-24-1.sql' => 'database/schema/archive/patch-2209-24-1.sql'
89=== renamed file 'database/schema/patch-2209-24-2.sql' => 'database/schema/archive/patch-2209-24-2.sql'
90=== renamed file 'database/schema/patch-2209-24-3.sql' => 'database/schema/archive/patch-2209-24-3.sql'
91=== renamed file 'database/schema/patch-2209-25-1.sql' => 'database/schema/archive/patch-2209-25-1.sql'
92=== renamed file 'database/schema/patch-2209-26-0.sql' => 'database/schema/archive/patch-2209-26-0.sql'
93=== renamed file 'database/schema/patch-2209-26-1.sql' => 'database/schema/archive/patch-2209-26-1.sql'
94=== renamed file 'database/schema/patch-2209-26-2.sql' => 'database/schema/archive/patch-2209-26-2.sql'
95=== renamed file 'database/schema/patch-2209-26-3.sql' => 'database/schema/archive/patch-2209-26-3.sql'
96=== renamed file 'database/schema/patch-2209-26-4.sql' => 'database/schema/archive/patch-2209-26-4.sql'
97=== renamed file 'database/schema/patch-2209-26-5.sql' => 'database/schema/archive/patch-2209-26-5.sql'
98=== renamed file 'database/schema/patch-2209-27-1.sql' => 'database/schema/archive/patch-2209-27-1.sql'
99=== renamed file 'database/schema/patch-2209-27-2.sql' => 'database/schema/archive/patch-2209-27-2.sql'
100=== renamed file 'database/schema/patch-2209-27-3.sql' => 'database/schema/archive/patch-2209-27-3.sql'
101=== renamed file 'database/schema/patch-2209-27-4.sql' => 'database/schema/archive/patch-2209-27-4.sql'
102=== renamed file 'database/schema/patch-2209-28-1.sql' => 'database/schema/archive/patch-2209-28-1.sql'
103=== renamed file 'database/schema/patch-2209-28-2.sql' => 'database/schema/archive/patch-2209-28-2.sql'
104=== renamed file 'database/schema/patch-2209-28-4.sql' => 'database/schema/archive/patch-2209-28-4.sql'
105=== renamed file 'database/schema/patch-2209-28-5.sql' => 'database/schema/archive/patch-2209-28-5.sql'
106=== renamed file 'database/schema/patch-2209-28-6.sql' => 'database/schema/archive/patch-2209-28-6.sql'
107=== renamed file 'database/schema/patch-2209-29-0.sql' => 'database/schema/archive/patch-2209-29-0.sql'
108=== renamed file 'database/schema/patch-2209-30-1.sql' => 'database/schema/archive/patch-2209-30-1.sql'
109=== renamed file 'database/schema/patch-2209-30-2.sql' => 'database/schema/archive/patch-2209-30-2.sql'
110=== renamed file 'database/schema/patch-2209-31-1.sql' => 'database/schema/archive/patch-2209-31-1.sql'
111=== renamed file 'database/schema/patch-2209-31-2.sql' => 'database/schema/archive/patch-2209-31-2.sql'
112=== renamed file 'database/schema/patch-2209-31-3.sql' => 'database/schema/archive/patch-2209-31-3.sql'
113=== renamed file 'database/schema/patch-2209-32-0.sql' => 'database/schema/archive/patch-2209-32-0.sql'
114=== renamed file 'database/schema/patch-2209-34-1.sql' => 'database/schema/archive/patch-2209-34-1.sql'
115=== renamed file 'database/schema/patch-2209-35-1.sql' => 'database/schema/archive/patch-2209-35-1.sql'
116=== renamed file 'database/schema/patch-2209-35-2.sql' => 'database/schema/archive/patch-2209-35-2.sql'
117=== renamed file 'database/schema/patch-2209-35-3.sql' => 'database/schema/archive/patch-2209-35-3.sql'
118=== renamed file 'database/schema/patch-2209-35-4.sql' => 'database/schema/archive/patch-2209-35-4.sql'
119=== renamed file 'database/schema/patch-2209-36-0.sql' => 'database/schema/archive/patch-2209-36-0.sql'
120=== renamed file 'database/schema/patch-2209-36-1.sql' => 'database/schema/archive/patch-2209-36-1.sql'
121=== renamed file 'database/schema/patch-2209-37-0.sql' => 'database/schema/archive/patch-2209-37-0.sql'
122=== renamed file 'database/schema/patch-2209-38-0.sql' => 'database/schema/archive/patch-2209-38-0.sql'
123=== renamed file 'database/schema/patch-2209-38-1.sql' => 'database/schema/archive/patch-2209-38-1.sql'
124=== renamed file 'database/schema/patch-2209-38-2.sql' => 'database/schema/archive/patch-2209-38-2.sql'
125=== renamed file 'database/schema/patch-2209-38-3.sql' => 'database/schema/archive/patch-2209-38-3.sql'
126=== renamed file 'database/schema/patch-2209-39-0.sql' => 'database/schema/archive/patch-2209-39-0.sql'
127=== renamed file 'database/schema/patch-2209-39-1.sql' => 'database/schema/archive/patch-2209-39-1.sql'
128=== renamed file 'database/schema/patch-2209-40-0.sql' => 'database/schema/archive/patch-2209-40-0.sql'
129=== renamed file 'database/schema/patch-2209-40-1.sql' => 'database/schema/archive/patch-2209-40-1.sql'
130=== renamed file 'database/schema/patch-2209-40-2.sql' => 'database/schema/archive/patch-2209-40-2.sql'
131=== renamed file 'database/schema/patch-2209-40-3.sql' => 'database/schema/archive/patch-2209-40-3.sql'
132=== renamed file 'database/schema/patch-2209-41-0.sql' => 'database/schema/archive/patch-2209-41-0.sql'
133=== renamed file 'database/schema/patch-2209-41-1.sql' => 'database/schema/archive/patch-2209-41-1.sql'
134=== renamed file 'database/schema/patch-2209-41-2.sql' => 'database/schema/archive/patch-2209-41-2.sql'
135=== renamed file 'database/schema/patch-2209-41-3.sql' => 'database/schema/archive/patch-2209-41-3.sql'
136=== renamed file 'database/schema/patch-2209-41-4.sql' => 'database/schema/archive/patch-2209-41-4.sql'
137=== renamed file 'database/schema/patch-2209-41-5.sql' => 'database/schema/archive/patch-2209-41-5.sql'
138=== renamed file 'database/schema/patch-2209-42-0.sql' => 'database/schema/archive/patch-2209-42-0.sql'
139=== renamed file 'database/schema/patch-2209-43-0.sql' => 'database/schema/archive/patch-2209-43-0.sql'
140=== renamed file 'database/schema/patch-2209-44-0.sql' => 'database/schema/archive/patch-2209-44-0.sql'
141=== renamed file 'database/schema/patch-2209-44-1.sql' => 'database/schema/archive/patch-2209-44-1.sql'
142=== renamed file 'database/schema/patch-2209-44-2.sql' => 'database/schema/archive/patch-2209-44-2.sql'
143=== renamed file 'database/schema/patch-2209-44-3.sql' => 'database/schema/archive/patch-2209-44-3.sql'
144=== renamed file 'database/schema/patch-2209-44-4.sql' => 'database/schema/archive/patch-2209-44-4.sql'
145=== renamed file 'database/schema/patch-2209-45-0.sql' => 'database/schema/archive/patch-2209-45-0.sql'
146=== renamed file 'database/schema/patch-2209-46-0.sql' => 'database/schema/archive/patch-2209-46-0.sql'
147=== renamed file 'database/schema/patch-2209-47-0.sql' => 'database/schema/archive/patch-2209-47-0.sql'
148=== renamed file 'database/schema/patch-2209-47-1.sql' => 'database/schema/archive/patch-2209-47-1.sql'
149=== renamed file 'database/schema/patch-2209-48-0.sql' => 'database/schema/archive/patch-2209-48-0.sql'
150=== renamed file 'database/schema/patch-2209-49-0.sql' => 'database/schema/archive/patch-2209-49-0.sql'
151=== renamed file 'database/schema/patch-2209-49-1.sql' => 'database/schema/archive/patch-2209-49-1.sql'
152=== renamed file 'database/schema/patch-2209-49-2.sql' => 'database/schema/archive/patch-2209-49-2.sql'
153=== renamed file 'database/schema/patch-2209-50-0.sql' => 'database/schema/archive/patch-2209-50-0.sql'
154=== renamed file 'database/schema/patch-2209-51-0.sql' => 'database/schema/archive/patch-2209-51-0.sql'
155=== renamed file 'database/schema/patch-2209-51-1.sql' => 'database/schema/archive/patch-2209-51-1.sql'
156=== renamed file 'database/schema/patch-2209-51-2.sql' => 'database/schema/archive/patch-2209-51-2.sql'
157=== renamed file 'database/schema/patch-2209-52-0.sql' => 'database/schema/archive/patch-2209-52-0.sql'
158=== renamed file 'database/schema/patch-2209-53-0.sql' => 'database/schema/archive/patch-2209-53-0.sql'
159=== renamed file 'database/schema/patch-2209-53-1.sql' => 'database/schema/archive/patch-2209-53-1.sql'
160=== renamed file 'database/schema/patch-2209-53-3.sql' => 'database/schema/archive/patch-2209-53-3.sql'
161=== renamed file 'database/schema/patch-2209-53-4.sql' => 'database/schema/archive/patch-2209-53-4.sql'
162=== renamed file 'database/schema/patch-2209-53-5.sql' => 'database/schema/archive/patch-2209-53-5.sql'
163=== renamed file 'database/schema/patch-2209-53-6.sql' => 'database/schema/archive/patch-2209-53-6.sql'
164=== renamed file 'database/schema/patch-2209-53-7.sql' => 'database/schema/archive/patch-2209-53-7.sql'
165=== renamed file 'database/schema/patch-2209-53-8.sql' => 'database/schema/archive/patch-2209-53-8.sql'
166=== renamed file 'database/schema/patch-2209-53-9.sql' => 'database/schema/archive/patch-2209-53-9.sql'
167=== renamed file 'database/schema/patch-2209-54-0.sql' => 'database/schema/archive/patch-2209-54-0.sql'
168=== renamed file 'database/schema/patch-2209-55-0.sql' => 'database/schema/archive/patch-2209-55-0.sql'
169=== renamed file 'database/schema/patch-2209-56-0.sql' => 'database/schema/archive/patch-2209-56-0.sql'
170=== renamed file 'database/schema/patch-2209-56-1.sql' => 'database/schema/archive/patch-2209-56-1.sql'
171=== renamed file 'database/schema/patch-2209-56-2.sql' => 'database/schema/archive/patch-2209-56-2.sql'
172=== renamed file 'database/schema/patch-2209-56-3.sql' => 'database/schema/archive/patch-2209-56-3.sql'
173=== renamed file 'database/schema/patch-2209-56-4.sql' => 'database/schema/archive/patch-2209-56-4.sql'
174=== renamed file 'database/schema/patch-2209-57-0.sql' => 'database/schema/archive/patch-2209-57-0.sql'
175=== renamed file 'database/schema/patch-2209-58-0.sql' => 'database/schema/archive/patch-2209-58-0.sql'
176=== renamed file 'database/schema/patch-2209-58-1.sql' => 'database/schema/archive/patch-2209-58-1.sql'
177=== renamed file 'database/schema/patch-2209-58-2.sql' => 'database/schema/archive/patch-2209-58-2.sql'
178=== renamed file 'database/schema/patch-2209-58-3.sql' => 'database/schema/archive/patch-2209-58-3.sql'
179=== renamed file 'database/schema/patch-2209-58-4.sql' => 'database/schema/archive/patch-2209-58-4.sql'
180=== renamed file 'database/schema/patch-2209-59-0.sql' => 'database/schema/archive/patch-2209-59-0.sql'
181=== renamed file 'database/schema/patch-2209-59-1.sql' => 'database/schema/archive/patch-2209-59-1.sql'
182=== renamed file 'database/schema/patch-2209-59-2.sql' => 'database/schema/archive/patch-2209-59-2.sql'
183=== renamed file 'database/schema/patch-2209-60-0.sql' => 'database/schema/archive/patch-2209-60-0.sql'
184=== renamed file 'database/schema/patch-2209-61-0.sql' => 'database/schema/archive/patch-2209-61-0.sql'
185=== renamed file 'database/schema/patch-2209-61-1.sql' => 'database/schema/archive/patch-2209-61-1.sql'
186=== renamed file 'database/schema/patch-2209-61-2.sql' => 'database/schema/archive/patch-2209-61-2.sql'
187=== renamed file 'database/schema/patch-2209-61-3.sql' => 'database/schema/archive/patch-2209-61-3.sql'
188=== renamed file 'database/schema/patch-2209-61-4.sql' => 'database/schema/archive/patch-2209-61-4.sql'
189=== renamed file 'database/schema/patch-2209-61-5.sql' => 'database/schema/archive/patch-2209-61-5.sql'
190=== renamed file 'database/schema/patch-2209-61-6.sql' => 'database/schema/archive/patch-2209-61-6.sql'
191=== renamed file 'database/schema/patch-2209-61-7.sql' => 'database/schema/archive/patch-2209-61-7.sql'
192=== renamed file 'database/schema/patch-2209-61-8.sql' => 'database/schema/archive/patch-2209-61-8.sql'
193=== renamed file 'database/schema/patch-2209-61-9.sql' => 'database/schema/archive/patch-2209-61-9.sql'
194=== renamed file 'database/schema/patch-2209-62-0.sql' => 'database/schema/archive/patch-2209-62-0.sql'
195=== renamed file 'database/schema/patch-2209-62-1.sql' => 'database/schema/archive/patch-2209-62-1.sql'
196=== renamed file 'database/schema/patch-2209-64-0.sql' => 'database/schema/archive/patch-2209-64-0.sql'
197=== renamed file 'database/schema/patch-2209-64-1.sql' => 'database/schema/archive/patch-2209-64-1.sql'
198=== renamed file 'database/schema/patch-2209-65-0.sql' => 'database/schema/archive/patch-2209-65-0.sql'
199=== renamed file 'database/schema/patch-2209-66-0.sql' => 'database/schema/archive/patch-2209-66-0.sql'
200=== renamed file 'database/schema/patch-2209-66-1.sql' => 'database/schema/archive/patch-2209-66-1.sql'
201=== renamed file 'database/schema/patch-2209-67-0.sql' => 'database/schema/archive/patch-2209-67-0.sql'
202=== renamed file 'database/schema/patch-2209-67-1.sql' => 'database/schema/archive/patch-2209-67-1.sql'
203=== renamed file 'database/schema/patch-2209-67-2.sql' => 'database/schema/archive/patch-2209-67-2.sql'
204=== renamed file 'database/schema/patch-2209-67-3.sql' => 'database/schema/archive/patch-2209-67-3.sql'
205=== renamed file 'database/schema/patch-2209-68-0.sql' => 'database/schema/archive/patch-2209-68-0.sql'
206=== renamed file 'database/schema/patch-2209-68-1.sql' => 'database/schema/archive/patch-2209-68-1.sql'
207=== renamed file 'database/schema/patch-2209-68-2.sql' => 'database/schema/archive/patch-2209-68-2.sql'
208=== renamed file 'database/schema/patch-2209-69-0.sql' => 'database/schema/archive/patch-2209-69-0.sql'
209=== renamed file 'database/schema/patch-2209-69-1.sql' => 'database/schema/archive/patch-2209-69-1.sql'
210=== renamed file 'database/schema/patch-2209-69-2.sql' => 'database/schema/archive/patch-2209-69-2.sql'
211=== renamed file 'database/schema/patch-2209-69-3.sql' => 'database/schema/archive/patch-2209-69-3.sql'
212=== renamed file 'database/schema/patch-2209-69-4.sql' => 'database/schema/archive/patch-2209-69-4.sql'
213=== renamed file 'database/schema/patch-2209-69-5.sql' => 'database/schema/archive/patch-2209-69-5.sql'
214=== renamed file 'database/schema/patch-2209-69-6.sql' => 'database/schema/archive/patch-2209-69-6.sql'
215=== renamed file 'database/schema/patch-2209-69-7.sql' => 'database/schema/archive/patch-2209-69-7.sql'
216=== renamed file 'database/schema/patch-2209-69-8.sql' => 'database/schema/archive/patch-2209-69-8.sql'
217=== renamed file 'database/schema/patch-2209-69-9.sql' => 'database/schema/archive/patch-2209-69-9.sql'
218=== renamed file 'database/schema/patch-2209-70-0.sql' => 'database/schema/archive/patch-2209-70-0.sql'
219=== renamed file 'database/schema/patch-2209-71-0.sql' => 'database/schema/archive/patch-2209-71-0.sql'
220=== renamed file 'database/schema/patch-2209-71-1.sql' => 'database/schema/archive/patch-2209-71-1.sql'
221=== renamed file 'database/schema/patch-2209-72-0.sql' => 'database/schema/archive/patch-2209-72-0.sql'
222=== renamed file 'database/schema/patch-2209-73-0.sql' => 'database/schema/archive/patch-2209-73-0.sql'
223=== renamed file 'database/schema/patch-2209-73-1.sql' => 'database/schema/archive/patch-2209-73-1.sql'
224=== renamed file 'database/schema/patch-2209-74-0.sql' => 'database/schema/archive/patch-2209-74-0.sql'
225=== renamed file 'database/schema/patch-2209-75-0.sql' => 'database/schema/archive/patch-2209-75-0.sql'
226=== renamed file 'database/schema/patch-2209-75-1.sql' => 'database/schema/archive/patch-2209-75-1.sql'
227=== renamed file 'database/schema/patch-2209-77-0.sql' => 'database/schema/archive/patch-2209-77-0.sql'
228=== renamed file 'database/schema/patch-2209-77-1.sql' => 'database/schema/archive/patch-2209-77-1.sql'
229=== renamed file 'database/schema/patch-2209-77-2.sql' => 'database/schema/archive/patch-2209-77-2.sql'
230=== renamed file 'database/schema/patch-2209-77-3.sql' => 'database/schema/archive/patch-2209-77-3.sql'
231=== renamed file 'database/schema/patch-2209-78-0.sql' => 'database/schema/archive/patch-2209-78-0.sql'
232=== renamed file 'database/schema/patch-2209-78-1.sql' => 'database/schema/archive/patch-2209-78-1.sql'
233=== renamed file 'database/schema/patch-2209-78-2.sql' => 'database/schema/archive/patch-2209-78-2.sql'
234=== renamed file 'database/schema/patch-2209-79-0.sql' => 'database/schema/archive/patch-2209-79-0.sql'
235=== renamed file 'database/schema/patch-2209-80-0.sql' => 'database/schema/archive/patch-2209-80-0.sql'
236=== renamed file 'database/schema/patch-2209-80-1.sql' => 'database/schema/archive/patch-2209-80-1.sql'
237=== renamed file 'database/schema/patch-2209-80-2.sql' => 'database/schema/archive/patch-2209-80-2.sql'
238=== renamed file 'database/schema/patch-2209-81-0.sql' => 'database/schema/archive/patch-2209-81-0.sql'
239=== renamed file 'database/schema/patch-2209-82-0.sql' => 'database/schema/archive/patch-2209-82-0.sql'
240=== renamed file 'database/schema/patch-2209-82-1.sql' => 'database/schema/archive/patch-2209-82-1.sql'
241=== renamed file 'database/schema/patch-2209-83-0.sql' => 'database/schema/archive/patch-2209-83-0.sql'
242=== renamed file 'database/schema/patch-2209-83-1.sql' => 'database/schema/archive/patch-2209-83-1.sql'
243=== renamed file 'database/schema/patch-2209-83-2.sql' => 'database/schema/archive/patch-2209-83-2.sql'
244=== renamed file 'database/schema/patch-2209-83-3.sql' => 'database/schema/archive/patch-2209-83-3.sql'
245=== renamed file 'database/schema/patch-2209-83-4.sql' => 'database/schema/archive/patch-2209-83-4.sql'
246=== renamed file 'database/schema/patch-2209-83-5.sql' => 'database/schema/archive/patch-2209-83-5.sql'
247=== renamed file 'database/schema/patch-2209-83-6.sql' => 'database/schema/archive/patch-2209-83-6.sql'
248=== renamed file 'database/schema/patch-2209-84-0.sql' => 'database/schema/archive/patch-2209-84-0.sql'
249=== renamed file 'database/schema/patch-2209-84-1.sql' => 'database/schema/archive/patch-2209-84-1.sql'
250=== renamed file 'database/schema/patch-2209-85-0.sql' => 'database/schema/archive/patch-2209-85-0.sql'
251=== renamed file 'database/schema/patch-2209-85-1.sql' => 'database/schema/archive/patch-2209-85-1.sql'
252=== renamed file 'database/schema/patch-2209-86-0.sql' => 'database/schema/archive/patch-2209-86-0.sql'
253=== renamed file 'database/schema/launchpad-2209-00-0.sql' => 'database/schema/launchpad-2210-00-0.sql'
254--- database/schema/launchpad-2209-00-0.sql 2015-07-21 09:04:01 +0000
255+++ database/schema/launchpad-2210-00-0.sql 2019-02-26 07:46:16 +0000
256@@ -1,9 +1,11 @@
257--- Generated Tue Dec 6 20:57:32 2011 UTC
258+-- Generated Mon Feb 25 21:35:23 2019 UTC
259
260 SET client_min_messages TO ERROR;
261 SET statement_timeout = 0;
262+SET lock_timeout = 0;
263 SET client_encoding = 'UTF8';
264 SET standard_conforming_strings = off;
265+SELECT pg_catalog.set_config('search_path', '', false);
266 SET check_function_bodies = false;
267 SET client_min_messages = warning;
268 SET escape_string_warning = off;
269@@ -11,56 +13,72 @@
270 CREATE SCHEMA todrop;
271
272
273-CREATE SCHEMA ts2;
274-
275-
276-CREATE PROCEDURAL LANGUAGE plpgsql;
277-
278-
279-CREATE PROCEDURAL LANGUAGE plpythonu;
280-
281-
282-SET search_path = public, pg_catalog;
283-
284-CREATE TYPE debversion;
285-
286-
287-CREATE FUNCTION debversionin(cstring) RETURNS debversion
288+CREATE SCHEMA trgm;
289+
290+
291+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
292+
293+
294+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
295+
296+
297+CREATE EXTENSION IF NOT EXISTS plpythonu WITH SCHEMA pg_catalog;
298+
299+
300+COMMENT ON EXTENSION plpythonu IS 'PL/PythonU untrusted procedural language';
301+
302+
303+CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA trgm;
304+
305+
306+COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams';
307+
308+
309+CREATE EXTENSION IF NOT EXISTS pgstattuple WITH SCHEMA public;
310+
311+
312+COMMENT ON EXTENSION pgstattuple IS 'show tuple-level statistics';
313+
314+
315+CREATE TYPE public.debversion;
316+
317+
318+CREATE FUNCTION public.debversionin(cstring) RETURNS public.debversion
319 LANGUAGE internal IMMUTABLE STRICT
320 AS $$textin$$;
321
322
323-CREATE FUNCTION debversionout(debversion) RETURNS cstring
324+CREATE FUNCTION public.debversionout(public.debversion) RETURNS cstring
325 LANGUAGE internal IMMUTABLE STRICT
326 AS $$textout$$;
327
328
329-CREATE FUNCTION debversionrecv(internal) RETURNS debversion
330+CREATE FUNCTION public.debversionrecv(internal) RETURNS public.debversion
331 LANGUAGE internal STABLE STRICT
332 AS $$textrecv$$;
333
334
335-CREATE FUNCTION debversionsend(debversion) RETURNS bytea
336+CREATE FUNCTION public.debversionsend(public.debversion) RETURNS bytea
337 LANGUAGE internal STABLE STRICT
338 AS $$textsend$$;
339
340
341-CREATE TYPE debversion (
342+CREATE TYPE public.debversion (
343 INTERNALLENGTH = variable,
344- INPUT = debversionin,
345- OUTPUT = debversionout,
346- RECEIVE = debversionrecv,
347- SEND = debversionsend,
348+ INPUT = public.debversionin,
349+ OUTPUT = public.debversionout,
350+ RECEIVE = public.debversionrecv,
351+ SEND = public.debversionsend,
352 CATEGORY = 'S',
353 ALIGNMENT = int4,
354 STORAGE = extended
355 );
356
357
358-COMMENT ON TYPE debversion IS 'Debian package version number';
359-
360-
361-CREATE TYPE pgstattuple_type AS (
362+COMMENT ON TYPE public.debversion IS 'Debian package version number';
363+
364+
365+CREATE TYPE public.pgstattuple_type AS (
366 table_len bigint,
367 tuple_count bigint,
368 tuple_len bigint,
369@@ -73,77 +91,337 @@
370 );
371
372
373-SET search_path = ts2, pg_catalog;
374-
375-CREATE DOMAIN gtsq AS text;
376-
377-
378-CREATE DOMAIN gtsvector AS pg_catalog.gtsvector;
379-
380-
381-CREATE TYPE statinfo AS (
382- word text,
383- ndoc integer,
384- nentry integer
385-);
386-
387-
388-CREATE TYPE tokenout AS (
389- tokid integer,
390- token text
391-);
392-
393-
394-CREATE TYPE tokentype AS (
395- tokid integer,
396- alias text,
397- descr text
398-);
399-
400-
401-CREATE TYPE tsdebug AS (
402- ts_name text,
403- tok_type text,
404- description text,
405- token text,
406- dict_name text[],
407- tsvector pg_catalog.tsvector
408-);
409-
410-
411-CREATE DOMAIN tsquery AS pg_catalog.tsquery;
412-
413-
414-CREATE DOMAIN tsvector AS pg_catalog.tsvector;
415-
416-
417-SET search_path = public, pg_catalog;
418-
419-CREATE FUNCTION activity() RETURNS SETOF pg_stat_activity
420- LANGUAGE sql SECURITY DEFINER
421- SET search_path TO public
422- AS $$
423- SELECT
424- datid, datname, procpid, usesysid, usename,
425- CASE
426- WHEN current_query LIKE '<IDLE>%'
427- OR current_query LIKE 'autovacuum:%'
428- THEN current_query
429- ELSE
430- '<HIDDEN>'
431- END AS current_query,
432- waiting, xact_start, query_start,
433- backend_start, client_addr, client_port
434- FROM pg_catalog.pg_stat_activity;
435-$$;
436-
437-
438-COMMENT ON FUNCTION activity() IS 'SECURITY DEFINER wrapper around pg_stat_activity allowing unprivileged users to access most of its information.';
439-
440-
441-CREATE FUNCTION add_test_openid_identifier(account_ integer) RETURNS boolean
442- LANGUAGE plpgsql SECURITY DEFINER
443- SET search_path TO public
444+CREATE DOMAIN public.ts2_tsvector AS tsvector;
445+
446+
447+CREATE FUNCTION public._ftq(text) RETURNS text
448+ LANGUAGE plpythonu IMMUTABLE STRICT
449+ AS $_$
450+ import re
451+
452+ # I think this method would be more robust if we used a real
453+ # tokenizer and parser to generate the query string, but we need
454+ # something suitable for use as a stored procedure which currently
455+ # means no external dependancies.
456+
457+ # Convert to Unicode
458+ query = args[0].decode('utf8')
459+ ## plpy.debug('1 query is %s' % repr(query))
460+
461+ # Replace tsquery operators with ' '. '<' begins all the phrase
462+ # search operators, and a standalone '>' is fine.
463+ query = re.sub('[|&!<]', ' ', query)
464+
465+ # Normalize whitespace
466+ query = re.sub("(?u)\s+"," ", query)
467+
468+ # Convert AND, OR, NOT to tsearch2 punctuation
469+ query = re.sub(r"(?u)\bAND\b", "&", query)
470+ query = re.sub(r"(?u)\bOR\b", "|", query)
471+ query = re.sub(r"(?u)\bNOT\b", " !", query)
472+ ## plpy.debug('2 query is %s' % repr(query))
473+
474+ # Deal with unwanted punctuation.
475+ # ':' is used in queries to specify a weight of a word.
476+ # '\' is treated differently in to_tsvector() and to_tsquery().
477+ punctuation = r'[:\\]'
478+ query = re.sub(r"(?u)%s+" % (punctuation,), " ", query)
479+ ## plpy.debug('3 query is %s' % repr(query))
480+
481+ # Now that we have handle case sensitive booleans, convert to lowercase
482+ query = query.lower()
483+
484+ # Remove unpartnered bracket on the left and right
485+ query = re.sub(r"(?ux) ^ ( [^(]* ) \)", r"(\1)", query)
486+ query = re.sub(r"(?ux) \( ( [^)]* ) $", r"(\1)", query)
487+
488+ # Remove spurious brackets
489+ query = re.sub(r"(?u)\(([^\&\|]*?)\)", r" \1 ", query)
490+ ## plpy.debug('5 query is %s' % repr(query))
491+
492+ # Insert & between tokens without an existing boolean operator
493+ # ( not proceeded by (|&!
494+ query = re.sub(r"(?u)(?<![\(\|\&\!])\s*\(", "&(", query)
495+ ## plpy.debug('6 query is %s' % repr(query))
496+ # ) not followed by )|&
497+ query = re.sub(r"(?u)\)(?!\s*(\)|\||\&|\s*$))", ")&", query)
498+ ## plpy.debug('6.1 query is %s' % repr(query))
499+ # Whitespace not proceded by (|&! not followed by &|
500+ query = re.sub(r"(?u)(?<![\(\|\&\!\s])\s+(?![\&\|\s])", "&", query)
501+ ## plpy.debug('7 query is %s' % repr(query))
502+
503+ # Detect and repair syntax errors - we are lenient because
504+ # this input is generally from users.
505+
506+ # Fix unbalanced brackets
507+ openings = query.count("(")
508+ closings = query.count(")")
509+ if openings > closings:
510+ query = query + " ) "*(openings-closings)
511+ elif closings > openings:
512+ query = " ( "*(closings-openings) + query
513+ ## plpy.debug('8 query is %s' % repr(query))
514+
515+ # Strip ' character that do not have letters on both sides
516+ query = re.sub(r"(?u)((?<!\w)'|'(?!\w))", "", query)
517+
518+ # Brackets containing nothing but whitespace and booleans, recursive
519+ last = ""
520+ while last != query:
521+ last = query
522+ query = re.sub(r"(?u)\([\s\&\|\!]*\)", "", query)
523+ ## plpy.debug('9 query is %s' % repr(query))
524+
525+ # An & or | following a (
526+ query = re.sub(r"(?u)(?<=\()[\&\|\s]+", "", query)
527+ ## plpy.debug('10 query is %s' % repr(query))
528+
529+ # An &, | or ! immediatly before a )
530+ query = re.sub(r"(?u)[\&\|\!\s]*[\&\|\!]+\s*(?=\))", "", query)
531+ ## plpy.debug('11 query is %s' % repr(query))
532+
533+ # An &,| or ! followed by another boolean.
534+ query = re.sub(r"(?ux) \s* ( [\&\|\!] ) [\s\&\|]+", r"\1", query)
535+ ## plpy.debug('12 query is %s' % repr(query))
536+
537+ # Leading & or |
538+ query = re.sub(r"(?u)^[\s\&\|]+", "", query)
539+ ## plpy.debug('13 query is %s' % repr(query))
540+
541+ # Trailing &, | or !
542+ query = re.sub(r"(?u)[\&\|\!\s]+$", "", query)
543+ ## plpy.debug('14 query is %s' % repr(query))
544+
545+ # If we have nothing but whitespace and tsearch2 operators,
546+ # return NULL.
547+ if re.search(r"(?u)^[\&\|\!\s\(\)]*$", query) is not None:
548+ return None
549+
550+ # Convert back to UTF-8
551+ query = query.encode('utf8')
552+ ## plpy.debug('15 query is %s' % repr(query))
553+
554+ return query or None
555+ $_$;
556+
557+
558+CREATE FUNCTION public.accessartifact_denorm_to_artifacts(artifact_id integer) RETURNS void
559+ LANGUAGE plpgsql
560+ AS $$
561+DECLARE
562+ artifact_row accessartifact%ROWTYPE;
563+BEGIN
564+ SELECT * INTO artifact_row FROM accessartifact WHERE id = artifact_id;
565+ IF artifact_row.bug IS NOT NULL THEN
566+ PERFORM bug_flatten_access(artifact_row.bug);
567+ END IF;
568+ IF artifact_row.branch IS NOT NULL THEN
569+ PERFORM branch_denorm_access(artifact_row.branch);
570+ END IF;
571+ IF artifact_row.gitrepository IS NOT NULL THEN
572+ PERFORM gitrepository_denorm_access(artifact_row.gitrepository);
573+ END IF;
574+ IF artifact_row.specification IS NOT NULL THEN
575+ PERFORM specification_denorm_access(artifact_row.specification);
576+ END IF;
577+ RETURN;
578+END;
579+$$;
580+
581+
582+COMMENT ON FUNCTION public.accessartifact_denorm_to_artifacts(artifact_id integer) IS 'Denormalize the policy access and artifact grants to bugs, branches, Git repositories, and specifications.';
583+
584+
585+CREATE FUNCTION public.accessartifact_maintain_denorm_to_artifacts_trig() RETURNS trigger
586+ LANGUAGE plpgsql
587+ AS $$
588+BEGIN
589+ IF TG_OP = 'INSERT' THEN
590+ PERFORM accessartifact_denorm_to_artifacts(NEW.artifact);
591+ ELSIF TG_OP = 'UPDATE' THEN
592+ PERFORM accessartifact_denorm_to_artifacts(NEW.artifact);
593+ IF OLD.artifact != NEW.artifact THEN
594+ PERFORM accessartifact_denorm_to_artifacts(OLD.artifact);
595+ END IF;
596+ ELSIF TG_OP = 'DELETE' THEN
597+ PERFORM accessartifact_denorm_to_artifacts(OLD.artifact);
598+ END IF;
599+ RETURN NULL;
600+END;
601+$$;
602+
603+
604+CREATE FUNCTION public.accessartifactgrant_maintain_accesspolicygrantflat_trig() RETURNS trigger
605+ LANGUAGE plpgsql SECURITY DEFINER
606+ SET search_path TO 'public'
607+ AS $$
608+BEGIN
609+ IF TG_OP = 'INSERT' THEN
610+ INSERT INTO AccessPolicyGrantFlat
611+ (policy, artifact, grantee)
612+ SELECT policy, NEW.artifact, NEW.grantee
613+ FROM AccessPolicyArtifact WHERE artifact = NEW.artifact;
614+ ELSIF TG_OP = 'UPDATE' THEN
615+ IF NEW.artifact != OLD.artifact OR NEW.grantee != OLD.grantee THEN
616+ UPDATE AccessPolicyGrantFlat
617+ SET artifact=NEW.artifact, grantee=NEW.grantee
618+ WHERE artifact = OLD.artifact AND grantee = OLD.grantee;
619+ END IF;
620+ ELSIF TG_OP = 'DELETE' THEN
621+ DELETE FROM AccessPolicyGrantFlat
622+ WHERE artifact = OLD.artifact AND grantee = OLD.grantee;
623+ END IF;
624+ RETURN NULL;
625+END;
626+$$;
627+
628+
629+CREATE FUNCTION public.accesspolicyartifact_maintain_accesspolicyartifactflat_trig() RETURNS trigger
630+ LANGUAGE plpgsql SECURITY DEFINER
631+ SET search_path TO 'public'
632+ AS $$
633+BEGIN
634+ IF TG_OP = 'INSERT' THEN
635+ INSERT INTO AccessPolicyGrantFlat
636+ (policy, artifact, grantee)
637+ SELECT NEW.policy, NEW.artifact, grantee
638+ FROM AccessArtifactGrant WHERE artifact = NEW.artifact;
639+ ELSIF TG_OP = 'UPDATE' THEN
640+ IF NEW.policy != OLD.policy OR NEW.artifact != OLD.artifact THEN
641+ UPDATE AccessPolicyGrantFlat
642+ SET policy=NEW.policy, artifact=NEW.artifact
643+ WHERE policy = OLD.policy AND artifact = OLD.artifact;
644+ END IF;
645+ ELSIF TG_OP = 'DELETE' THEN
646+ DELETE FROM AccessPolicyGrantFlat
647+ WHERE policy = OLD.policy AND artifact = OLD.artifact;
648+ END IF;
649+ RETURN NULL;
650+END;
651+$$;
652+
653+
654+CREATE FUNCTION public.accesspolicygrant_maintain_accesspolicygrantflat_trig() RETURNS trigger
655+ LANGUAGE plpgsql SECURITY DEFINER
656+ SET search_path TO 'public'
657+ AS $$
658+BEGIN
659+ IF TG_OP = 'INSERT' THEN
660+ INSERT INTO AccessPolicyGrantFlat
661+ (policy, grantee) VALUES (NEW.policy, NEW.grantee);
662+ ELSIF TG_OP = 'UPDATE' THEN
663+ IF NEW.policy != OLD.policy OR NEW.grantee != OLD.grantee THEN
664+ UPDATE AccessPolicyGrantFlat
665+ SET policy=NEW.policy, grantee=NEW.grantee
666+ WHERE
667+ policy = OLD.policy
668+ AND grantee = OLD.grantee
669+ AND artifact IS NULL;
670+ END IF;
671+ ELSIF TG_OP = 'DELETE' THEN
672+ DELETE FROM AccessPolicyGrantFlat
673+ WHERE
674+ policy = OLD.policy
675+ AND grantee = OLD.grantee
676+ AND artifact IS NULL;
677+ END IF;
678+ RETURN NULL;
679+END;
680+$$;
681+
682+
683+CREATE FUNCTION public.activity() RETURNS SETOF pg_stat_activity
684+ LANGUAGE plpgsql SECURITY DEFINER
685+ SET search_path TO 'public'
686+ AS $$
687+DECLARE
688+ a pg_stat_activity%ROWTYPE;
689+BEGIN
690+ IF EXISTS (
691+ SELECT 1 FROM pg_attribute WHERE
692+ attrelid =
693+ (SELECT oid FROM pg_class
694+ WHERE relname = 'pg_stat_activity')
695+ AND attname = 'backend_type') THEN
696+ -- >= 10
697+ RETURN QUERY SELECT
698+ datid, datname, pid, usesysid, usename, application_name,
699+ client_addr, client_hostname, client_port, backend_start,
700+ xact_start, query_start, state_change, wait_event_type,
701+ wait_event, state, backend_xid, backend_xmin, backend_type,
702+ CASE
703+ WHEN query LIKE '<IDLE>%'
704+ OR query LIKE 'autovacuum:%'
705+ THEN query
706+ ELSE
707+ '<HIDDEN>'
708+ END AS query
709+ FROM pg_catalog.pg_stat_activity;
710+ ELSIF EXISTS (
711+ SELECT 1 FROM pg_attribute WHERE
712+ attrelid =
713+ (SELECT oid FROM pg_class
714+ WHERE relname = 'pg_stat_activity')
715+ AND attname = 'wait_event_type') THEN
716+ -- >= 9.6
717+ RETURN QUERY SELECT
718+ datid, datname, pid, usesysid, usename, application_name,
719+ client_addr, client_hostname, client_port, backend_start,
720+ xact_start, query_start, state_change, wait_event_type,
721+ wait_event, state, backend_xid, backend_xmin,
722+ CASE
723+ WHEN query LIKE '<IDLE>%'
724+ OR query LIKE 'autovacuum:%'
725+ THEN query
726+ ELSE
727+ '<HIDDEN>'
728+ END AS query
729+ FROM pg_catalog.pg_stat_activity;
730+ ELSIF EXISTS (
731+ SELECT 1 FROM pg_attribute WHERE
732+ attrelid =
733+ (SELECT oid FROM pg_class
734+ WHERE relname = 'pg_stat_activity')
735+ AND attname = 'backend_xid') THEN
736+ -- >= 9.4
737+ RETURN QUERY SELECT
738+ datid, datname, pid, usesysid, usename, application_name,
739+ client_addr, client_hostname, client_port, backend_start,
740+ xact_start, query_start, state_change, waiting, state,
741+ backend_xid, backend_xmin,
742+ CASE
743+ WHEN query LIKE '<IDLE>%'
744+ OR query LIKE 'autovacuum:%'
745+ THEN query
746+ ELSE
747+ '<HIDDEN>'
748+ END AS query
749+ FROM pg_catalog.pg_stat_activity;
750+ ELSE
751+ -- >= 9.2; anything older is unsupported
752+ RETURN QUERY SELECT
753+ datid, datname, pid, usesysid, usename, application_name,
754+ client_addr, client_hostname, client_port, backend_start,
755+ xact_start, query_start, state_change, waiting, state,
756+ CASE
757+ WHEN query LIKE '<IDLE>%'
758+ OR query LIKE 'autovacuum:%'
759+ THEN query
760+ ELSE
761+ '<HIDDEN>'
762+ END AS query
763+ FROM pg_catalog.pg_stat_activity;
764+ END IF;
765+END;
766+$$;
767+
768+
769+COMMENT ON FUNCTION public.activity() IS 'SECURITY DEFINER wrapper around pg_stat_activity allowing unprivileged users to access most of its information.';
770+
771+
772+CREATE FUNCTION public.add_test_openid_identifier(account_ integer) RETURNS boolean
773+ LANGUAGE plpgsql SECURITY DEFINER
774+ SET search_path TO 'public'
775 AS $$
776 BEGIN
777 -- The generated OpenIdIdentifier is not a valid OpenId Identity URL
778@@ -161,10 +439,10 @@
779 $$;
780
781
782-COMMENT ON FUNCTION add_test_openid_identifier(account_ integer) IS 'Add an OpenIdIdentifier to an account that can be used to login in the test environment. These identifiers are not usable on production or staging.';
783-
784-
785-CREATE FUNCTION assert_patch_applied(major integer, minor integer, patch integer) RETURNS boolean
786+COMMENT ON FUNCTION public.add_test_openid_identifier(account_ integer) IS 'Add an OpenIdIdentifier to an account that can be used to login in the test environment. These identifiers are not usable on production or staging.';
787+
788+
789+CREATE FUNCTION public.assert_patch_applied(major integer, minor integer, patch integer) RETURNS boolean
790 LANGUAGE plpythonu STABLE
791 AS $$
792 rv = plpy.execute("""
793@@ -179,39 +457,98 @@
794 $$;
795
796
797-COMMENT ON FUNCTION assert_patch_applied(major integer, minor integer, patch integer) IS 'Raise an exception if the given database patch has not been applied.';
798-
799-
800-CREATE FUNCTION bug_maintain_bug_summary() RETURNS trigger
801+COMMENT ON FUNCTION public.assert_patch_applied(major integer, minor integer, patch integer) IS 'Raise an exception if the given database patch has not been applied.';
802+
803+
804+CREATE FUNCTION public.branch_denorm_access(branch_id integer) RETURNS void
805+ LANGUAGE sql SECURITY DEFINER
806+ SET search_path TO 'public'
807+ AS $_$
808+ UPDATE branch
809+ SET access_policy = policies[1], access_grants = grants
810+ FROM
811+ build_access_cache(
812+ (SELECT id FROM accessartifact WHERE branch = $1),
813+ (SELECT information_type FROM branch WHERE id = $1))
814+ AS (policies integer[], grants integer[])
815+ WHERE id = $1;
816+$_$;
817+
818+
819+CREATE FUNCTION public.branch_maintain_access_cache_trig() RETURNS trigger
820+ LANGUAGE plpgsql
821+ AS $$
822+BEGIN
823+ PERFORM branch_denorm_access(NEW.id);
824+ RETURN NULL;
825+END;
826+$$;
827+
828+
829+CREATE FUNCTION public.bug_build_access_cache(bug_id integer, information_type integer) RETURNS record
830+ LANGUAGE sql
831+ AS $_$
832+ SELECT build_access_cache(
833+ (SELECT id FROM accessartifact WHERE bug = $1), $2);
834+$_$;
835+
836+
837+COMMENT ON FUNCTION public.bug_build_access_cache(bug_id integer, information_type integer) IS 'Build an access cache for the given bug. Returns ({AccessPolicyArtifact.policy}, {AccessArtifactGrant.grantee}) for private bugs, or (NULL, NULL) for public ones.';
838+
839+
840+CREATE FUNCTION public.bug_flatten_access(bug_id integer) RETURNS void
841+ LANGUAGE sql SECURITY DEFINER
842+ SET search_path TO 'public'
843+ AS $_$
844+ UPDATE bugtaskflat
845+ SET access_policies = policies, access_grants = grants
846+ FROM
847+ build_access_cache(
848+ (SELECT id FROM accessartifact WHERE bug = $1),
849+ (SELECT information_type FROM bug WHERE id = $1))
850+ AS (policies integer[], grants integer[])
851+ WHERE bug = $1;
852+$_$;
853+
854+
855+COMMENT ON FUNCTION public.bug_flatten_access(bug_id integer) IS 'Recalculate the access cache on a bug''s flattened tasks.';
856+
857+
858+CREATE FUNCTION public.bug_maintain_bugtaskflat_trig() RETURNS trigger
859 LANGUAGE plpgsql SECURITY DEFINER
860- SET search_path TO public
861+ SET search_path TO 'public'
862 AS $$
863 BEGIN
864- -- There is no INSERT logic, as a bug will not have any summary
865- -- information until BugTask rows have been attached.
866- IF TG_OP = 'UPDATE' THEN
867- IF OLD.duplicateof IS DISTINCT FROM NEW.duplicateof
868- OR OLD.private IS DISTINCT FROM NEW.private
869- OR (OLD.latest_patch_uploaded IS NULL)
870- <> (NEW.latest_patch_uploaded IS NULL) THEN
871- PERFORM unsummarise_bug(OLD);
872- PERFORM summarise_bug(NEW);
873- END IF;
874-
875- ELSIF TG_OP = 'DELETE' THEN
876- PERFORM unsummarise_bug(OLD);
877- END IF;
878-
879- PERFORM bug_summary_flush_temp_journal();
880- RETURN NULL; -- Ignored - this is an AFTER trigger
881+ IF (
882+ NEW.duplicateof IS DISTINCT FROM OLD.duplicateof
883+ OR NEW.owner IS DISTINCT FROM OLD.owner
884+ OR NEW.fti IS DISTINCT FROM OLD.fti
885+ OR NEW.information_type IS DISTINCT FROM OLD.information_type
886+ OR NEW.date_last_updated IS DISTINCT FROM OLD.date_last_updated
887+ OR NEW.heat IS DISTINCT FROM OLD.heat
888+ OR NEW.latest_patch_uploaded IS DISTINCT FROM
889+ OLD.latest_patch_uploaded) THEN
890+ UPDATE bugtaskflat
891+ SET
892+ duplicateof = NEW.duplicateof,
893+ bug_owner = NEW.owner,
894+ fti = NEW.fti,
895+ information_type = NEW.information_type,
896+ date_last_updated = NEW.date_last_updated,
897+ heat = NEW.heat,
898+ latest_patch_uploaded = NEW.latest_patch_uploaded
899+ WHERE bug = OLD.id;
900+ END IF;
901+
902+ IF NEW.information_type IS DISTINCT FROM OLD.information_type THEN
903+ PERFORM bug_flatten_access(OLD.id);
904+ END IF;
905+ RETURN NULL;
906 END;
907 $$;
908
909
910-COMMENT ON FUNCTION bug_maintain_bug_summary() IS 'AFTER trigger on bug maintaining the bugs summaries in bugsummary.';
911-
912-
913-CREATE FUNCTION valid_bug_name(text) RETURNS boolean
914+CREATE FUNCTION public.valid_bug_name(text) RETURNS boolean
915 LANGUAGE plpythonu IMMUTABLE STRICT
916 AS $_$
917 import re
918@@ -223,7 +560,7 @@
919 $_$;
920
921
922-COMMENT ON FUNCTION valid_bug_name(text) IS 'validate a bug name
923+COMMENT ON FUNCTION public.valid_bug_name(text) IS 'validate a bug name
924
925 As per valid_name, except numeric-only names are not allowed (including
926 names that look like floats).';
927@@ -233,7 +570,7 @@
928
929 SET default_with_oids = false;
930
931-CREATE TABLE bug (
932+CREATE TABLE public.bug (
933 id integer NOT NULL,
934 datecreated timestamp without time zone DEFAULT timezone('UTC'::text, ('now'::text)::timestamp(6) with time zone) NOT NULL,
935 name text,
936@@ -241,9 +578,7 @@
937 description text NOT NULL,
938 owner integer NOT NULL,
939 duplicateof integer,
940- fti ts2.tsvector,
941- private boolean DEFAULT false NOT NULL,
942- security_related boolean DEFAULT false NOT NULL,
943+ fti public.ts2_tsvector,
944 date_last_updated timestamp without time zone DEFAULT timezone('UTC'::text, now()) NOT NULL,
945 date_made_private timestamp without time zone,
946 who_made_private integer,
947@@ -255,60 +590,57 @@
948 heat integer DEFAULT 0 NOT NULL,
949 heat_last_updated timestamp without time zone,
950 latest_patch_uploaded timestamp without time zone,
951- access_policy integer,
952+ information_type integer NOT NULL,
953 CONSTRAINT notduplicateofself CHECK ((NOT (id = duplicateof))),
954 CONSTRAINT sane_description CHECK (((ltrim(description) <> ''::text) AND (char_length(description) <= 50000))),
955- CONSTRAINT valid_bug_name CHECK (valid_bug_name(name))
956+ CONSTRAINT valid_bug_name CHECK (public.valid_bug_name(name))
957 );
958
959
960-COMMENT ON TABLE bug IS 'A software bug that requires fixing. This particular bug may be linked to one or more products or source packages to identify the location(s) that this bug is found.';
961-
962-
963-COMMENT ON COLUMN bug.name IS 'A lowercase name uniquely identifying the bug';
964-
965-
966-COMMENT ON COLUMN bug.description IS 'A detailed description of the bug. Initially this will be set to the contents of the initial email or bug filing comment, but later it can be edited to give a more accurate description of the bug itself rather than the symptoms observed by the reporter.';
967-
968-
969-COMMENT ON COLUMN bug.private IS 'Is this bug private? If so, only explicit subscribers will be able to see it';
970-
971-
972-COMMENT ON COLUMN bug.security_related IS 'Is this bug a security issue?';
973-
974-
975-COMMENT ON COLUMN bug.date_last_message IS 'When the last BugMessage was attached to this Bug. Maintained by a trigger on the BugMessage table.';
976-
977-
978-COMMENT ON COLUMN bug.number_of_duplicates IS 'The number of bugs marked as duplicates of this bug, populated by a trigger after setting the duplicateof of bugs.';
979-
980-
981-COMMENT ON COLUMN bug.message_count IS 'The number of messages (currently just comments) on this bugbug, maintained by the set_bug_message_count_t trigger.';
982-
983-
984-COMMENT ON COLUMN bug.users_affected_count IS 'The number of users affected by this bug, maintained by the set_bug_users_affected_count_t trigger.';
985-
986-
987-COMMENT ON COLUMN bug.heat IS 'The relevance of this bug. This value is computed periodically using bug_affects_person and other bug values.';
988-
989-
990-COMMENT ON COLUMN bug.heat_last_updated IS 'The time this bug''s heat was last updated, or NULL if the heat has never yet been updated.';
991-
992-
993-COMMENT ON COLUMN bug.latest_patch_uploaded IS 'The time when the most recent patch has been attached to this bug or NULL if no patches are attached';
994-
995-
996-CREATE FUNCTION bug_row(bug_id integer) RETURNS bug
997+COMMENT ON TABLE public.bug IS 'A software bug that requires fixing. This particular bug may be linked to one or more products or source packages to identify the location(s) that this bug is found.';
998+
999+
1000+COMMENT ON COLUMN public.bug.name IS 'A lowercase name uniquely identifying the bug';
1001+
1002+
1003+COMMENT ON COLUMN public.bug.description IS 'A detailed description of the bug. Initially this will be set to the contents of the initial email or bug filing comment, but later it can be edited to give a more accurate description of the bug itself rather than the symptoms observed by the reporter.';
1004+
1005+
1006+COMMENT ON COLUMN public.bug.date_last_message IS 'When the last BugMessage was attached to this Bug. Maintained by a trigger on the BugMessage table.';
1007+
1008+
1009+COMMENT ON COLUMN public.bug.number_of_duplicates IS 'The number of bugs marked as duplicates of this bug, populated by a trigger after setting the duplicateof of bugs.';
1010+
1011+
1012+COMMENT ON COLUMN public.bug.message_count IS 'The number of messages (currently just comments) on this bugbug, maintained by the set_bug_message_count_t trigger.';
1013+
1014+
1015+COMMENT ON COLUMN public.bug.users_affected_count IS 'The number of users affected by this bug, maintained by the set_bug_users_affected_count_t trigger.';
1016+
1017+
1018+COMMENT ON COLUMN public.bug.heat IS 'The relevance of this bug. This value is computed periodically using bug_affects_person and other bug values.';
1019+
1020+
1021+COMMENT ON COLUMN public.bug.heat_last_updated IS 'The time this bug''s heat was last updated, or NULL if the heat has never yet been updated.';
1022+
1023+
1024+COMMENT ON COLUMN public.bug.latest_patch_uploaded IS 'The time when the most recent patch has been attached to this bug or NULL if no patches are attached';
1025+
1026+
1027+COMMENT ON COLUMN public.bug.information_type IS 'Enum describing what type of information is stored, such as type of private or security related data, and used to determine how to apply an access policy.';
1028+
1029+
1030+CREATE FUNCTION public.bug_row(bug_id integer) RETURNS public.bug
1031 LANGUAGE sql STABLE
1032 AS $_$
1033 SELECT * FROM Bug WHERE id=$1;
1034 $_$;
1035
1036
1037-COMMENT ON FUNCTION bug_row(bug_id integer) IS 'Helper for manually testing functions requiring a bug row as input. eg. SELECT * FROM bugsummary_tags(bug_row(1))';
1038-
1039-
1040-CREATE TABLE bugsummary (
1041+COMMENT ON FUNCTION public.bug_row(bug_id integer) IS 'Helper for manually testing functions requiring a bug row as input. eg. SELECT * FROM bugsummary_tags(bug_row(1))';
1042+
1043+
1044+CREATE TABLE public.bugsummary (
1045 id integer NOT NULL,
1046 count integer DEFAULT 0 NOT NULL,
1047 product integer,
1048@@ -322,12 +654,28 @@
1049 milestone integer,
1050 importance integer NOT NULL,
1051 has_patch boolean NOT NULL,
1052- fixed_upstream boolean NOT NULL,
1053- CONSTRAINT bugtask_assignment_checks CHECK (CASE WHEN (product IS NOT NULL) THEN ((((productseries IS NULL) AND (distribution IS NULL)) AND (distroseries IS NULL)) AND (sourcepackagename IS NULL)) WHEN (productseries IS NOT NULL) THEN (((distribution IS NULL) AND (distroseries IS NULL)) AND (sourcepackagename IS NULL)) WHEN (distribution IS NOT NULL) THEN (distroseries IS NULL) WHEN (distroseries IS NOT NULL) THEN true ELSE false END)
1054+ access_policy integer,
1055+ CONSTRAINT bugtask_assignment_checks CHECK (
1056+CASE
1057+ WHEN (product IS NOT NULL) THEN ((((productseries IS NULL) AND (distribution IS NULL)) AND (distroseries IS NULL)) AND (sourcepackagename IS NULL))
1058+ WHEN (productseries IS NOT NULL) THEN (((distribution IS NULL) AND (distroseries IS NULL)) AND (sourcepackagename IS NULL))
1059+ WHEN (distribution IS NOT NULL) THEN (distroseries IS NULL)
1060+ WHEN (distroseries IS NOT NULL) THEN true
1061+ ELSE false
1062+END)
1063 );
1064
1065
1066-CREATE FUNCTION bug_summary_dec(bugsummary) RETURNS void
1067+COMMENT ON TABLE public.bugsummary IS 'A fact table for bug metadata aggregate queries. Each row represents the number of bugs that are in the system addressed by all the dimensions (e.g. product or productseries etc). ';
1068+
1069+
1070+COMMENT ON COLUMN public.bugsummary.sourcepackagename IS 'The sourcepackagename for the aggregate. Counting bugs in a distribution/distroseries requires selecting all rows by sourcepackagename. If this is too slow, add the bug to the NULL row and select with sourcepackagename is NULL to exclude them from the calculations';
1071+
1072+
1073+COMMENT ON COLUMN public.bugsummary.milestone IS 'A milestone present on the bug. All bugs are also aggregated with a NULL entry for milestone to permit querying totals (because the milestone figures cannot be summed as many milestones can be on a single bug)';
1074+
1075+
1076+CREATE FUNCTION public.bug_summary_dec(public.bugsummary) RETURNS void
1077 LANGUAGE sql
1078 AS $_$
1079 -- We own the row reference, so in the absence of bugs this cannot
1080@@ -353,34 +701,47 @@
1081 OR milestone = $1.milestone)
1082 AND importance = $1.importance
1083 AND has_patch = $1.has_patch
1084- AND fixed_upstream = $1.fixed_upstream;
1085+ AND access_policy IS NOT DISTINCT FROM $1.access_policy;
1086 $_$;
1087
1088
1089-COMMENT ON FUNCTION bug_summary_dec(bugsummary) IS 'UPSERT into bugsummary incrementing one row';
1090-
1091-
1092-CREATE FUNCTION bug_summary_flush_temp_journal() RETURNS void
1093+COMMENT ON FUNCTION public.bug_summary_dec(public.bugsummary) IS 'UPSERT into bugsummary incrementing one row';
1094+
1095+
1096+CREATE FUNCTION public.bug_summary_flush_temp_journal() RETURNS void
1097 LANGUAGE plpgsql
1098 AS $$
1099 DECLARE
1100 d bugsummary%ROWTYPE;
1101 BEGIN
1102- -- may get called even though no summaries were made (for simplicity in the
1103- -- callers)
1104+ -- May get called even though no summaries were made (for simplicity in the
1105+ -- callers). We sum the rows here to minimise the number of inserts
1106+ -- into the persistent journal, as it's reasonably likely that we'll
1107+ -- have -1s and +1s cancelling each other out.
1108 PERFORM ensure_bugsummary_temp_journal();
1109- FOR d IN SELECT * FROM bugsummary_temp_journal LOOP
1110- PERFORM bugsummary_journal_ins(d);
1111- END LOOP;
1112+ INSERT INTO BugSummaryJournal(
1113+ count, product, productseries, distribution,
1114+ distroseries, sourcepackagename, viewed_by, tag,
1115+ status, milestone, importance, has_patch, access_policy)
1116+ SELECT
1117+ SUM(count), product, productseries, distribution,
1118+ distroseries, sourcepackagename, viewed_by, tag,
1119+ status, milestone, importance, has_patch, access_policy
1120+ FROM bugsummary_temp_journal
1121+ GROUP BY
1122+ product, productseries, distribution,
1123+ distroseries, sourcepackagename, viewed_by, tag,
1124+ status, milestone, importance, has_patch, access_policy
1125+ HAVING SUM(count) != 0;
1126 TRUNCATE bugsummary_temp_journal;
1127 END;
1128 $$;
1129
1130
1131-COMMENT ON FUNCTION bug_summary_flush_temp_journal() IS 'flush the temporary bugsummary journal into the bugsummary table';
1132-
1133-
1134-CREATE FUNCTION bug_summary_inc(d bugsummary) RETURNS void
1135+COMMENT ON FUNCTION public.bug_summary_flush_temp_journal() IS 'flush the temporary bugsummary journal into the bugsummary table';
1136+
1137+
1138+CREATE FUNCTION public.bug_summary_inc(d public.bugsummary) RETURNS void
1139 LANGUAGE plpgsql
1140 AS $_$
1141 BEGIN
1142@@ -408,7 +769,7 @@
1143 OR milestone = $1.milestone)
1144 AND importance = $1.importance
1145 AND has_patch = $1.has_patch
1146- AND fixed_upstream = $1.fixed_upstream;
1147+ AND access_policy IS NOT DISTINCT FROM $1.access_policy;
1148 IF found THEN
1149 RETURN;
1150 END IF;
1151@@ -419,13 +780,12 @@
1152 INSERT INTO BugSummary(
1153 count, product, productseries, distribution,
1154 distroseries, sourcepackagename, viewed_by, tag,
1155- status, milestone,
1156- importance, has_patch, fixed_upstream)
1157+ status, milestone, importance, has_patch, access_policy)
1158 VALUES (
1159 d.count, d.product, d.productseries, d.distribution,
1160 d.distroseries, d.sourcepackagename, d.viewed_by, d.tag,
1161- d.status, d.milestone,
1162- d.importance, d.has_patch, d.fixed_upstream);
1163+ d.status, d.milestone, d.importance, d.has_patch,
1164+ d.access_policy);
1165 RETURN;
1166 EXCEPTION WHEN unique_violation THEN
1167 -- do nothing, and loop to try the UPDATE again
1168@@ -435,48 +795,12 @@
1169 $_$;
1170
1171
1172-COMMENT ON FUNCTION bug_summary_inc(d bugsummary) IS 'UPSERT into bugsummary incrementing one row';
1173-
1174-
1175-CREATE FUNCTION bug_summary_temp_journal_ins(d bugsummary) RETURNS void
1176- LANGUAGE plpgsql
1177- AS $$
1178-BEGIN
1179- INSERT INTO BugSummary_Temp_Journal(
1180- count, product, productseries, distribution,
1181- distroseries, sourcepackagename, viewed_by, tag,
1182- status, milestone, importance, has_patch, fixed_upstream)
1183- VALUES (
1184- d.count, d.product, d.productseries, d.distribution,
1185- d.distroseries, d.sourcepackagename, d.viewed_by, d.tag,
1186- d.status, d.milestone, d.importance, d.has_patch, d.fixed_upstream);
1187- RETURN;
1188-END;
1189-$$;
1190-
1191-
1192-COMMENT ON FUNCTION bug_summary_temp_journal_ins(d bugsummary) IS 'Insert a BugSummary into the temporary journal';
1193-
1194-
1195-CREATE FUNCTION bug_update_heat_copy_to_bugtask() RETURNS trigger
1196- LANGUAGE plpgsql SECURITY DEFINER
1197- SET search_path TO public
1198- AS $$
1199-BEGIN
1200- IF NEW.heat != OLD.heat THEN
1201- UPDATE bugtask SET heat=NEW.heat WHERE bugtask.bug=NEW.id;
1202- END IF;
1203- RETURN NULL; -- Ignored - this is an AFTER trigger
1204-END;
1205-$$;
1206-
1207-
1208-COMMENT ON FUNCTION bug_update_heat_copy_to_bugtask() IS 'Copies bug heat to bugtasks when the bug is changed. Runs on UPDATE only because INSERTs do not have bugtasks at the point of insertion.';
1209-
1210-
1211-CREATE FUNCTION bug_update_latest_patch_uploaded(integer) RETURNS void
1212- LANGUAGE plpgsql SECURITY DEFINER
1213- SET search_path TO public
1214+COMMENT ON FUNCTION public.bug_summary_inc(d public.bugsummary) IS 'UPSERT into bugsummary incrementing one row';
1215+
1216+
1217+CREATE FUNCTION public.bug_update_latest_patch_uploaded(integer) RETURNS void
1218+ LANGUAGE plpgsql SECURITY DEFINER
1219+ SET search_path TO 'public'
1220 AS $_$
1221 BEGIN
1222 UPDATE bug SET latest_patch_uploaded =
1223@@ -490,9 +814,9 @@
1224 $_$;
1225
1226
1227-CREATE FUNCTION bug_update_latest_patch_uploaded_on_delete() RETURNS trigger
1228+CREATE FUNCTION public.bug_update_latest_patch_uploaded_on_delete() RETURNS trigger
1229 LANGUAGE plpgsql SECURITY DEFINER
1230- SET search_path TO public
1231+ SET search_path TO 'public'
1232 AS $$
1233 BEGIN
1234 PERFORM bug_update_latest_patch_uploaded(OLD.bug);
1235@@ -501,9 +825,9 @@
1236 $$;
1237
1238
1239-CREATE FUNCTION bug_update_latest_patch_uploaded_on_insert_update() RETURNS trigger
1240+CREATE FUNCTION public.bug_update_latest_patch_uploaded_on_insert_update() RETURNS trigger
1241 LANGUAGE plpgsql SECURITY DEFINER
1242- SET search_path TO public
1243+ SET search_path TO 'public'
1244 AS $$
1245 BEGIN
1246 PERFORM bug_update_latest_patch_uploaded(NEW.bug);
1247@@ -512,9 +836,9 @@
1248 $$;
1249
1250
1251-CREATE FUNCTION bugmessage_copy_owner_from_message() RETURNS trigger
1252+CREATE FUNCTION public.bugmessage_copy_owner_from_message() RETURNS trigger
1253 LANGUAGE plpgsql SECURITY DEFINER
1254- SET search_path TO public
1255+ SET search_path TO 'public'
1256 AS $$
1257 BEGIN
1258 IF TG_OP = 'INSERT' THEN
1259@@ -537,145 +861,98 @@
1260 $$;
1261
1262
1263-COMMENT ON FUNCTION bugmessage_copy_owner_from_message() IS 'Copies the message owner into bugmessage when bugmessage changes.';
1264-
1265-
1266-CREATE FUNCTION bugsubscription_maintain_bug_summary() RETURNS trigger
1267- LANGUAGE plpgsql SECURITY DEFINER
1268- SET search_path TO public
1269- AS $$
1270-BEGIN
1271- -- This trigger only works if we are inserting, updating or deleting
1272- -- a single row per statement.
1273- IF TG_OP = 'INSERT' THEN
1274- IF NOT (bug_row(NEW.bug)).private THEN
1275- -- Public subscriptions are not aggregated.
1276- RETURN NEW;
1277- END IF;
1278- IF TG_WHEN = 'BEFORE' THEN
1279- PERFORM unsummarise_bug(bug_row(NEW.bug));
1280- ELSE
1281- PERFORM summarise_bug(bug_row(NEW.bug));
1282- END IF;
1283- PERFORM bug_summary_flush_temp_journal();
1284- RETURN NEW;
1285- ELSIF TG_OP = 'DELETE' THEN
1286- IF NOT (bug_row(OLD.bug)).private THEN
1287- -- Public subscriptions are not aggregated.
1288- RETURN OLD;
1289- END IF;
1290- IF TG_WHEN = 'BEFORE' THEN
1291- PERFORM unsummarise_bug(bug_row(OLD.bug));
1292- ELSE
1293- PERFORM summarise_bug(bug_row(OLD.bug));
1294- END IF;
1295- PERFORM bug_summary_flush_temp_journal();
1296- RETURN OLD;
1297- ELSE
1298- IF (OLD.person IS DISTINCT FROM NEW.person
1299- OR OLD.bug IS DISTINCT FROM NEW.bug) THEN
1300- IF TG_WHEN = 'BEFORE' THEN
1301- IF (bug_row(OLD.bug)).private THEN
1302- -- Public subscriptions are not aggregated.
1303- PERFORM unsummarise_bug(bug_row(OLD.bug));
1304- END IF;
1305- IF OLD.bug <> NEW.bug AND (bug_row(NEW.bug)).private THEN
1306- -- Public subscriptions are not aggregated.
1307- PERFORM unsummarise_bug(bug_row(NEW.bug));
1308- END IF;
1309- ELSE
1310- IF (bug_row(OLD.bug)).private THEN
1311- -- Public subscriptions are not aggregated.
1312- PERFORM summarise_bug(bug_row(OLD.bug));
1313- END IF;
1314- IF OLD.bug <> NEW.bug AND (bug_row(NEW.bug)).private THEN
1315- -- Public subscriptions are not aggregated.
1316- PERFORM summarise_bug(bug_row(NEW.bug));
1317- END IF;
1318- END IF;
1319- END IF;
1320- PERFORM bug_summary_flush_temp_journal();
1321- RETURN NEW;
1322- END IF;
1323-END;
1324-$$;
1325-
1326-
1327-COMMENT ON FUNCTION bugsubscription_maintain_bug_summary() IS 'AFTER trigger on bugsubscription maintaining the bugs summaries in bugsummary.';
1328-
1329-
1330-CREATE FUNCTION bugsummary_journal_ins(d bugsummary) RETURNS void
1331- LANGUAGE plpgsql
1332- AS $$
1333-BEGIN
1334- IF d.count <> 0 THEN
1335- INSERT INTO BugSummaryJournal (
1336- count, product, productseries, distribution,
1337- distroseries, sourcepackagename, viewed_by, tag,
1338- status, milestone,
1339- importance, has_patch, fixed_upstream)
1340- VALUES (
1341- d.count, d.product, d.productseries, d.distribution,
1342- d.distroseries, d.sourcepackagename, d.viewed_by, d.tag,
1343- d.status, d.milestone,
1344- d.importance, d.has_patch, d.fixed_upstream);
1345- END IF;
1346-END;
1347-$$;
1348-
1349-
1350-COMMENT ON FUNCTION bugsummary_journal_ins(d bugsummary) IS 'Add an entry into BugSummaryJournal';
1351-
1352-
1353-CREATE FUNCTION bugsummary_locations(bug_row bug) RETURNS SETOF bugsummary
1354- LANGUAGE plpgsql
1355- AS $$
1356-BEGIN
1357- IF BUG_ROW.duplicateof IS NOT NULL THEN
1358+COMMENT ON FUNCTION public.bugmessage_copy_owner_from_message() IS 'Copies the message owner into bugmessage when bugmessage changes.';
1359+
1360+
1361+CREATE FUNCTION public.bugsummary_journal_bug(bug_row public.bug, _count integer) RETURNS void
1362+ LANGUAGE plpgsql
1363+ AS $$
1364+DECLARE
1365+ btf_row bugtaskflat%ROWTYPE;
1366+BEGIN
1367+ FOR btf_row IN SELECT * FROM bugtaskflat WHERE bug = bug_row.id
1368+ LOOP
1369+ PERFORM bugsummary_journal_bugtaskflat(btf_row, _count);
1370+ END LOOP;
1371+END;
1372+$$;
1373+
1374+
1375+CREATE TABLE public.bugtaskflat (
1376+ bugtask integer NOT NULL,
1377+ bug integer NOT NULL,
1378+ datecreated timestamp without time zone,
1379+ duplicateof integer,
1380+ bug_owner integer NOT NULL,
1381+ fti public.ts2_tsvector,
1382+ information_type integer NOT NULL,
1383+ date_last_updated timestamp without time zone NOT NULL,
1384+ heat integer NOT NULL,
1385+ product integer,
1386+ productseries integer,
1387+ distribution integer,
1388+ distroseries integer,
1389+ sourcepackagename integer,
1390+ status integer NOT NULL,
1391+ importance integer NOT NULL,
1392+ assignee integer,
1393+ milestone integer,
1394+ owner integer NOT NULL,
1395+ active boolean NOT NULL,
1396+ access_policies integer[],
1397+ access_grants integer[],
1398+ latest_patch_uploaded timestamp without time zone,
1399+ date_closed timestamp without time zone
1400+);
1401+
1402+
1403+CREATE FUNCTION public.bugsummary_journal_bugtaskflat(btf_row public.bugtaskflat, _count integer) RETURNS void
1404+ LANGUAGE plpgsql
1405+ AS $$
1406+BEGIN
1407+ PERFORM ensure_bugsummary_temp_journal();
1408+ INSERT INTO BugSummary_Temp_Journal(
1409+ count, product, productseries, distribution,
1410+ distroseries, sourcepackagename, viewed_by, tag,
1411+ status, milestone, importance, has_patch, access_policy)
1412+ SELECT
1413+ _count, product, productseries, distribution,
1414+ distroseries, sourcepackagename, viewed_by, tag,
1415+ status, milestone, importance, has_patch, access_policy
1416+ FROM bugsummary_locations(btf_row);
1417+END;
1418+$$;
1419+
1420+
1421+CREATE FUNCTION public.bugsummary_locations(btf_row public.bugtaskflat) RETURNS SETOF public.bugsummary
1422+ LANGUAGE plpgsql
1423+ AS $$
1424+BEGIN
1425+ IF btf_row.duplicateof IS NOT NULL THEN
1426 RETURN;
1427 END IF;
1428 RETURN QUERY
1429 SELECT
1430 CAST(NULL AS integer) AS id,
1431 CAST(1 AS integer) AS count,
1432- product, productseries, distribution, distroseries,
1433- sourcepackagename, person AS viewed_by, tag, status, milestone,
1434- importance,
1435- BUG_ROW.latest_patch_uploaded IS NOT NULL AS has_patch,
1436- (EXISTS (
1437- SELECT TRUE FROM BugTask AS RBT
1438- WHERE
1439- RBT.bug = tasks.bug
1440- -- This would just be 'RBT.id <> tasks.id', except
1441- -- that the records from tasks are summaries and not
1442- -- real bugtasks, and do not have an id.
1443- AND (RBT.product IS DISTINCT FROM tasks.product
1444- OR RBT.productseries
1445- IS DISTINCT FROM tasks.productseries
1446- OR RBT.distribution IS DISTINCT FROM tasks.distribution
1447- OR RBT.distroseries IS DISTINCT FROM tasks.distroseries
1448- OR RBT.sourcepackagename
1449- IS DISTINCT FROM tasks.sourcepackagename)
1450- -- Flagged as INVALID, FIXCOMMITTED or FIXRELEASED
1451- -- via a bugwatch, or FIXCOMMITTED or FIXRELEASED on
1452- -- the product.
1453- AND ((bugwatch IS NOT NULL AND status IN (17, 25, 30))
1454- OR (bugwatch IS NULL AND product IS NOT NULL
1455- AND status IN (25, 30))))
1456- )::boolean AS fixed_upstream
1457- FROM bugsummary_tasks(BUG_ROW) AS tasks
1458- JOIN bugsummary_tags(BUG_ROW) AS bug_tags ON TRUE
1459- LEFT OUTER JOIN bugsummary_viewers(BUG_ROW) AS bug_viewers ON TRUE;
1460+ bug_targets.product, bug_targets.productseries,
1461+ bug_targets.distribution, bug_targets.distroseries,
1462+ bug_targets.sourcepackagename,
1463+ bug_viewers.viewed_by, bug_tags.tag, btf_row.status,
1464+ btf_row.milestone, btf_row.importance,
1465+ btf_row.latest_patch_uploaded IS NOT NULL AS has_patch,
1466+ bug_viewers.access_policy
1467+ FROM
1468+ bugsummary_targets(btf_row) as bug_targets,
1469+ bugsummary_tags(btf_row) AS bug_tags,
1470+ bugsummary_viewers(btf_row) AS bug_viewers;
1471 END;
1472 $$;
1473
1474
1475-COMMENT ON FUNCTION bugsummary_locations(bug_row bug) IS 'Calculate what BugSummary rows should exist for a given Bug.';
1476-
1477-
1478-CREATE FUNCTION bugsummary_rollup_journal(batchsize integer DEFAULT NULL::integer) RETURNS void
1479+CREATE FUNCTION public.bugsummary_rollup_journal(batchsize integer DEFAULT NULL::integer) RETURNS void
1480 LANGUAGE plpgsql SECURITY DEFINER
1481- SET search_path TO public
1482+ SET search_path TO 'public'
1483 AS $$
1484 DECLARE
1485 d bugsummary%ROWTYPE;
1486@@ -710,13 +987,13 @@
1487 milestone,
1488 importance,
1489 has_patch,
1490- fixed_upstream
1491+ access_policy
1492 FROM BugSummaryJournal
1493 WHERE id <= max_id
1494 GROUP BY
1495 product, productseries, distribution, distroseries,
1496 sourcepackagename, viewed_by, tag, status, milestone,
1497- importance, has_patch, fixed_upstream
1498+ importance, has_patch, access_policy
1499 HAVING sum(count) <> 0
1500 LOOP
1501 IF d.count < 0 THEN
1502@@ -734,10 +1011,10 @@
1503 $$;
1504
1505
1506-COMMENT ON FUNCTION bugsummary_rollup_journal(batchsize integer) IS 'Collate and migrate rows from BugSummaryJournal to BugSummary';
1507-
1508-
1509-CREATE FUNCTION valid_name(text) RETURNS boolean
1510+COMMENT ON FUNCTION public.bugsummary_rollup_journal(batchsize integer) IS 'Collate and migrate rows from BugSummaryJournal to BugSummary';
1511+
1512+
1513+CREATE FUNCTION public.valid_name(text) RETURNS boolean
1514 LANGUAGE plpythonu IMMUTABLE STRICT
1515 AS $$
1516 import re
1517@@ -749,7 +1026,7 @@
1518 $$;
1519
1520
1521-COMMENT ON FUNCTION valid_name(text) IS 'validate a name.
1522+COMMENT ON FUNCTION public.valid_name(text) IS 'validate a name.
1523
1524 Names must contain only lowercase letters, numbers, ., & -. They
1525 must start with an alphanumeric. They are ASCII only. Names are useful
1526@@ -760,446 +1037,347 @@
1527 namespace conflict if URL traversal is possible by name as well as id.';
1528
1529
1530-CREATE TABLE bugtag (
1531+CREATE TABLE public.bugtag (
1532 id integer NOT NULL,
1533 bug integer NOT NULL,
1534 tag text NOT NULL,
1535- CONSTRAINT valid_tag CHECK (valid_name(tag))
1536-);
1537-
1538-
1539-COMMENT ON TABLE bugtag IS 'Attaches simple text tags to a bug.';
1540-
1541-
1542-COMMENT ON COLUMN bugtag.bug IS 'The bug the tags is attached to.';
1543-
1544-
1545-COMMENT ON COLUMN bugtag.tag IS 'The text representation of the tag.';
1546-
1547-
1548-CREATE FUNCTION bugsummary_tags(bug_row bug) RETURNS SETOF bugtag
1549- LANGUAGE sql STABLE
1550- AS $_$
1551- SELECT * FROM BugTag WHERE BugTag.bug = $1.id
1552- UNION ALL
1553- SELECT NULL::integer, $1.id, NULL::text;
1554-$_$;
1555-
1556-
1557-COMMENT ON FUNCTION bugsummary_tags(bug_row bug) IS 'Return (bug, tag) for all tags + (bug, NULL::text)';
1558-
1559-
1560-CREATE TABLE bugtask (
1561- id integer NOT NULL,
1562- bug integer NOT NULL,
1563- product integer,
1564- distribution integer,
1565- distroseries integer,
1566- sourcepackagename integer,
1567- binarypackagename integer,
1568- status integer NOT NULL,
1569- importance integer DEFAULT 5 NOT NULL,
1570- assignee integer,
1571- date_assigned timestamp without time zone,
1572- datecreated timestamp without time zone DEFAULT timezone('UTC'::text, ('now'::text)::timestamp(6) with time zone),
1573- owner integer NOT NULL,
1574- milestone integer,
1575- bugwatch integer,
1576- fti ts2.tsvector,
1577- targetnamecache text,
1578- date_confirmed timestamp without time zone,
1579- date_inprogress timestamp without time zone,
1580- date_closed timestamp without time zone,
1581- productseries integer,
1582- date_incomplete timestamp without time zone,
1583- date_left_new timestamp without time zone,
1584- date_triaged timestamp without time zone,
1585- date_fix_committed timestamp without time zone,
1586- date_fix_released timestamp without time zone,
1587- date_left_closed timestamp without time zone,
1588- heat_rank integer DEFAULT 0 NOT NULL,
1589- date_milestone_set timestamp without time zone,
1590- heat integer DEFAULT 0 NOT NULL,
1591- CONSTRAINT bugtask_assignment_checks CHECK (CASE WHEN (product IS NOT NULL) THEN ((((productseries IS NULL) AND (distribution IS NULL)) AND (distroseries IS NULL)) AND (sourcepackagename IS NULL)) WHEN (productseries IS NOT NULL) THEN (((distribution IS NULL) AND (distroseries IS NULL)) AND (sourcepackagename IS NULL)) WHEN (distribution IS NOT NULL) THEN (distroseries IS NULL) WHEN (distroseries IS NOT NULL) THEN true ELSE false END)
1592-);
1593-
1594-
1595-COMMENT ON TABLE bugtask IS 'Links a given Bug to a particular (sourcepackagename, distro) or product.';
1596-
1597-
1598-COMMENT ON COLUMN bugtask.bug IS 'The bug that is assigned to this (sourcepackagename, distro) or product.';
1599-
1600-
1601-COMMENT ON COLUMN bugtask.product IS 'The product in which this bug shows up.';
1602-
1603-
1604-COMMENT ON COLUMN bugtask.distribution IS 'The distro of the named sourcepackage.';
1605-
1606-
1607-COMMENT ON COLUMN bugtask.sourcepackagename IS 'The name of the sourcepackage in which this bug shows up.';
1608-
1609-
1610-COMMENT ON COLUMN bugtask.binarypackagename IS 'The name of the binary package built from the source package. This column may only contain a value if this bug task is linked to a sourcepackage (not a product)';
1611-
1612-
1613-COMMENT ON COLUMN bugtask.status IS 'The general health of the bug, e.g. Accepted, Rejected, etc.';
1614-
1615-
1616-COMMENT ON COLUMN bugtask.importance IS 'The importance of fixing the bug.';
1617-
1618-
1619-COMMENT ON COLUMN bugtask.assignee IS 'The person who has been assigned to fix this bug in this product or (sourcepackagename, distro)';
1620-
1621-
1622-COMMENT ON COLUMN bugtask.date_assigned IS 'The date on which the bug in this (sourcepackagename, distro) or product was assigned to someone to fix';
1623-
1624-
1625-COMMENT ON COLUMN bugtask.datecreated IS 'A timestamp for the creation of this bug assignment. Note that this is not the date the bug was created (though it might be), it''s the date the bug was assigned to this product, which could have come later.';
1626-
1627-
1628-COMMENT ON COLUMN bugtask.milestone IS 'A way to mark a bug for grouping purposes, e.g. to say it needs to be fixed by version 1.2';
1629-
1630-
1631-COMMENT ON COLUMN bugtask.bugwatch IS 'This column allows us to link a bug
1632-task to a bug watch. In other words, we are connecting the state of the task
1633-to the state of the bug in a different bug tracking system. To the best of
1634-our ability we''ll try and keep the bug task syncronised with the state of
1635-the remote bug watch.';
1636-
1637-
1638-COMMENT ON COLUMN bugtask.targetnamecache IS 'A cached value of the target name of this bugtask, to make it easier to sort and search on the target name.';
1639-
1640-
1641-COMMENT ON COLUMN bugtask.date_confirmed IS 'The date when this bug transitioned from an unconfirmed status to a confirmed one. If the state regresses to a one that logically occurs before Confirmed, e.g., Unconfirmed, this date is cleared.';
1642-
1643-
1644-COMMENT ON COLUMN bugtask.date_inprogress IS 'The date on which this bug transitioned from not being in progress to a state >= In Progress. If the status moves back to a pre-In Progress state, this date is cleared';
1645-
1646-
1647-COMMENT ON COLUMN bugtask.date_closed IS 'The date when this bug transitioned to a resolved state, e.g., Rejected, Fix Released, etc. If the state changes back to a pre-closed state, this date is cleared';
1648-
1649-
1650-COMMENT ON COLUMN bugtask.productseries IS 'The product series to which the bug is targeted';
1651-
1652-
1653-COMMENT ON COLUMN bugtask.date_left_new IS 'The date when this bug first transitioned out of the NEW status.';
1654-
1655-
1656-COMMENT ON COLUMN bugtask.date_triaged IS 'The date when this bug transitioned to a status >= TRIAGED.';
1657-
1658-
1659-COMMENT ON COLUMN bugtask.date_fix_committed IS 'The date when this bug transitioned to a status >= FIXCOMMITTED.';
1660-
1661-
1662-COMMENT ON COLUMN bugtask.date_fix_released IS 'The date when this bug transitioned to a FIXRELEASED status.';
1663-
1664-
1665-COMMENT ON COLUMN bugtask.date_left_closed IS 'The date when this bug last transitioned out of a CLOSED status.';
1666-
1667-
1668-COMMENT ON COLUMN bugtask.heat_rank IS 'The heat bin in which this bugtask appears, as a value from the BugTaskHeatRank enumeration.';
1669-
1670-
1671-COMMENT ON COLUMN bugtask.date_milestone_set IS 'The date when this bug was targed to the milestone that is currently set.';
1672-
1673-
1674-CREATE FUNCTION bugsummary_tasks(bug_row bug) RETURNS SETOF bugtask
1675- LANGUAGE plpgsql STABLE
1676- AS $$
1677-DECLARE
1678- bt bugtask%ROWTYPE;
1679- r record;
1680-BEGIN
1681- bt.bug = BUG_ROW.id;
1682-
1683- -- One row only for each target permutation - need to ignore other fields
1684- -- like date last modified to deal with conjoined masters and multiple
1685- -- sourcepackage tasks in a distro.
1686- FOR r IN
1687- SELECT
1688- product, productseries, distribution, distroseries,
1689- sourcepackagename, status, milestone, importance, bugwatch
1690- FROM BugTask WHERE bug=BUG_ROW.id
1691- UNION -- Implicit DISTINCT
1692- SELECT
1693- product, productseries, distribution, distroseries,
1694- NULL, status, milestone, importance, bugwatch
1695- FROM BugTask WHERE bug=BUG_ROW.id AND sourcepackagename IS NOT NULL
1696- LOOP
1697- bt.product = r.product;
1698- bt.productseries = r.productseries;
1699- bt.distribution = r.distribution;
1700- bt.distroseries = r.distroseries;
1701- bt.sourcepackagename = r.sourcepackagename;
1702- bt.status = r.status;
1703- bt.milestone = r.milestone;
1704- bt.importance = r.importance;
1705- bt.bugwatch = r.bugwatch;
1706- RETURN NEXT bt;
1707- END LOOP;
1708-END;
1709-$$;
1710-
1711-
1712-COMMENT ON FUNCTION bugsummary_tasks(bug_row bug) IS 'Return all tasks for the bug + all sourcepackagename tasks again with the sourcepackagename squashed';
1713-
1714-
1715-CREATE TABLE bugsubscription (
1716- id integer NOT NULL,
1717- person integer NOT NULL,
1718- bug integer NOT NULL,
1719- date_created timestamp without time zone DEFAULT timezone('UTC'::text, now()) NOT NULL,
1720- subscribed_by integer NOT NULL,
1721- bug_notification_level integer DEFAULT 40 NOT NULL
1722-);
1723-
1724-
1725-COMMENT ON TABLE bugsubscription IS 'A subscription by a Person to a bug.';
1726-
1727-
1728-COMMENT ON COLUMN bugsubscription.bug_notification_level IS 'The level of notifications which the Person will receive from this subscription.';
1729-
1730-
1731-CREATE FUNCTION bugsummary_viewers(bug_row bug) RETURNS SETOF bugsubscription
1732- LANGUAGE sql STABLE
1733- AS $_$
1734- SELECT *
1735- FROM BugSubscription
1736- WHERE
1737- bugsubscription.bug=$1.id
1738- AND $1.private IS TRUE;
1739-$_$;
1740-
1741-
1742-COMMENT ON FUNCTION bugsummary_viewers(bug_row bug) IS 'Return (bug, viewer) for all viewers if private, nothing otherwise';
1743-
1744-
1745-CREATE FUNCTION bugtag_maintain_bug_summary() RETURNS trigger
1746- LANGUAGE plpgsql SECURITY DEFINER
1747- SET search_path TO public
1748- AS $$
1749-BEGIN
1750- IF TG_OP = 'INSERT' THEN
1751- IF TG_WHEN = 'BEFORE' THEN
1752- PERFORM unsummarise_bug(bug_row(NEW.bug));
1753- ELSE
1754- PERFORM summarise_bug(bug_row(NEW.bug));
1755- END IF;
1756- PERFORM bug_summary_flush_temp_journal();
1757- RETURN NEW;
1758- ELSIF TG_OP = 'DELETE' THEN
1759- IF TG_WHEN = 'BEFORE' THEN
1760- PERFORM unsummarise_bug(bug_row(OLD.bug));
1761- ELSE
1762- PERFORM summarise_bug(bug_row(OLD.bug));
1763- END IF;
1764- PERFORM bug_summary_flush_temp_journal();
1765- RETURN OLD;
1766- ELSE
1767- IF TG_WHEN = 'BEFORE' THEN
1768- PERFORM unsummarise_bug(bug_row(OLD.bug));
1769- IF OLD.bug <> NEW.bug THEN
1770- PERFORM unsummarise_bug(bug_row(NEW.bug));
1771- END IF;
1772- ELSE
1773- PERFORM summarise_bug(bug_row(OLD.bug));
1774- IF OLD.bug <> NEW.bug THEN
1775- PERFORM summarise_bug(bug_row(NEW.bug));
1776- END IF;
1777- END IF;
1778- PERFORM bug_summary_flush_temp_journal();
1779- RETURN NEW;
1780- END IF;
1781-END;
1782-$$;
1783-
1784-
1785-COMMENT ON FUNCTION bugtag_maintain_bug_summary() IS 'AFTER trigger on bugtag maintaining the bugs summaries in bugsummary.';
1786-
1787-
1788-CREATE FUNCTION bugtask_maintain_bug_summary() RETURNS trigger
1789- LANGUAGE plpgsql SECURITY DEFINER
1790- SET search_path TO public
1791- AS $$
1792-BEGIN
1793- -- This trigger only works if we are inserting, updating or deleting
1794- -- a single row per statement.
1795-
1796- -- Unlike bug_maintain_bug_summary, this trigger does not have access
1797- -- to the old bug when invoked as an AFTER trigger. To work around this
1798- -- we install this trigger as both a BEFORE and an AFTER trigger.
1799- IF TG_OP = 'INSERT' THEN
1800- IF TG_WHEN = 'BEFORE' THEN
1801- PERFORM unsummarise_bug(bug_row(NEW.bug));
1802- ELSE
1803- PERFORM summarise_bug(bug_row(NEW.bug));
1804- END IF;
1805- PERFORM bug_summary_flush_temp_journal();
1806- RETURN NEW;
1807-
1808- ELSIF TG_OP = 'DELETE' THEN
1809- IF TG_WHEN = 'BEFORE' THEN
1810- PERFORM unsummarise_bug(bug_row(OLD.bug));
1811- ELSE
1812- PERFORM summarise_bug(bug_row(OLD.bug));
1813- END IF;
1814- PERFORM bug_summary_flush_temp_journal();
1815- RETURN OLD;
1816-
1817- ELSE
1818- IF (OLD.product IS DISTINCT FROM NEW.product
1819- OR OLD.productseries IS DISTINCT FROM NEW.productseries
1820- OR OLD.distribution IS DISTINCT FROM NEW.distribution
1821- OR OLD.distroseries IS DISTINCT FROM NEW.distroseries
1822- OR OLD.sourcepackagename IS DISTINCT FROM NEW.sourcepackagename
1823- OR OLD.status IS DISTINCT FROM NEW.status
1824- OR OLD.importance IS DISTINCT FROM NEW.importance
1825- OR OLD.bugwatch IS DISTINCT FROM NEW.bugwatch
1826- OR OLD.milestone IS DISTINCT FROM NEW.milestone) THEN
1827-
1828- IF TG_WHEN = 'BEFORE' THEN
1829- PERFORM unsummarise_bug(bug_row(OLD.bug));
1830- IF OLD.bug <> NEW.bug THEN
1831- PERFORM unsummarise_bug(bug_row(NEW.bug));
1832- END IF;
1833- ELSE
1834- PERFORM summarise_bug(bug_row(OLD.bug));
1835- IF OLD.bug <> NEW.bug THEN
1836- PERFORM summarise_bug(bug_row(NEW.bug));
1837- END IF;
1838- END IF;
1839- END IF;
1840- PERFORM bug_summary_flush_temp_journal();
1841- RETURN NEW;
1842- END IF;
1843-END;
1844-$$;
1845-
1846-
1847-COMMENT ON FUNCTION bugtask_maintain_bug_summary() IS 'Both BEFORE & AFTER trigger on bugtask maintaining the bugs summaries in bugsummary.';
1848-
1849-
1850-CREATE FUNCTION calculate_bug_heat(bug_id integer) RETURNS integer
1851- LANGUAGE plpythonu STABLE STRICT
1852- AS $$
1853- from datetime import datetime
1854-
1855- class BugHeatConstants:
1856- PRIVACY = 150
1857- SECURITY = 250
1858- DUPLICATE = 6
1859- AFFECTED_USER = 4
1860- SUBSCRIBER = 2
1861-
1862- def get_max_heat_for_bug(bug_id):
1863- results = plpy.execute("""
1864- SELECT MAX(
1865- GREATEST(Product.max_bug_heat,
1866- DistributionSourcePackage.max_bug_heat))
1867- AS max_heat
1868- FROM BugTask
1869- LEFT OUTER JOIN ProductSeries ON
1870- BugTask.productseries = ProductSeries.id
1871- LEFT OUTER JOIN Product ON (
1872- BugTask.product = Product.id
1873- OR ProductSeries.product = Product.id)
1874- LEFT OUTER JOIN DistroSeries ON
1875- BugTask.distroseries = DistroSeries.id
1876- LEFT OUTER JOIN Distribution ON (
1877- BugTask.distribution = Distribution.id
1878- OR DistroSeries.distribution = Distribution.id)
1879- LEFT OUTER JOIN DistributionSourcePackage ON (
1880- BugTask.sourcepackagename =
1881- DistributionSourcePackage.sourcepackagename)
1882- WHERE
1883- BugTask.bug = %s""" % bug_id)
1884-
1885- return results[0]['max_heat']
1886-
1887- # It would be nice to be able to just SELECT * here, but we need the
1888- # timestamps to be in a format that datetime.fromtimestamp() will
1889- # understand.
1890- bug_data = plpy.execute("""
1891- SELECT
1892- duplicateof,
1893- private,
1894- security_related,
1895- number_of_duplicates,
1896- users_affected_count,
1897- EXTRACT(epoch from datecreated)
1898- AS timestamp_date_created,
1899- EXTRACT(epoch from date_last_updated)
1900- AS timestamp_date_last_updated,
1901- EXTRACT(epoch from date_last_message)
1902- AS timestamp_date_last_message
1903- FROM Bug WHERE id = %s""" % bug_id)
1904-
1905- if bug_data.nrows() == 0:
1906- raise Exception("Bug %s doesn't exist." % bug_id)
1907-
1908- bug = bug_data[0]
1909- if bug['duplicateof'] is not None:
1910- return None
1911-
1912- heat = {}
1913- heat['dupes'] = (
1914- BugHeatConstants.DUPLICATE * bug['number_of_duplicates'])
1915- heat['affected_users'] = (
1916- BugHeatConstants.AFFECTED_USER *
1917- bug['users_affected_count'])
1918-
1919- if bug['private']:
1920- heat['privacy'] = BugHeatConstants.PRIVACY
1921- if bug['security_related']:
1922- heat['security'] = BugHeatConstants.SECURITY
1923-
1924- # Get the heat from subscribers, both direct and via duplicates.
1925- subs_from_dupes = plpy.execute("""
1926- SELECT COUNT(DISTINCT BugSubscription.person) AS sub_count
1927- FROM BugSubscription, Bug
1928- WHERE Bug.id = BugSubscription.bug
1929- AND (Bug.id = %s OR Bug.duplicateof = %s)"""
1930- % (bug_id, bug_id))
1931-
1932- heat['subcribers'] = (
1933- BugHeatConstants.SUBSCRIBER
1934- * subs_from_dupes[0]['sub_count'])
1935-
1936- total_heat = sum(heat.values())
1937-
1938- # Bugs decay over time. Every day the bug isn't touched its heat
1939- # decreases by 1%.
1940- date_last_updated = datetime.fromtimestamp(
1941- bug['timestamp_date_last_updated'])
1942- days_since_last_update = (datetime.utcnow() - date_last_updated).days
1943- total_heat = int(total_heat * (0.99 ** days_since_last_update))
1944-
1945- if days_since_last_update > 0:
1946- # Bug heat increases by a quarter of the maximum bug heat
1947- # divided by the number of days since the bug's creation date.
1948- date_created = datetime.fromtimestamp(
1949- bug['timestamp_date_created'])
1950-
1951- if bug['timestamp_date_last_message'] is not None:
1952- date_last_message = datetime.fromtimestamp(
1953- bug['timestamp_date_last_message'])
1954- oldest_date = max(date_last_updated, date_last_message)
1955- else:
1956- date_last_message = None
1957- oldest_date = date_last_updated
1958-
1959- days_since_last_activity = (datetime.utcnow() - oldest_date).days
1960- days_since_created = (datetime.utcnow() - date_created).days
1961- max_heat = get_max_heat_for_bug(bug_id)
1962- if max_heat is not None and days_since_created > 0:
1963- total_heat = (
1964- total_heat + (max_heat * 0.25 / days_since_created))
1965-
1966- return int(total_heat)
1967-$$;
1968-
1969-
1970-CREATE FUNCTION cursor_fetch(cur refcursor, n integer) RETURNS SETOF record
1971+ CONSTRAINT valid_tag CHECK (public.valid_name(tag))
1972+);
1973+
1974+
1975+COMMENT ON TABLE public.bugtag IS 'Attaches simple text tags to a bug.';
1976+
1977+
1978+COMMENT ON COLUMN public.bugtag.bug IS 'The bug the tags is attached to.';
1979+
1980+
1981+COMMENT ON COLUMN public.bugtag.tag IS 'The text representation of the tag.';
1982+
1983+
1984+CREATE FUNCTION public.bugsummary_tags(btf_row public.bugtaskflat) RETURNS SETOF public.bugtag
1985+ LANGUAGE sql STABLE
1986+ AS $_$
1987+ SELECT * FROM BugTag WHERE BugTag.bug = $1.bug
1988+ UNION ALL
1989+ SELECT NULL::integer, $1.bug, NULL::text;
1990+$_$;
1991+
1992+
1993+CREATE FUNCTION public.bugsummary_targets(btf_row public.bugtaskflat) RETURNS TABLE(product integer, productseries integer, distribution integer, distroseries integer, sourcepackagename integer)
1994+ LANGUAGE sql IMMUTABLE
1995+ AS $_$
1996+ -- Include a sourcepackagename-free task if this one has a
1997+ -- sourcepackagename, so package tasks are also counted in their
1998+ -- distro/series.
1999+ SELECT
2000+ $1.product, $1.productseries, $1.distribution,
2001+ $1.distroseries, $1.sourcepackagename
2002+ UNION -- Implicit DISTINCT
2003+ SELECT
2004+ $1.product, $1.productseries, $1.distribution,
2005+ $1.distroseries, NULL;
2006+$_$;
2007+
2008+
2009+CREATE FUNCTION public.bugsummary_viewers(btf_row public.bugtaskflat) RETURNS TABLE(viewed_by integer, access_policy integer)
2010+ LANGUAGE sql IMMUTABLE
2011+ AS $_$
2012+ SELECT NULL::integer, NULL::integer WHERE $1.information_type IN (1, 2)
2013+ UNION ALL
2014+ SELECT unnest($1.access_grants), NULL::integer
2015+ WHERE $1.information_type NOT IN (1, 2)
2016+ UNION ALL
2017+ SELECT NULL::integer, unnest($1.access_policies)
2018+ WHERE $1.information_type NOT IN (1, 2);
2019+$_$;
2020+
2021+
2022+CREATE FUNCTION public.bugtag_maintain_bug_summary() RETURNS trigger
2023+ LANGUAGE plpgsql SECURITY DEFINER
2024+ SET search_path TO 'public'
2025+ AS $$
2026+BEGIN
2027+ IF TG_OP = 'INSERT' THEN
2028+ IF TG_WHEN = 'BEFORE' THEN
2029+ PERFORM unsummarise_bug(NEW.bug);
2030+ ELSE
2031+ PERFORM summarise_bug(NEW.bug);
2032+ END IF;
2033+ PERFORM bug_summary_flush_temp_journal();
2034+ RETURN NEW;
2035+ ELSIF TG_OP = 'DELETE' THEN
2036+ IF TG_WHEN = 'BEFORE' THEN
2037+ PERFORM unsummarise_bug(OLD.bug);
2038+ ELSE
2039+ PERFORM summarise_bug(OLD.bug);
2040+ END IF;
2041+ PERFORM bug_summary_flush_temp_journal();
2042+ RETURN OLD;
2043+ ELSE
2044+ IF TG_WHEN = 'BEFORE' THEN
2045+ PERFORM unsummarise_bug(OLD.bug);
2046+ IF OLD.bug <> NEW.bug THEN
2047+ PERFORM unsummarise_bug(NEW.bug);
2048+ END IF;
2049+ ELSE
2050+ PERFORM summarise_bug(OLD.bug);
2051+ IF OLD.bug <> NEW.bug THEN
2052+ PERFORM summarise_bug(NEW.bug);
2053+ END IF;
2054+ END IF;
2055+ PERFORM bug_summary_flush_temp_journal();
2056+ RETURN NEW;
2057+ END IF;
2058+END;
2059+$$;
2060+
2061+
2062+COMMENT ON FUNCTION public.bugtag_maintain_bug_summary() IS 'AFTER trigger on bugtag maintaining the bugs summaries in bugsummary.';
2063+
2064+
2065+CREATE FUNCTION public.bugtask_flatten(task_id integer, check_only boolean) RETURNS boolean
2066+ LANGUAGE plpgsql SECURITY DEFINER
2067+ SET search_path TO 'public'
2068+ AS $$
2069+DECLARE
2070+ bug_row Bug%ROWTYPE;
2071+ task_row BugTask%ROWTYPE;
2072+ old_flat_row BugTaskFlat%ROWTYPE;
2073+ new_flat_row BugTaskFlat%ROWTYPE;
2074+ _product_active boolean;
2075+ _access_policies integer[];
2076+ _access_grants integer[];
2077+BEGIN
2078+ -- This is the master function to update BugTaskFlat, but there are
2079+ -- maintenance triggers and jobs on the involved tables that update
2080+ -- it directly. Any changes here probably require a corresponding
2081+ -- change in other trigger functions.
2082+
2083+ SELECT * INTO task_row FROM BugTask WHERE id = task_id;
2084+ SELECT * INTO old_flat_row FROM BugTaskFlat WHERE bugtask = task_id;
2085+
2086+ -- If the task doesn't exist, ensure that there's no flat row.
2087+ IF task_row.id IS NULL THEN
2088+ IF old_flat_row.bugtask IS NOT NULL THEN
2089+ IF NOT check_only THEN
2090+ DELETE FROM BugTaskFlat WHERE bugtask = task_id;
2091+ END IF;
2092+ RETURN FALSE;
2093+ ELSE
2094+ RETURN TRUE;
2095+ END IF;
2096+ END IF;
2097+
2098+ SELECT * FROM bug INTO bug_row WHERE id = task_row.bug;
2099+
2100+ -- If it's a product(series) task, we must consult the active flag.
2101+ IF task_row.product IS NOT NULL THEN
2102+ SELECT product.active INTO _product_active
2103+ FROM product WHERE product.id = task_row.product LIMIT 1;
2104+ ELSIF task_row.productseries IS NOT NULL THEN
2105+ SELECT product.active INTO _product_active
2106+ FROM
2107+ product
2108+ JOIN productseries ON productseries.product = product.id
2109+ WHERE productseries.id = task_row.productseries LIMIT 1;
2110+ END IF;
2111+
2112+ SELECT policies, grants
2113+ INTO _access_policies, _access_grants
2114+ FROM bug_build_access_cache(bug_row.id, bug_row.information_type)
2115+ AS (policies integer[], grants integer[]);
2116+
2117+ -- Compile the new flat row.
2118+ SELECT task_row.id, bug_row.id, task_row.datecreated,
2119+ bug_row.duplicateof, bug_row.owner, bug_row.fti,
2120+ bug_row.information_type, bug_row.date_last_updated,
2121+ bug_row.heat, task_row.product, task_row.productseries,
2122+ task_row.distribution, task_row.distroseries,
2123+ task_row.sourcepackagename, task_row.status,
2124+ task_row.importance, task_row.assignee,
2125+ task_row.milestone, task_row.owner,
2126+ COALESCE(_product_active, TRUE),
2127+ _access_policies,
2128+ _access_grants,
2129+ bug_row.latest_patch_uploaded, task_row.date_closed
2130+ INTO new_flat_row;
2131+
2132+ -- Calculate the necessary updates.
2133+ IF old_flat_row.bugtask IS NULL THEN
2134+ IF NOT check_only THEN
2135+ INSERT INTO BugTaskFlat VALUES (new_flat_row.*);
2136+ END IF;
2137+ RETURN FALSE;
2138+ ELSIF new_flat_row != old_flat_row THEN
2139+ IF NOT check_only THEN
2140+ UPDATE BugTaskFlat SET
2141+ bug = new_flat_row.bug,
2142+ datecreated = new_flat_row.datecreated,
2143+ duplicateof = new_flat_row.duplicateof,
2144+ bug_owner = new_flat_row.bug_owner,
2145+ fti = new_flat_row.fti,
2146+ information_type = new_flat_row.information_type,
2147+ date_last_updated = new_flat_row.date_last_updated,
2148+ heat = new_flat_row.heat,
2149+ product = new_flat_row.product,
2150+ productseries = new_flat_row.productseries,
2151+ distribution = new_flat_row.distribution,
2152+ distroseries = new_flat_row.distroseries,
2153+ sourcepackagename = new_flat_row.sourcepackagename,
2154+ status = new_flat_row.status,
2155+ importance = new_flat_row.importance,
2156+ assignee = new_flat_row.assignee,
2157+ milestone = new_flat_row.milestone,
2158+ owner = new_flat_row.owner,
2159+ active = new_flat_row.active,
2160+ access_policies = new_flat_row.access_policies,
2161+ access_grants = new_flat_row.access_grants,
2162+ date_closed = new_flat_row.date_closed,
2163+ latest_patch_uploaded = new_flat_row.latest_patch_uploaded
2164+ WHERE bugtask = new_flat_row.bugtask;
2165+ END IF;
2166+ RETURN FALSE;
2167+ ELSE
2168+ RETURN TRUE;
2169+ END IF;
2170+END;
2171+$$;
2172+
2173+
2174+COMMENT ON FUNCTION public.bugtask_flatten(task_id integer, check_only boolean) IS 'Create or update a BugTaskFlat row from the source tables. Returns whether the row was up to date. If check_only is true, the row is not brought up to date.';
2175+
2176+
2177+CREATE FUNCTION public.bugtask_maintain_bugtaskflat_trig() RETURNS trigger
2178+ LANGUAGE plpgsql SECURITY DEFINER
2179+ SET search_path TO 'public'
2180+ AS $$
2181+BEGIN
2182+ IF TG_OP = 'INSERT' THEN
2183+ PERFORM bugtask_flatten(NEW.id, FALSE);
2184+ ELSIF TG_OP = 'UPDATE' THEN
2185+ IF NEW.bug != OLD.bug THEN
2186+ RAISE EXCEPTION 'cannot move bugtask to a different bug';
2187+ ELSIF (NEW.product IS DISTINCT FROM OLD.product
2188+ OR NEW.productseries IS DISTINCT FROM OLD.productseries) THEN
2189+ -- product.active may differ. Do a full update.
2190+ PERFORM bugtask_flatten(NEW.id, FALSE);
2191+ ELSIF (
2192+ NEW.datecreated IS DISTINCT FROM OLD.datecreated
2193+ OR NEW.product IS DISTINCT FROM OLD.product
2194+ OR NEW.productseries IS DISTINCT FROM OLD.productseries
2195+ OR NEW.distribution IS DISTINCT FROM OLD.distribution
2196+ OR NEW.distroseries IS DISTINCT FROM OLD.distroseries
2197+ OR NEW.sourcepackagename IS DISTINCT FROM OLD.sourcepackagename
2198+ OR NEW.status IS DISTINCT FROM OLD.status
2199+ OR NEW.importance IS DISTINCT FROM OLD.importance
2200+ OR NEW.assignee IS DISTINCT FROM OLD.assignee
2201+ OR NEW.milestone IS DISTINCT FROM OLD.milestone
2202+ OR NEW.owner IS DISTINCT FROM OLD.owner
2203+ OR NEW.date_closed IS DISTINCT FROM OLD.date_closed) THEN
2204+ -- Otherwise just update the columns from bugtask.
2205+ -- Access policies and grants may have changed due to target
2206+ -- transitions, but an earlier trigger will already have
2207+ -- mirrored them to all relevant flat tasks.
2208+ UPDATE BugTaskFlat SET
2209+ datecreated = NEW.datecreated,
2210+ product = NEW.product,
2211+ productseries = NEW.productseries,
2212+ distribution = NEW.distribution,
2213+ distroseries = NEW.distroseries,
2214+ sourcepackagename = NEW.sourcepackagename,
2215+ status = NEW.status,
2216+ importance = NEW.importance,
2217+ assignee = NEW.assignee,
2218+ milestone = NEW.milestone,
2219+ owner = NEW.owner,
2220+ date_closed = NEW.date_closed
2221+ WHERE bugtask = NEW.id;
2222+ END IF;
2223+ ELSIF TG_OP = 'DELETE' THEN
2224+ PERFORM bugtask_flatten(OLD.id, FALSE);
2225+ END IF;
2226+ RETURN NULL;
2227+END;
2228+$$;
2229+
2230+
2231+CREATE FUNCTION public.bugtaskflat_maintain_bug_summary() RETURNS trigger
2232+ LANGUAGE plpgsql SECURITY DEFINER
2233+ SET search_path TO 'public'
2234+ AS $$
2235+BEGIN
2236+ IF TG_OP = 'INSERT' THEN
2237+ PERFORM bugsummary_journal_bugtaskflat(NEW, 1);
2238+ PERFORM bug_summary_flush_temp_journal();
2239+ ELSIF TG_OP = 'DELETE' THEN
2240+ PERFORM bugsummary_journal_bugtaskflat(OLD, -1);
2241+ PERFORM bug_summary_flush_temp_journal();
2242+ ELSIF
2243+ NEW.product IS DISTINCT FROM OLD.product
2244+ OR NEW.productseries IS DISTINCT FROM OLD.productseries
2245+ OR NEW.distribution IS DISTINCT FROM OLD.distribution
2246+ OR NEW.distroseries IS DISTINCT FROM OLD.distroseries
2247+ OR NEW.sourcepackagename IS DISTINCT FROM OLD.sourcepackagename
2248+ OR NEW.status IS DISTINCT FROM OLD.status
2249+ OR NEW.milestone IS DISTINCT FROM OLD.milestone
2250+ OR NEW.importance IS DISTINCT FROM OLD.importance
2251+ OR NEW.latest_patch_uploaded IS DISTINCT FROM OLD.latest_patch_uploaded
2252+ OR NEW.information_type IS DISTINCT FROM OLD.information_type
2253+ OR NEW.access_grants IS DISTINCT FROM OLD.access_grants
2254+ OR NEW.access_policies IS DISTINCT FROM OLD.access_policies
2255+ OR NEW.duplicateof IS DISTINCT FROM OLD.duplicateof
2256+ THEN
2257+ PERFORM bugsummary_journal_bugtaskflat(OLD, -1);
2258+ PERFORM bugsummary_journal_bugtaskflat(NEW, 1);
2259+ PERFORM bug_summary_flush_temp_journal();
2260+ END IF;
2261+ RETURN NULL;
2262+END;
2263+$$;
2264+
2265+
2266+CREATE FUNCTION public.build_access_cache(art_id integer, information_type integer) RETURNS record
2267+ LANGUAGE plpgsql
2268+ AS $$
2269+DECLARE
2270+ _policies integer[];
2271+ _grants integer[];
2272+ cache record;
2273+BEGIN
2274+ -- If private, grab the access control information.
2275+ -- If public, access_policies and access_grants are NULL.
2276+ -- 3 == PRIVATESECURITY, 4 == USERDATA, 5 == PROPRIETARY
2277+ -- 6 == EMBARGOED
2278+ IF information_type NOT IN (1, 2) THEN
2279+ SELECT COALESCE(array_agg(policy ORDER BY policy), ARRAY[]::integer[])
2280+ INTO _policies FROM accesspolicyartifact WHERE artifact = art_id;
2281+ SELECT COALESCE(array_agg(grantee ORDER BY grantee), ARRAY[]::integer[])
2282+ INTO _grants FROM accessartifactgrant WHERE artifact = art_id;
2283+ END IF;
2284+ cache := (_policies, _grants);
2285+ RETURN cache;
2286+END;
2287+$$;
2288+
2289+
2290+CREATE FUNCTION public.calculate_bug_heat(bug_id integer) RETURNS integer
2291+ LANGUAGE sql STABLE STRICT
2292+ AS $_$
2293+ SELECT
2294+ (CASE information_type WHEN 1 THEN 0 WHEN 2 THEN 250
2295+ WHEN 3 THEN 400 ELSE 150 END)
2296+ + (number_of_duplicates * 6)
2297+ + (users_affected_count * 4)
2298+ + (
2299+ SELECT COUNT(DISTINCT person) * 2
2300+ FROM BugSubscription
2301+ JOIN Bug AS SubBug ON BugSubscription.bug = SubBug.id
2302+ WHERE SubBug.id = $1 OR SubBug.duplicateof = $1)::integer AS heat
2303+ FROM Bug WHERE Bug.id = $1;
2304+$_$;
2305+
2306+
2307+CREATE FUNCTION public.cursor_fetch(cur refcursor, n integer) RETURNS SETOF record
2308 LANGUAGE plpgsql
2309 AS $$
2310 DECLARE
2311@@ -1217,86 +1395,86 @@
2312 $$;
2313
2314
2315-COMMENT ON FUNCTION cursor_fetch(cur refcursor, n integer) IS 'Fetch the next n items from a cursor. Work around for not being able to use FETCH inside a SELECT statement.';
2316-
2317-
2318-CREATE FUNCTION debversion(character) RETURNS debversion
2319+COMMENT ON FUNCTION public.cursor_fetch(cur refcursor, n integer) IS 'Fetch the next n items from a cursor. Work around for not being able to use FETCH inside a SELECT statement.';
2320+
2321+
2322+CREATE FUNCTION public.debversion(character) RETURNS public.debversion
2323 LANGUAGE internal IMMUTABLE STRICT
2324 AS $$rtrim1$$;
2325
2326
2327-CREATE FUNCTION debversion_cmp(version1 debversion, version2 debversion) RETURNS integer
2328+CREATE FUNCTION public.debversion_cmp(version1 public.debversion, version2 public.debversion) RETURNS integer
2329 LANGUAGE c IMMUTABLE STRICT
2330 AS '$libdir/debversion', 'debversion_cmp';
2331
2332
2333-COMMENT ON FUNCTION debversion_cmp(version1 debversion, version2 debversion) IS 'Compare Debian versions';
2334-
2335-
2336-CREATE FUNCTION debversion_eq(version1 debversion, version2 debversion) RETURNS boolean
2337+COMMENT ON FUNCTION public.debversion_cmp(version1 public.debversion, version2 public.debversion) IS 'Compare Debian versions';
2338+
2339+
2340+CREATE FUNCTION public.debversion_eq(version1 public.debversion, version2 public.debversion) RETURNS boolean
2341 LANGUAGE c IMMUTABLE STRICT
2342 AS '$libdir/debversion', 'debversion_eq';
2343
2344
2345-COMMENT ON FUNCTION debversion_eq(version1 debversion, version2 debversion) IS 'debversion equal';
2346-
2347-
2348-CREATE FUNCTION debversion_ge(version1 debversion, version2 debversion) RETURNS boolean
2349+COMMENT ON FUNCTION public.debversion_eq(version1 public.debversion, version2 public.debversion) IS 'debversion equal';
2350+
2351+
2352+CREATE FUNCTION public.debversion_ge(version1 public.debversion, version2 public.debversion) RETURNS boolean
2353 LANGUAGE c IMMUTABLE STRICT
2354 AS '$libdir/debversion', 'debversion_ge';
2355
2356
2357-COMMENT ON FUNCTION debversion_ge(version1 debversion, version2 debversion) IS 'debversion greater-than-or-equal';
2358-
2359-
2360-CREATE FUNCTION debversion_gt(version1 debversion, version2 debversion) RETURNS boolean
2361+COMMENT ON FUNCTION public.debversion_ge(version1 public.debversion, version2 public.debversion) IS 'debversion greater-than-or-equal';
2362+
2363+
2364+CREATE FUNCTION public.debversion_gt(version1 public.debversion, version2 public.debversion) RETURNS boolean
2365 LANGUAGE c IMMUTABLE STRICT
2366 AS '$libdir/debversion', 'debversion_gt';
2367
2368
2369-COMMENT ON FUNCTION debversion_gt(version1 debversion, version2 debversion) IS 'debversion greater-than';
2370-
2371-
2372-CREATE FUNCTION debversion_hash(debversion) RETURNS integer
2373+COMMENT ON FUNCTION public.debversion_gt(version1 public.debversion, version2 public.debversion) IS 'debversion greater-than';
2374+
2375+
2376+CREATE FUNCTION public.debversion_hash(public.debversion) RETURNS integer
2377 LANGUAGE c IMMUTABLE STRICT
2378 AS '$libdir/debversion', 'debversion_hash';
2379
2380
2381-CREATE FUNCTION debversion_larger(version1 debversion, version2 debversion) RETURNS debversion
2382+CREATE FUNCTION public.debversion_larger(version1 public.debversion, version2 public.debversion) RETURNS public.debversion
2383 LANGUAGE c IMMUTABLE STRICT
2384 AS '$libdir/debversion', 'debversion_larger';
2385
2386
2387-CREATE FUNCTION debversion_le(version1 debversion, version2 debversion) RETURNS boolean
2388+CREATE FUNCTION public.debversion_le(version1 public.debversion, version2 public.debversion) RETURNS boolean
2389 LANGUAGE c IMMUTABLE STRICT
2390 AS '$libdir/debversion', 'debversion_le';
2391
2392
2393-COMMENT ON FUNCTION debversion_le(version1 debversion, version2 debversion) IS 'debversion less-than-or-equal';
2394-
2395-
2396-CREATE FUNCTION debversion_lt(version1 debversion, version2 debversion) RETURNS boolean
2397+COMMENT ON FUNCTION public.debversion_le(version1 public.debversion, version2 public.debversion) IS 'debversion less-than-or-equal';
2398+
2399+
2400+CREATE FUNCTION public.debversion_lt(version1 public.debversion, version2 public.debversion) RETURNS boolean
2401 LANGUAGE c IMMUTABLE STRICT
2402 AS '$libdir/debversion', 'debversion_lt';
2403
2404
2405-COMMENT ON FUNCTION debversion_lt(version1 debversion, version2 debversion) IS 'debversion less-than';
2406-
2407-
2408-CREATE FUNCTION debversion_ne(version1 debversion, version2 debversion) RETURNS boolean
2409+COMMENT ON FUNCTION public.debversion_lt(version1 public.debversion, version2 public.debversion) IS 'debversion less-than';
2410+
2411+
2412+CREATE FUNCTION public.debversion_ne(version1 public.debversion, version2 public.debversion) RETURNS boolean
2413 LANGUAGE c IMMUTABLE STRICT
2414 AS '$libdir/debversion', 'debversion_ne';
2415
2416
2417-COMMENT ON FUNCTION debversion_ne(version1 debversion, version2 debversion) IS 'debversion not equal';
2418-
2419-
2420-CREATE FUNCTION debversion_smaller(version1 debversion, version2 debversion) RETURNS debversion
2421+COMMENT ON FUNCTION public.debversion_ne(version1 public.debversion, version2 public.debversion) IS 'debversion not equal';
2422+
2423+
2424+CREATE FUNCTION public.debversion_smaller(version1 public.debversion, version2 public.debversion) RETURNS public.debversion
2425 LANGUAGE c IMMUTABLE STRICT
2426 AS '$libdir/debversion', 'debversion_smaller';
2427
2428
2429-CREATE FUNCTION debversion_sort_key(version text) RETURNS text
2430+CREATE FUNCTION public.debversion_sort_key(version text) RETURNS text
2431 LANGUAGE plpythonu IMMUTABLE STRICT
2432 AS $_$
2433 # If this method is altered, then any functional indexes using it
2434@@ -1365,13 +1543,12 @@
2435 $_$;
2436
2437
2438-COMMENT ON FUNCTION debversion_sort_key(version text) IS 'Return a string suitable for sorting debian version strings on';
2439-
2440-
2441-CREATE FUNCTION ensure_bugsummary_temp_journal() RETURNS void
2442+COMMENT ON FUNCTION public.debversion_sort_key(version text) IS 'Return a string suitable for sorting debian version strings on';
2443+
2444+
2445+CREATE FUNCTION public.ensure_bugsummary_temp_journal() RETURNS void
2446 LANGUAGE plpgsql
2447 AS $$
2448-DECLARE
2449 BEGIN
2450 CREATE TEMPORARY TABLE bugsummary_temp_journal (
2451 LIKE bugsummary ) ON COMMIT DROP;
2452@@ -1383,48 +1560,66 @@
2453 $$;
2454
2455
2456-COMMENT ON FUNCTION ensure_bugsummary_temp_journal() IS 'Create a temporary table bugsummary_temp_journal if it does not exist.';
2457-
2458-
2459-CREATE FUNCTION generate_openid_identifier() RETURNS text
2460+COMMENT ON FUNCTION public.ensure_bugsummary_temp_journal() IS 'Create a temporary table bugsummary_temp_journal if it does not exist.';
2461+
2462+
2463+CREATE FUNCTION public.ftiupdate() RETURNS trigger
2464 LANGUAGE plpythonu
2465- AS $$
2466- from random import choice
2467-
2468- # Non display confusing characters.
2469- chars = '34678bcdefhkmnprstwxyzABCDEFGHJKLMNPQRTWXY'
2470-
2471- # Character length of tokens. Can be increased, decreased or even made
2472- # random - Launchpad does not care. 7 means it takes 40 bytes to store
2473- # a null-terminated Launchpad identity URL on the current domain name.
2474- length=7
2475-
2476- loop_count = 0
2477- while loop_count < 20000:
2478- # Generate a random openid_identifier
2479- oid = ''.join(choice(chars) for count in range(length))
2480-
2481- # Check if the oid is already in the db, although this is pretty
2482- # unlikely
2483- rv = plpy.execute("""
2484- SELECT COUNT(*) AS num FROM Account WHERE openid_identifier = '%s'
2485- """ % oid, 1)
2486- if rv[0]['num'] == 0:
2487- return oid
2488- loop_count += 1
2489- if loop_count == 1:
2490- plpy.warning(
2491- 'Clash generating unique openid_identifier. '
2492- 'Increase length if you see this warning too much.')
2493- plpy.error(
2494- "Unable to generate unique openid_identifier. "
2495- "Need to increase length of tokens.")
2496-$$;
2497-
2498-
2499-CREATE FUNCTION getlocalnodeid() RETURNS integer
2500+ AS $_$
2501+ new = TD["new"]
2502+ args = TD["args"][:]
2503+
2504+ # Short circuit if none of the relevant columns have been
2505+ # modified and fti is not being set to NULL (setting the fti
2506+ # column to NULL is thus how we can force a rebuild of the fti
2507+ # column).
2508+ if TD["event"] == "UPDATE" and new["fti"] != None:
2509+ old = TD["old"]
2510+ relevant_modification = False
2511+ for column_name in args[::2]:
2512+ if new[column_name] != old[column_name]:
2513+ relevant_modification = True
2514+ break
2515+ if not relevant_modification:
2516+ return "OK"
2517+
2518+ # Generate an SQL statement that turns the requested
2519+ # column values into a weighted tsvector
2520+ sql = []
2521+ for i in range(0, len(args), 2):
2522+ sql.append(
2523+ "setweight(to_tsvector('default', coalesce("
2524+ "substring(ltrim($%d) from 1 for 2500),'')),"
2525+ "CAST($%d AS \"char\"))" % (i + 1, i + 2))
2526+ args[i] = new[args[i]]
2527+
2528+ sql = "SELECT %s AS fti" % "||".join(sql)
2529+
2530+ # Execute and store in the fti column
2531+ plan = plpy.prepare(sql, ["text", "char"] * (len(args)/2))
2532+ new["fti"] = plpy.execute(plan, args, 1)[0]["fti"]
2533+
2534+ # Tell PostgreSQL we have modified the data
2535+ return "MODIFY"
2536+$_$;
2537+
2538+
2539+COMMENT ON FUNCTION public.ftiupdate() IS 'Trigger function that keeps the fti tsvector column up to date.';
2540+
2541+
2542+CREATE FUNCTION public.ftq(text) RETURNS tsquery
2543+ LANGUAGE plpythonu IMMUTABLE STRICT
2544+ AS $_$
2545+ p = plpy.prepare(
2546+ "SELECT to_tsquery('default', _ftq($1)) AS x", ["text"])
2547+ query = plpy.execute(p, args, 1)[0]["x"]
2548+ return query or None
2549+ $_$;
2550+
2551+
2552+CREATE FUNCTION public.getlocalnodeid() RETURNS integer
2553 LANGUAGE plpgsql STABLE SECURITY DEFINER
2554- SET search_path TO public
2555+ SET search_path TO 'public'
2556 AS $$
2557 DECLARE
2558 v_node_id integer;
2559@@ -1438,70 +1633,48 @@
2560 $$;
2561
2562
2563-COMMENT ON FUNCTION getlocalnodeid() IS 'Return the replication node id for this node, or NULL if not a replicated installation.';
2564-
2565-
2566-CREATE FUNCTION is_blacklisted_name(text, integer) RETURNS boolean
2567+COMMENT ON FUNCTION public.getlocalnodeid() IS 'Return the replication node id for this node, or NULL if not a replicated installation.';
2568+
2569+
2570+CREATE FUNCTION public.gitrepository_denorm_access(gitrepository_id integer) RETURNS void
2571+ LANGUAGE sql SECURITY DEFINER
2572+ SET search_path TO 'public'
2573+ AS $_$
2574+ UPDATE GitRepository
2575+ SET access_policy = policies[1], access_grants = grants
2576+ FROM
2577+ build_access_cache(
2578+ (SELECT id FROM accessartifact WHERE gitrepository = $1),
2579+ (SELECT information_type FROM gitrepository WHERE id = $1))
2580+ AS (policies integer[], grants integer[])
2581+ WHERE id = $1;
2582+$_$;
2583+
2584+
2585+CREATE FUNCTION public.gitrepository_maintain_access_cache_trig() RETURNS trigger
2586+ LANGUAGE plpgsql
2587+ AS $$
2588+BEGIN
2589+ PERFORM gitrepository_denorm_access(NEW.id);
2590+ RETURN NULL;
2591+END;
2592+$$;
2593+
2594+
2595+CREATE FUNCTION public.is_blacklisted_name(text, integer) RETURNS boolean
2596 LANGUAGE sql STABLE STRICT SECURITY DEFINER
2597- SET search_path TO public
2598+ SET search_path TO 'public'
2599 AS $_$
2600 SELECT COALESCE(name_blacklist_match($1, $2)::boolean, FALSE);
2601 $_$;
2602
2603
2604-COMMENT ON FUNCTION is_blacklisted_name(text, integer) IS 'Return TRUE if any regular expressions stored in the NameBlacklist table match the givenname, otherwise return FALSE.';
2605-
2606-
2607-CREATE FUNCTION is_person(text) RETURNS boolean
2608- LANGUAGE sql STABLE STRICT
2609- AS $_$
2610- SELECT count(*)>0 FROM Person WHERE name=$1 AND teamowner IS NULL;
2611-$_$;
2612-
2613-
2614-COMMENT ON FUNCTION is_person(text) IS 'True if the given name identifies a person in the Person table';
2615-
2616-
2617-CREATE FUNCTION is_printable_ascii(text) RETURNS boolean
2618- LANGUAGE plpythonu IMMUTABLE STRICT
2619- AS $_$
2620- import re, string
2621- try:
2622- text = args[0].decode("ASCII")
2623- except UnicodeError:
2624- return False
2625- if re.search(r"^[%s]*$" % re.escape(string.printable), text) is None:
2626- return False
2627- return True
2628-$_$;
2629-
2630-
2631-COMMENT ON FUNCTION is_printable_ascii(text) IS 'True if the string is pure printable US-ASCII';
2632-
2633-
2634-CREATE FUNCTION is_team(integer) RETURNS boolean
2635- LANGUAGE sql STABLE STRICT
2636- AS $_$
2637- SELECT count(*)>0 FROM Person WHERE id=$1 AND teamowner IS NOT NULL;
2638-$_$;
2639-
2640-
2641-COMMENT ON FUNCTION is_team(integer) IS 'True if the given id identifies a team in the Person table';
2642-
2643-
2644-CREATE FUNCTION is_team(text) RETURNS boolean
2645- LANGUAGE sql STABLE STRICT
2646- AS $_$
2647- SELECT count(*)>0 FROM Person WHERE name=$1 AND teamowner IS NOT NULL;
2648-$_$;
2649-
2650-
2651-COMMENT ON FUNCTION is_team(text) IS 'True if the given name identifies a team in the Person table';
2652-
2653-
2654-CREATE FUNCTION lp_mirror_account_ins() RETURNS trigger
2655+COMMENT ON FUNCTION public.is_blacklisted_name(text, integer) IS 'Return TRUE if any regular expressions stored in the NameBlacklist table match the givenname, otherwise return FALSE.';
2656+
2657+
2658+CREATE FUNCTION public.lp_mirror_account_ins() RETURNS trigger
2659 LANGUAGE plpgsql SECURITY DEFINER
2660- SET search_path TO public
2661+ SET search_path TO 'public'
2662 AS $$
2663 BEGIN
2664 INSERT INTO lp_Account (id, openid_identifier)
2665@@ -1511,9 +1684,9 @@
2666 $$;
2667
2668
2669-CREATE FUNCTION lp_mirror_account_upd() RETURNS trigger
2670+CREATE FUNCTION public.lp_mirror_account_upd() RETURNS trigger
2671 LANGUAGE plpgsql SECURITY DEFINER
2672- SET search_path TO public
2673+ SET search_path TO 'public'
2674 AS $$
2675 BEGIN
2676 IF OLD.id <> NEW.id OR OLD.openid_identifier <> NEW.openid_identifier THEN
2677@@ -1526,9 +1699,9 @@
2678 $$;
2679
2680
2681-CREATE FUNCTION lp_mirror_del() RETURNS trigger
2682+CREATE FUNCTION public.lp_mirror_del() RETURNS trigger
2683 LANGUAGE plpgsql SECURITY DEFINER
2684- SET search_path TO public
2685+ SET search_path TO 'public'
2686 AS $$
2687 BEGIN
2688 EXECUTE 'DELETE FROM lp_' || TG_TABLE_NAME || ' WHERE id=' || OLD.id;
2689@@ -1537,9 +1710,9 @@
2690 $$;
2691
2692
2693-CREATE FUNCTION lp_mirror_openididentifier_del() RETURNS trigger
2694+CREATE FUNCTION public.lp_mirror_openididentifier_del() RETURNS trigger
2695 LANGUAGE plpgsql SECURITY DEFINER
2696- SET search_path TO public
2697+ SET search_path TO 'public'
2698 AS $$
2699 DECLARE
2700 next_identifier text;
2701@@ -1562,9 +1735,9 @@
2702 $$;
2703
2704
2705-CREATE FUNCTION lp_mirror_openididentifier_ins() RETURNS trigger
2706+CREATE FUNCTION public.lp_mirror_openididentifier_ins() RETURNS trigger
2707 LANGUAGE plpgsql SECURITY DEFINER
2708- SET search_path TO public
2709+ SET search_path TO 'public'
2710 AS $$
2711 BEGIN
2712 -- Support obsolete lp_Account.openid_identifier as best we can
2713@@ -1584,9 +1757,9 @@
2714 $$;
2715
2716
2717-CREATE FUNCTION lp_mirror_openididentifier_upd() RETURNS trigger
2718+CREATE FUNCTION public.lp_mirror_openididentifier_upd() RETURNS trigger
2719 LANGUAGE plpgsql SECURITY DEFINER
2720- SET search_path TO public
2721+ SET search_path TO 'public'
2722 AS $$
2723 BEGIN
2724 IF OLD.identifier <> NEW.identifier THEN
2725@@ -1604,9 +1777,9 @@
2726 $$;
2727
2728
2729-CREATE FUNCTION lp_mirror_person_ins() RETURNS trigger
2730+CREATE FUNCTION public.lp_mirror_person_ins() RETURNS trigger
2731 LANGUAGE plpgsql SECURITY DEFINER
2732- SET search_path TO public
2733+ SET search_path TO 'public'
2734 AS $$
2735 BEGIN
2736 INSERT INTO lp_Person (
2737@@ -1634,9 +1807,9 @@
2738 $$;
2739
2740
2741-CREATE FUNCTION lp_mirror_person_upd() RETURNS trigger
2742+CREATE FUNCTION public.lp_mirror_person_upd() RETURNS trigger
2743 LANGUAGE plpgsql SECURITY DEFINER
2744- SET search_path TO public
2745+ SET search_path TO 'public'
2746 AS $$
2747 BEGIN
2748 UPDATE lp_Person
2749@@ -1676,9 +1849,9 @@
2750 $$;
2751
2752
2753-CREATE FUNCTION lp_mirror_personlocation_ins() RETURNS trigger
2754+CREATE FUNCTION public.lp_mirror_personlocation_ins() RETURNS trigger
2755 LANGUAGE plpgsql SECURITY DEFINER
2756- SET search_path TO public
2757+ SET search_path TO 'public'
2758 AS $$
2759 BEGIN
2760 INSERT INTO lp_PersonLocation SELECT NEW.*;
2761@@ -1687,9 +1860,9 @@
2762 $$;
2763
2764
2765-CREATE FUNCTION lp_mirror_personlocation_upd() RETURNS trigger
2766+CREATE FUNCTION public.lp_mirror_personlocation_upd() RETURNS trigger
2767 LANGUAGE plpgsql SECURITY DEFINER
2768- SET search_path TO public
2769+ SET search_path TO 'public'
2770 AS $$
2771 BEGIN
2772 UPDATE lp_PersonLocation
2773@@ -1709,9 +1882,9 @@
2774 $$;
2775
2776
2777-CREATE FUNCTION lp_mirror_teamparticipation_ins() RETURNS trigger
2778+CREATE FUNCTION public.lp_mirror_teamparticipation_ins() RETURNS trigger
2779 LANGUAGE plpgsql SECURITY DEFINER
2780- SET search_path TO public
2781+ SET search_path TO 'public'
2782 AS $$
2783 BEGIN
2784 INSERT INTO lp_TeamParticipation SELECT NEW.*;
2785@@ -1720,9 +1893,9 @@
2786 $$;
2787
2788
2789-CREATE FUNCTION lp_mirror_teamparticipation_upd() RETURNS trigger
2790+CREATE FUNCTION public.lp_mirror_teamparticipation_upd() RETURNS trigger
2791 LANGUAGE plpgsql SECURITY DEFINER
2792- SET search_path TO public
2793+ SET search_path TO 'public'
2794 AS $$
2795 BEGIN
2796 UPDATE lp_TeamParticipation
2797@@ -1735,28 +1908,9 @@
2798 $$;
2799
2800
2801-CREATE FUNCTION maintain_transitively_private() RETURNS trigger
2802- LANGUAGE plpgsql
2803- AS $$
2804-BEGIN
2805- IF TG_OP = 'UPDATE' THEN
2806- IF (NEW.stacked_on IS NOT DISTINCT FROM OLD.stacked_on
2807- AND NEW.private IS NOT DISTINCT FROM OLD.private) THEN
2808- RETURN NULL;
2809- END IF;
2810- END IF;
2811- PERFORM update_transitively_private(NEW.id);
2812- RETURN NULL;
2813-END;
2814-$$;
2815-
2816-
2817-COMMENT ON FUNCTION maintain_transitively_private() IS 'Trigger maintaining the Branch transitively_private column';
2818-
2819-
2820-CREATE FUNCTION message_copy_owner_to_bugmessage() RETURNS trigger
2821+CREATE FUNCTION public.message_copy_owner_to_bugmessage() RETURNS trigger
2822 LANGUAGE plpgsql SECURITY DEFINER
2823- SET search_path TO public
2824+ SET search_path TO 'public'
2825 AS $$
2826 BEGIN
2827 IF NEW.owner != OLD.owner THEN
2828@@ -1770,12 +1924,12 @@
2829 $$;
2830
2831
2832-COMMENT ON FUNCTION message_copy_owner_to_bugmessage() IS 'Copies the message owner into bugmessage when message changes.';
2833-
2834-
2835-CREATE FUNCTION message_copy_owner_to_questionmessage() RETURNS trigger
2836+COMMENT ON FUNCTION public.message_copy_owner_to_bugmessage() IS 'Copies the message owner into bugmessage when message changes.';
2837+
2838+
2839+CREATE FUNCTION public.message_copy_owner_to_questionmessage() RETURNS trigger
2840 LANGUAGE plpgsql SECURITY DEFINER
2841- SET search_path TO public
2842+ SET search_path TO 'public'
2843 AS $$
2844 BEGIN
2845 IF NEW.owner != OLD.owner THEN
2846@@ -1789,10 +1943,10 @@
2847 $$;
2848
2849
2850-COMMENT ON FUNCTION message_copy_owner_to_questionmessage() IS 'Copies the message owner into questionmessage when message changes.';
2851-
2852-
2853-CREATE FUNCTION milestone_sort_key(dateexpected timestamp without time zone, name text) RETURNS text
2854+COMMENT ON FUNCTION public.message_copy_owner_to_questionmessage() IS 'Copies the message owner into questionmessage when message changes.';
2855+
2856+
2857+CREATE FUNCTION public.milestone_sort_key(dateexpected timestamp without time zone, name text) RETURNS text
2858 LANGUAGE plpythonu IMMUTABLE
2859 AS $$
2860 # If this method is altered, then any functional indexes using it
2861@@ -1813,10 +1967,10 @@
2862 $$;
2863
2864
2865-COMMENT ON FUNCTION milestone_sort_key(dateexpected timestamp without time zone, name text) IS 'Sort by the Milestone dateexpected and name. If the dateexpected is NULL, then it is converted to a date far in the future, so it will be sorted as a milestone in the future.';
2866-
2867-
2868-CREATE FUNCTION mv_branch_distribution_update() RETURNS trigger
2869+COMMENT ON FUNCTION public.milestone_sort_key(dateexpected timestamp without time zone, name text) IS 'Sort by the Milestone dateexpected and name. If the dateexpected is NULL, then it is converted to a date far in the future, so it will be sorted as a milestone in the future.';
2870+
2871+
2872+CREATE FUNCTION public.mv_branch_distribution_update() RETURNS trigger
2873 LANGUAGE plpgsql
2874 AS $$
2875 BEGIN
2876@@ -1834,10 +1988,10 @@
2877 $$;
2878
2879
2880-COMMENT ON FUNCTION mv_branch_distribution_update() IS 'Maintain Branch name cache when Distribution is modified.';
2881-
2882-
2883-CREATE FUNCTION mv_branch_distroseries_update() RETURNS trigger
2884+COMMENT ON FUNCTION public.mv_branch_distribution_update() IS 'Maintain Branch name cache when Distribution is modified.';
2885+
2886+
2887+CREATE FUNCTION public.mv_branch_distroseries_update() RETURNS trigger
2888 LANGUAGE plpgsql
2889 AS $$
2890 BEGIN
2891@@ -1853,10 +2007,10 @@
2892 $$;
2893
2894
2895-COMMENT ON FUNCTION mv_branch_distroseries_update() IS 'Maintain Branch name cache when Distroseries is modified.';
2896-
2897-
2898-CREATE FUNCTION mv_branch_person_update() RETURNS trigger
2899+COMMENT ON FUNCTION public.mv_branch_distroseries_update() IS 'Maintain Branch name cache when Distroseries is modified.';
2900+
2901+
2902+CREATE FUNCTION public.mv_branch_person_update() RETURNS trigger
2903 LANGUAGE plpgsql
2904 AS $$
2905 DECLARE
2906@@ -1873,10 +2027,10 @@
2907 $$;
2908
2909
2910-COMMENT ON FUNCTION mv_branch_person_update() IS 'Maintain Branch name cache when Person is modified.';
2911-
2912-
2913-CREATE FUNCTION mv_branch_product_update() RETURNS trigger
2914+COMMENT ON FUNCTION public.mv_branch_person_update() IS 'Maintain Branch name cache when Person is modified.';
2915+
2916+
2917+CREATE FUNCTION public.mv_branch_product_update() RETURNS trigger
2918 LANGUAGE plpgsql
2919 AS $$
2920 DECLARE
2921@@ -1893,12 +2047,12 @@
2922 $$;
2923
2924
2925-COMMENT ON FUNCTION mv_branch_product_update() IS 'Maintain Branch name cache when Product is modified.';
2926-
2927-
2928-CREATE FUNCTION mv_pillarname_distribution() RETURNS trigger
2929+COMMENT ON FUNCTION public.mv_branch_product_update() IS 'Maintain Branch name cache when Product is modified.';
2930+
2931+
2932+CREATE FUNCTION public.mv_pillarname_distribution() RETURNS trigger
2933 LANGUAGE plpgsql SECURITY DEFINER
2934- SET search_path TO public
2935+ SET search_path TO 'public'
2936 AS $$
2937 BEGIN
2938 IF TG_OP = 'INSERT' THEN
2939@@ -1912,12 +2066,12 @@
2940 $$;
2941
2942
2943-COMMENT ON FUNCTION mv_pillarname_distribution() IS 'Trigger maintaining the PillarName table';
2944-
2945-
2946-CREATE FUNCTION mv_pillarname_product() RETURNS trigger
2947+COMMENT ON FUNCTION public.mv_pillarname_distribution() IS 'Trigger maintaining the PillarName table';
2948+
2949+
2950+CREATE FUNCTION public.mv_pillarname_product() RETURNS trigger
2951 LANGUAGE plpgsql SECURITY DEFINER
2952- SET search_path TO public
2953+ SET search_path TO 'public'
2954 AS $$
2955 BEGIN
2956 IF TG_OP = 'INSERT' THEN
2957@@ -1932,12 +2086,12 @@
2958 $$;
2959
2960
2961-COMMENT ON FUNCTION mv_pillarname_product() IS 'Trigger maintaining the PillarName table';
2962-
2963-
2964-CREATE FUNCTION mv_pillarname_project() RETURNS trigger
2965+COMMENT ON FUNCTION public.mv_pillarname_product() IS 'Trigger maintaining the PillarName table';
2966+
2967+
2968+CREATE FUNCTION public.mv_pillarname_project() RETURNS trigger
2969 LANGUAGE plpgsql SECURITY DEFINER
2970- SET search_path TO public
2971+ SET search_path TO 'public'
2972 AS $$
2973 BEGIN
2974 IF TG_OP = 'INSERT' THEN
2975@@ -1952,221 +2106,47 @@
2976 $$;
2977
2978
2979-COMMENT ON FUNCTION mv_pillarname_project() IS 'Trigger maintaining the PillarName table';
2980-
2981-
2982-CREATE FUNCTION mv_pofiletranslator_pomsgset() RETURNS trigger
2983- LANGUAGE plpgsql
2984- AS $$
2985-BEGIN
2986- IF TG_OP = 'DELETE' THEN
2987- RAISE EXCEPTION
2988- 'Deletions from POMsgSet not supported by the POFileTranslator materialized view';
2989- ELSIF TG_OP = 'UPDATE' THEN
2990- IF OLD.pofile != NEW.pofile THEN
2991- RAISE EXCEPTION
2992- 'Changing POMsgSet.pofile not supported by the POFileTranslator materialized view';
2993- END IF;
2994- END IF;
2995- RETURN NEW;
2996-END;
2997-$$;
2998-
2999-
3000-COMMENT ON FUNCTION mv_pofiletranslator_pomsgset() IS 'Trigger enforing no POMsgSet deletions or POMsgSet.pofile changes';
3001-
3002-
3003-CREATE FUNCTION mv_pofiletranslator_posubmission() RETURNS trigger
3004- LANGUAGE plpgsql SECURITY DEFINER
3005- AS $$
3006-DECLARE
3007- v_pofile INTEGER;
3008- v_trash_old BOOLEAN;
3009-BEGIN
3010- -- If we are deleting a row, we need to remove the existing
3011- -- POFileTranslator row and reinsert the historical data if it exists.
3012- -- We also treat UPDATEs that change the key (person, pofile) the same
3013- -- as deletes. UPDATEs that don't change these columns are treated like
3014- -- INSERTs below.
3015- IF TG_OP = 'INSERT' THEN
3016- v_trash_old := FALSE;
3017- ELSIF TG_OP = 'DELETE' THEN
3018- v_trash_old := TRUE;
3019- ELSE -- UPDATE
3020- v_trash_old = (
3021- OLD.person != NEW.person OR OLD.pomsgset != NEW.pomsgset
3022- );
3023- END IF;
3024-
3025- IF v_trash_old THEN
3026-
3027- -- Delete the old record.
3028- DELETE FROM POFileTranslator USING POMsgSet
3029- WHERE POFileTranslator.pofile = POMsgSet.pofile
3030- AND POFileTranslator.person = OLD.person
3031- AND POMsgSet.id = OLD.pomsgset;
3032-
3033- -- Insert a past record if there is one.
3034- INSERT INTO POFileTranslator (
3035- person, pofile, latest_posubmission, date_last_touched
3036- )
3037- SELECT DISTINCT ON (POSubmission.person, POMsgSet.pofile)
3038- POSubmission.person, POMsgSet.pofile,
3039- POSubmission.id, POSubmission.datecreated
3040- FROM POSubmission, POMsgSet
3041- WHERE POSubmission.pomsgset = POMsgSet.id
3042- AND POSubmission.pomsgset = OLD.pomsgset
3043- AND POSubmission.person = OLD.person
3044- ORDER BY
3045- POSubmission.person, POMsgSet.pofile,
3046- POSubmission.datecreated DESC, POSubmission.id DESC;
3047-
3048- -- No NEW with DELETE, so we can short circuit and leave.
3049- IF TG_OP = 'DELETE' THEN
3050- RETURN NULL; -- Ignored because this is an AFTER trigger
3051- END IF;
3052- END IF;
3053-
3054- -- Get our new pofile id
3055- SELECT INTO v_pofile POMsgSet.pofile FROM POMsgSet
3056- WHERE POMsgSet.id = NEW.pomsgset;
3057-
3058- -- Standard 'upsert' loop to avoid race conditions.
3059- LOOP
3060- UPDATE POFileTranslator
3061- SET
3062- date_last_touched = CURRENT_TIMESTAMP AT TIME ZONE 'UTC',
3063- latest_posubmission = NEW.id
3064- WHERE
3065- person = NEW.person
3066- AND pofile = v_pofile;
3067- IF found THEN
3068- RETURN NULL; -- Return value ignored as this is an AFTER trigger
3069- END IF;
3070-
3071- BEGIN
3072- INSERT INTO POFileTranslator (person, pofile, latest_posubmission)
3073- VALUES (NEW.person, v_pofile, NEW.id);
3074- RETURN NULL; -- Return value ignored as this is an AFTER trigger
3075- EXCEPTION WHEN unique_violation THEN
3076- -- do nothing
3077- END;
3078- END LOOP;
3079-END;
3080-$$;
3081-
3082-
3083-COMMENT ON FUNCTION mv_pofiletranslator_posubmission() IS 'Trigger maintaining the POFileTranslator table';
3084-
3085-
3086-CREATE FUNCTION mv_pofiletranslator_translationmessage() RETURNS trigger
3087- LANGUAGE plpgsql SECURITY DEFINER
3088- SET search_path TO public
3089- AS $$
3090-DECLARE
3091- v_trash_old BOOLEAN;
3092-BEGIN
3093- -- If we are deleting a row, we need to remove the existing
3094- -- POFileTranslator row and reinsert the historical data if it exists.
3095- -- We also treat UPDATEs that change the key (submitter) the same
3096- -- as deletes. UPDATEs that don't change these columns are treated like
3097- -- INSERTs below.
3098- IF TG_OP = 'INSERT' THEN
3099- v_trash_old := FALSE;
3100- ELSIF TG_OP = 'DELETE' THEN
3101- v_trash_old := TRUE;
3102- ELSE -- UPDATE
3103- v_trash_old = (
3104- OLD.submitter != NEW.submitter
3105- );
3106- END IF;
3107-
3108- IF v_trash_old THEN
3109- -- Was this somebody's most-recently-changed message?
3110- -- If so, delete the entry for that change.
3111- DELETE FROM POFileTranslator
3112- WHERE latest_message = OLD.id;
3113- IF FOUND THEN
3114- -- We deleted the entry for somebody's latest contribution.
3115- -- Find that person's latest remaining contribution and
3116- -- create a new record for that.
3117- INSERT INTO POFileTranslator (
3118- person, pofile, latest_message, date_last_touched
3119- )
3120- SELECT DISTINCT ON (person, pofile.id)
3121- new_latest_message.submitter AS person,
3122- pofile.id,
3123- new_latest_message.id,
3124- greatest(new_latest_message.date_created,
3125- new_latest_message.date_reviewed)
3126- FROM POFile
3127- JOIN TranslationTemplateItem AS old_template_item
3128- ON OLD.potmsgset = old_template_item.potmsgset AND
3129- old_template_item.potemplate = pofile.potemplate AND
3130- pofile.language = OLD.language
3131- JOIN TranslationTemplateItem AS new_template_item
3132- ON (old_template_item.potemplate =
3133- new_template_item.potemplate)
3134- JOIN TranslationMessage AS new_latest_message
3135- ON new_latest_message.potmsgset =
3136- new_template_item.potmsgset AND
3137- new_latest_message.language = OLD.language
3138- LEFT OUTER JOIN POfileTranslator AS ExistingEntry
3139- ON ExistingEntry.person = OLD.submitter AND
3140- ExistingEntry.pofile = POFile.id
3141- WHERE
3142- new_latest_message.submitter = OLD.submitter AND
3143- ExistingEntry IS NULL
3144- ORDER BY new_latest_message.submitter, pofile.id,
3145- new_latest_message.date_created DESC,
3146- new_latest_message.id DESC;
3147- END IF;
3148-
3149- -- No NEW with DELETE, so we can short circuit and leave.
3150- IF TG_OP = 'DELETE' THEN
3151- RETURN NULL; -- Ignored because this is an AFTER trigger
3152- END IF;
3153- END IF;
3154-
3155- -- Standard 'upsert' loop to avoid race conditions.
3156- LOOP
3157- UPDATE POFileTranslator
3158- SET
3159- date_last_touched = CURRENT_TIMESTAMP AT TIME ZONE 'UTC',
3160- latest_message = NEW.id
3161- FROM POFile, TranslationTemplateItem
3162- WHERE person = NEW.submitter AND
3163- TranslationTemplateItem.potmsgset=NEW.potmsgset AND
3164- TranslationTemplateItem.potemplate=pofile.potemplate AND
3165- pofile.language=NEW.language AND
3166- POFileTranslator.pofile = pofile.id;
3167- IF found THEN
3168- RETURN NULL; -- Return value ignored as this is an AFTER trigger
3169- END IF;
3170-
3171- BEGIN
3172- INSERT INTO POFileTranslator (person, pofile, latest_message)
3173- SELECT DISTINCT ON (NEW.submitter, pofile.id)
3174- NEW.submitter, pofile.id, NEW.id
3175- FROM TranslationTemplateItem
3176- JOIN POFile
3177- ON pofile.language = NEW.language AND
3178- pofile.potemplate = translationtemplateitem.potemplate
3179- WHERE
3180- TranslationTemplateItem.potmsgset = NEW.potmsgset;
3181- RETURN NULL; -- Return value ignored as this is an AFTER trigger
3182- EXCEPTION WHEN unique_violation THEN
3183- -- do nothing
3184- END;
3185- END LOOP;
3186-END;
3187-$$;
3188-
3189-
3190-COMMENT ON FUNCTION mv_pofiletranslator_translationmessage() IS 'Trigger maintaining the POFileTranslator table';
3191-
3192-
3193-CREATE FUNCTION mv_validpersonorteamcache_emailaddress() RETURNS trigger
3194+COMMENT ON FUNCTION public.mv_pillarname_project() IS 'Trigger maintaining the PillarName table';
3195+
3196+
3197+CREATE FUNCTION public.mv_pofiletranslator_translationmessage() RETURNS trigger
3198+ LANGUAGE plpgsql SECURITY DEFINER
3199+ SET search_path TO 'public'
3200+ AS $$
3201+BEGIN
3202+ -- Update any existing entries.
3203+ UPDATE POFileTranslator
3204+ SET date_last_touched = CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
3205+ FROM POFile, TranslationTemplateItem
3206+ WHERE person = NEW.submitter AND
3207+ TranslationTemplateItem.potmsgset = NEW.potmsgset AND
3208+ TranslationTemplateItem.potemplate = POFile.potemplate AND
3209+ POFile.language = NEW.language AND
3210+ POFileTranslator.pofile = POFile.id;
3211+
3212+ -- Insert any missing entries.
3213+ INSERT INTO POFileTranslator (person, pofile)
3214+ SELECT DISTINCT NEW.submitter, POFile.id
3215+ FROM TranslationTemplateItem
3216+ JOIN POFile ON
3217+ POFile.language = NEW.language AND
3218+ POFile.potemplate = TranslationTemplateItem.potemplate
3219+ WHERE
3220+ TranslationTemplateItem.potmsgset = NEW.potmsgset AND
3221+ NOT EXISTS (
3222+ SELECT *
3223+ FROM POFileTranslator
3224+ WHERE person = NEW.submitter AND pofile = POFile.id
3225+ );
3226+ RETURN NULL;
3227+END;
3228+$$;
3229+
3230+
3231+COMMENT ON FUNCTION public.mv_pofiletranslator_translationmessage() IS 'Trigger maintaining the POFileTranslator table';
3232+
3233+
3234+CREATE FUNCTION public.mv_validpersonorteamcache_emailaddress() RETURNS trigger
3235 LANGUAGE plpythonu SECURITY DEFINER
3236 AS $_$
3237 # This trigger function keeps the ValidPersonOrTeamCache materialized
3238@@ -2258,10 +2238,10 @@
3239 $_$;
3240
3241
3242-COMMENT ON FUNCTION mv_validpersonorteamcache_emailaddress() IS 'A trigger for maintaining the ValidPersonOrTeamCache eager materialized view when changes are made to the EmailAddress table';
3243-
3244-
3245-CREATE FUNCTION mv_validpersonorteamcache_person() RETURNS trigger
3246+COMMENT ON FUNCTION public.mv_validpersonorteamcache_emailaddress() IS 'A trigger for maintaining the ValidPersonOrTeamCache eager materialized view when changes are made to the EmailAddress table';
3247+
3248+
3249+CREATE FUNCTION public.mv_validpersonorteamcache_person() RETURNS trigger
3250 LANGUAGE plpythonu SECURITY DEFINER
3251 AS $_$
3252 # This trigger function could be simplified by simply issuing
3253@@ -2322,12 +2302,12 @@
3254 $_$;
3255
3256
3257-COMMENT ON FUNCTION mv_validpersonorteamcache_person() IS 'A trigger for maintaining the ValidPersonOrTeamCache eager materialized view when changes are made to the Person table';
3258-
3259-
3260-CREATE FUNCTION name_blacklist_match(text, integer) RETURNS integer
3261+COMMENT ON FUNCTION public.mv_validpersonorteamcache_person() IS 'A trigger for maintaining the ValidPersonOrTeamCache eager materialized view when changes are made to the Person table';
3262+
3263+
3264+CREATE FUNCTION public.name_blacklist_match(text, integer) RETURNS integer
3265 LANGUAGE plpythonu STABLE STRICT SECURITY DEFINER
3266- SET search_path TO public
3267+ SET search_path TO 'public'
3268 AS $_$
3269 import re
3270 name = args[0].decode("UTF-8")
3271@@ -2396,10 +2376,10 @@
3272 $_$;
3273
3274
3275-COMMENT ON FUNCTION name_blacklist_match(text, integer) IS 'Return the id of the row in the NameBlacklist table that matches the given name, or NULL if no regexps in the NameBlacklist table match.';
3276-
3277-
3278-CREATE FUNCTION null_count(p_values anyarray) RETURNS integer
3279+COMMENT ON FUNCTION public.name_blacklist_match(text, integer) IS 'Return the id of the row in the NameBlacklist table that matches the given name, or NULL if no regexps in the NameBlacklist table match.';
3280+
3281+
3282+CREATE FUNCTION public.null_count(p_values anyarray) RETURNS integer
3283 LANGUAGE plpgsql IMMUTABLE STRICT
3284 AS $$
3285 DECLARE
3286@@ -2416,10 +2396,10 @@
3287 $$;
3288
3289
3290-COMMENT ON FUNCTION null_count(p_values anyarray) IS 'Return the number of NULLs in the first row of the given array.';
3291-
3292-
3293-CREATE FUNCTION packageset_deleted_trig() RETURNS trigger
3294+COMMENT ON FUNCTION public.null_count(p_values anyarray) IS 'Return the number of NULLs in the first row of the given array.';
3295+
3296+
3297+CREATE FUNCTION public.packageset_deleted_trig() RETURNS trigger
3298 LANGUAGE plpgsql
3299 AS $$
3300 BEGIN
3301@@ -2436,10 +2416,10 @@
3302 $$;
3303
3304
3305-COMMENT ON FUNCTION packageset_deleted_trig() IS 'Remove any DAG edges leading to/from the deleted package set.';
3306-
3307-
3308-CREATE FUNCTION packageset_inserted_trig() RETURNS trigger
3309+COMMENT ON FUNCTION public.packageset_deleted_trig() IS 'Remove any DAG edges leading to/from the deleted package set.';
3310+
3311+
3312+CREATE FUNCTION public.packageset_inserted_trig() RETURNS trigger
3313 LANGUAGE plpgsql
3314 AS $$
3315 BEGIN
3316@@ -2453,10 +2433,10 @@
3317 $$;
3318
3319
3320-COMMENT ON FUNCTION packageset_inserted_trig() IS 'Insert self-referencing DAG edge when a new package set is inserted.';
3321-
3322-
3323-CREATE FUNCTION packagesetinclusion_deleted_trig() RETURNS trigger
3324+COMMENT ON FUNCTION public.packageset_inserted_trig() IS 'Insert self-referencing DAG edge when a new package set is inserted.';
3325+
3326+
3327+CREATE FUNCTION public.packagesetinclusion_deleted_trig() RETURNS trigger
3328 LANGUAGE plpgsql
3329 AS $$
3330 BEGIN
3331@@ -2524,10 +2504,10 @@
3332 $$;
3333
3334
3335-COMMENT ON FUNCTION packagesetinclusion_deleted_trig() IS 'Maintain the transitive closure in the DAG when an edge leading to/from a package set is deleted.';
3336-
3337-
3338-CREATE FUNCTION packagesetinclusion_inserted_trig() RETURNS trigger
3339+COMMENT ON FUNCTION public.packagesetinclusion_deleted_trig() IS 'Maintain the transitive closure in the DAG when an edge leading to/from a package set is deleted.';
3340+
3341+
3342+CREATE FUNCTION public.packagesetinclusion_inserted_trig() RETURNS trigger
3343 LANGUAGE plpgsql
3344 AS $$
3345 BEGIN
3346@@ -2598,10 +2578,10 @@
3347 $$;
3348
3349
3350-COMMENT ON FUNCTION packagesetinclusion_inserted_trig() IS 'Maintain the transitive closure in the DAG for a newly inserted edge leading to/from a package set.';
3351-
3352-
3353-CREATE FUNCTION person_sort_key(displayname text, name text) RETURNS text
3354+COMMENT ON FUNCTION public.packagesetinclusion_inserted_trig() IS 'Maintain the transitive closure in the DAG for a newly inserted edge leading to/from a package set.';
3355+
3356+
3357+CREATE FUNCTION public.person_sort_key(displayname text, name text) RETURNS text
3358 LANGUAGE plpythonu IMMUTABLE STRICT
3359 AS $$
3360 # NB: If this implementation is changed, the person_sort_idx needs to be
3361@@ -2623,32 +2603,17 @@
3362 $$;
3363
3364
3365-COMMENT ON FUNCTION person_sort_key(displayname text, name text) IS 'Return a string suitable for sorting people on, generated by stripping noise out of displayname and concatenating name';
3366-
3367-
3368-CREATE FUNCTION pgstattuple(text) RETURNS pgstattuple_type
3369- LANGUAGE c STRICT
3370- AS '$libdir/pgstattuple', 'pgstattuple';
3371-
3372-
3373-CREATE FUNCTION pgstattuple(oid) RETURNS pgstattuple_type
3374- LANGUAGE c STRICT
3375- AS '$libdir/pgstattuple', 'pgstattuplebyid';
3376-
3377-
3378-CREATE FUNCTION plpgsql_call_handler() RETURNS language_handler
3379+COMMENT ON FUNCTION public.person_sort_key(displayname text, name text) IS 'Return a string suitable for sorting people on, generated by stripping noise out of displayname and concatenating name';
3380+
3381+
3382+CREATE FUNCTION public.plpgsql_call_handler() RETURNS language_handler
3383 LANGUAGE c
3384 AS '$libdir/plpgsql', 'plpgsql_call_handler';
3385
3386
3387-CREATE FUNCTION plpython_call_handler() RETURNS language_handler
3388- LANGUAGE c
3389- AS '$libdir/plpython', 'plpython_call_handler';
3390-
3391-
3392-CREATE FUNCTION questionmessage_copy_owner_from_message() RETURNS trigger
3393+CREATE FUNCTION public.questionmessage_copy_owner_from_message() RETURNS trigger
3394 LANGUAGE plpgsql SECURITY DEFINER
3395- SET search_path TO public
3396+ SET search_path TO 'public'
3397 AS $$
3398 BEGIN
3399 IF TG_OP = 'INSERT' THEN
3400@@ -2671,12 +2636,12 @@
3401 $$;
3402
3403
3404-COMMENT ON FUNCTION questionmessage_copy_owner_from_message() IS 'Copies the message owner into QuestionMessage when QuestionMessage changes.';
3405-
3406-
3407-CREATE FUNCTION replication_lag() RETURNS interval
3408+COMMENT ON FUNCTION public.questionmessage_copy_owner_from_message() IS 'Copies the message owner into QuestionMessage when QuestionMessage changes.';
3409+
3410+
3411+CREATE FUNCTION public.replication_lag() RETURNS interval
3412 LANGUAGE plpgsql STABLE SECURITY DEFINER
3413- SET search_path TO public
3414+ SET search_path TO 'public'
3415 AS $$
3416 DECLARE
3417 v_lag interval;
3418@@ -2693,12 +2658,12 @@
3419 $$;
3420
3421
3422-COMMENT ON FUNCTION replication_lag() IS 'Returns the worst lag time in our cluster, or NULL if not a replicated installation. Only returns meaningful results on the lpmain replication set master.';
3423-
3424-
3425-CREATE FUNCTION replication_lag(node_id integer) RETURNS interval
3426+COMMENT ON FUNCTION public.replication_lag() IS 'Returns the worst lag time in our cluster, or NULL if not a replicated installation. Only returns meaningful results on the lpmain replication set master.';
3427+
3428+
3429+CREATE FUNCTION public.replication_lag(node_id integer) RETURNS interval
3430 LANGUAGE plpgsql STABLE SECURITY DEFINER
3431- SET search_path TO public
3432+ SET search_path TO 'public'
3433 AS $$
3434 DECLARE
3435 v_lag interval;
3436@@ -2717,10 +2682,10 @@
3437 $$;
3438
3439
3440-COMMENT ON FUNCTION replication_lag(node_id integer) IS 'Returns the lag time of the lpmain replication set to the given node, or NULL if not a replicated installation. The node id parameter can be obtained by calling getlocalnodeid() on the relevant database. This function only returns meaningful results on the lpmain replication set master.';
3441-
3442-
3443-CREATE FUNCTION sane_version(text) RETURNS boolean
3444+COMMENT ON FUNCTION public.replication_lag(node_id integer) IS 'Returns the lag time of the lpmain replication set to the given node, or NULL if not a replicated installation. The node id parameter can be obtained by calling getlocalnodeid() on the relevant database. This function only returns meaningful results on the lpmain replication set master.';
3445+
3446+
3447+CREATE FUNCTION public.sane_version(text) RETURNS boolean
3448 LANGUAGE plpythonu IMMUTABLE STRICT
3449 AS $_$
3450 import re
3451@@ -2733,12 +2698,12 @@
3452 $_$;
3453
3454
3455-COMMENT ON FUNCTION sane_version(text) IS 'A sane version number for use by ProductRelease and DistroRelease. We may make it less strict if required, but it would be nice if we can enforce simple version strings because we use them in URLs';
3456-
3457-
3458-CREATE FUNCTION set_bug_date_last_message() RETURNS trigger
3459+COMMENT ON FUNCTION public.sane_version(text) IS 'A sane version number for use by ProductRelease and DistroRelease. We may make it less strict if required, but it would be nice if we can enforce simple version strings because we use them in URLs';
3460+
3461+
3462+CREATE FUNCTION public.set_bug_date_last_message() RETURNS trigger
3463 LANGUAGE plpgsql SECURITY DEFINER
3464- SET search_path TO public
3465+ SET search_path TO 'public'
3466 AS $$
3467 BEGIN
3468 IF TG_OP = 'INSERT' THEN
3469@@ -2763,10 +2728,10 @@
3470 $$;
3471
3472
3473-COMMENT ON FUNCTION set_bug_date_last_message() IS 'AFTER INSERT trigger on BugMessage maintaining the Bug.date_last_message column';
3474-
3475-
3476-CREATE FUNCTION set_bug_message_count() RETURNS trigger
3477+COMMENT ON FUNCTION public.set_bug_date_last_message() IS 'AFTER INSERT trigger on BugMessage maintaining the Bug.date_last_message column';
3478+
3479+
3480+CREATE FUNCTION public.set_bug_message_count() RETURNS trigger
3481 LANGUAGE plpgsql
3482 AS $$
3483 BEGIN
3484@@ -2791,10 +2756,10 @@
3485 $$;
3486
3487
3488-COMMENT ON FUNCTION set_bug_message_count() IS 'AFTER UPDATE trigger on BugAffectsPerson maintaining the Bug.users_affected_count column';
3489-
3490-
3491-CREATE FUNCTION set_bug_number_of_duplicates() RETURNS trigger
3492+COMMENT ON FUNCTION public.set_bug_message_count() IS 'AFTER UPDATE trigger on BugAffectsPerson maintaining the Bug.users_affected_count column';
3493+
3494+
3495+CREATE FUNCTION public.set_bug_number_of_duplicates() RETURNS trigger
3496 LANGUAGE plpgsql
3497 AS $$
3498 BEGIN
3499@@ -2826,10 +2791,10 @@
3500 $$;
3501
3502
3503-COMMENT ON FUNCTION set_bug_number_of_duplicates() IS 'AFTER UPDATE trigger on Bug maintaining the Bug.number_of_duplicates column';
3504-
3505-
3506-CREATE FUNCTION set_bug_users_affected_count() RETURNS trigger
3507+COMMENT ON FUNCTION public.set_bug_number_of_duplicates() IS 'AFTER UPDATE trigger on Bug maintaining the Bug.number_of_duplicates column';
3508+
3509+
3510+CREATE FUNCTION public.set_bug_users_affected_count() RETURNS trigger
3511 LANGUAGE plpgsql
3512 AS $$
3513 BEGIN
3514@@ -2878,7 +2843,7 @@
3515 $$;
3516
3517
3518-CREATE FUNCTION set_bugtask_date_milestone_set() RETURNS trigger
3519+CREATE FUNCTION public.set_bugtask_date_milestone_set() RETURNS trigger
3520 LANGUAGE plpgsql
3521 AS $$
3522 BEGIN
3523@@ -2924,10 +2889,10 @@
3524 $$;
3525
3526
3527-COMMENT ON FUNCTION set_bugtask_date_milestone_set() IS 'Update BugTask.date_milestone_set when BugTask.milestone is changed.';
3528-
3529-
3530-CREATE FUNCTION set_date_status_set() RETURNS trigger
3531+COMMENT ON FUNCTION public.set_bugtask_date_milestone_set() IS 'Update BugTask.date_milestone_set when BugTask.milestone is changed.';
3532+
3533+
3534+CREATE FUNCTION public.set_date_status_set() RETURNS trigger
3535 LANGUAGE plpgsql
3536 AS $$
3537 BEGIN
3538@@ -2939,74 +2904,10 @@
3539 $$;
3540
3541
3542-COMMENT ON FUNCTION set_date_status_set() IS 'BEFORE UPDATE trigger on Account that maintains the Account.date_status_set column.';
3543-
3544-
3545-CREATE FUNCTION set_openid_identifier() RETURNS trigger
3546- LANGUAGE plpythonu
3547- AS $$
3548- # If someone is trying to explicitly set the openid_identifier, let them.
3549- # This also causes openid_identifiers to be left alone if this is an
3550- # UPDATE trigger.
3551- if TD['new']['openid_identifier'] is not None:
3552- return None
3553-
3554- from random import choice
3555-
3556- # Non display confusing characters
3557- chars = '34678bcdefhkmnprstwxyzABCDEFGHJKLMNPQRTWXY'
3558-
3559- # character length of tokens. Can be increased, decreased or even made
3560- # random - Launchpad does not care. 7 means it takes 40 bytes to store
3561- # a null-terminated Launchpad identity URL on the current domain name.
3562- length=7
3563-
3564- loop_count = 0
3565- while loop_count < 20000:
3566- # Generate a random openid_identifier
3567- oid = ''.join(choice(chars) for count in range(length))
3568-
3569- # Check if the oid is already in the db, although this is pretty
3570- # unlikely
3571- rv = plpy.execute("""
3572- SELECT COUNT(*) AS num FROM Person WHERE openid_identifier = '%s'
3573- """ % oid, 1)
3574- if rv[0]['num'] == 0:
3575- TD['new']['openid_identifier'] = oid
3576- return "MODIFY"
3577- loop_count += 1
3578- if loop_count == 1:
3579- plpy.warning(
3580- 'Clash generating unique openid_identifier. '
3581- 'Increase length if you see this warning too much.')
3582- plpy.error(
3583- "Unable to generate unique openid_identifier. "
3584- "Need to increase length of tokens.")
3585-$$;
3586-
3587-
3588-CREATE FUNCTION set_shipit_normalized_address() RETURNS trigger
3589- LANGUAGE plpgsql
3590- AS $$
3591- BEGIN
3592- NEW.normalized_address =
3593- lower(
3594- -- Strip off everything that's not alphanumeric
3595- -- characters.
3596- regexp_replace(
3597- coalesce(NEW.addressline1, '') || ' ' ||
3598- coalesce(NEW.addressline2, '') || ' ' ||
3599- coalesce(NEW.city, ''),
3600- '[^a-zA-Z0-9]+', '', 'g'));
3601- RETURN NEW;
3602- END;
3603-$$;
3604-
3605-
3606-COMMENT ON FUNCTION set_shipit_normalized_address() IS 'Store a normalized concatenation of the request''s address into the normalized_address column.';
3607-
3608-
3609-CREATE FUNCTION sha1(text) RETURNS character
3610+COMMENT ON FUNCTION public.set_date_status_set() IS 'BEFORE UPDATE trigger on Account that maintains the Account.date_status_set column.';
3611+
3612+
3613+CREATE FUNCTION public.sha1(text) RETURNS character
3614 LANGUAGE plpythonu IMMUTABLE STRICT
3615 AS $$
3616 import hashlib
3617@@ -3014,56 +2915,63 @@
3618 $$;
3619
3620
3621-COMMENT ON FUNCTION sha1(text) IS 'Return the SHA1 one way cryptographic hash as a string of 40 hex digits';
3622-
3623-
3624-CREATE FUNCTION summarise_bug(bug_row bug) RETURNS void
3625- LANGUAGE plpgsql
3626- AS $$
3627-DECLARE
3628- d bugsummary%ROWTYPE;
3629-BEGIN
3630- PERFORM ensure_bugsummary_temp_journal();
3631- FOR d IN SELECT * FROM bugsummary_locations(BUG_ROW) LOOP
3632- d.count = 1;
3633- PERFORM bug_summary_temp_journal_ins(d);
3634- END LOOP;
3635-END;
3636-$$;
3637-
3638-
3639-COMMENT ON FUNCTION summarise_bug(bug_row bug) IS 'AFTER summarise a bug row into bugsummary.';
3640-
3641-
3642-CREATE FUNCTION ulower(text) RETURNS text
3643+COMMENT ON FUNCTION public.sha1(text) IS 'Return the SHA1 one way cryptographic hash as a string of 40 hex digits';
3644+
3645+
3646+CREATE FUNCTION public.specification_denorm_access(spec_id integer) RETURNS void
3647+ LANGUAGE sql SECURITY DEFINER
3648+ SET search_path TO 'public'
3649+ AS $_$
3650+ UPDATE specification
3651+ SET access_policy = policies[1], access_grants = grants
3652+ FROM
3653+ build_access_cache(
3654+ (SELECT id FROM accessartifact WHERE specification = $1),
3655+ (SELECT information_type FROM specification WHERE id = $1))
3656+ AS (policies integer[], grants integer[])
3657+ WHERE id = $1;
3658+$_$;
3659+
3660+
3661+CREATE FUNCTION public.specification_maintain_access_cache_trig() RETURNS trigger
3662+ LANGUAGE plpgsql
3663+ AS $$
3664+BEGIN
3665+ PERFORM specification_denorm_access(NEW.id);
3666+ RETURN NULL;
3667+END;
3668+$$;
3669+
3670+
3671+CREATE FUNCTION public.summarise_bug(bug integer) RETURNS void
3672+ LANGUAGE plpgsql
3673+ AS $$
3674+BEGIN
3675+ PERFORM bugsummary_journal_bug(bug_row(bug), 1);
3676+END;
3677+$$;
3678+
3679+
3680+CREATE FUNCTION public.ulower(text) RETURNS text
3681 LANGUAGE plpythonu IMMUTABLE STRICT
3682 AS $$
3683 return args[0].decode('utf8').lower().encode('utf8')
3684 $$;
3685
3686
3687-COMMENT ON FUNCTION ulower(text) IS 'Return the lower case version of a UTF-8 encoded string.';
3688-
3689-
3690-CREATE FUNCTION unsummarise_bug(bug_row bug) RETURNS void
3691+COMMENT ON FUNCTION public.ulower(text) IS 'Return the lower case version of a UTF-8 encoded string.';
3692+
3693+
3694+CREATE FUNCTION public.unsummarise_bug(bug integer) RETURNS void
3695 LANGUAGE plpgsql
3696 AS $$
3697-DECLARE
3698- d bugsummary%ROWTYPE;
3699 BEGIN
3700- PERFORM ensure_bugsummary_temp_journal();
3701- FOR d IN SELECT * FROM bugsummary_locations(BUG_ROW) LOOP
3702- d.count = -1;
3703- PERFORM bug_summary_temp_journal_ins(d);
3704- END LOOP;
3705+ PERFORM bugsummary_journal_bug(bug_row(bug), -1);
3706 END;
3707 $$;
3708
3709
3710-COMMENT ON FUNCTION unsummarise_bug(bug_row bug) IS 'AFTER unsummarise a bug row from bugsummary.';
3711-
3712-
3713-CREATE FUNCTION update_branch_name_cache() RETURNS trigger
3714+CREATE FUNCTION public.update_branch_name_cache() RETURNS trigger
3715 LANGUAGE plpgsql
3716 AS $$
3717 DECLARE
3718@@ -3075,7 +2983,7 @@
3719 OR NEW.unique_name IS NULL
3720 OR OLD.owner_name <> NEW.owner_name
3721 OR OLD.unique_name <> NEW.unique_name
3722- OR (NEW.target_suffix IS NULL <> OLD.target_suffix IS NULL)
3723+ OR ((NEW.target_suffix IS NULL) <> (OLD.target_suffix IS NULL))
3724 OR COALESCE(OLD.target_suffix, '') <> COALESCE(NEW.target_suffix, '')
3725 OR OLD.name <> NEW.name
3726 OR OLD.owner <> NEW.owner
3727@@ -3111,12 +3019,12 @@
3728 $$;
3729
3730
3731-COMMENT ON FUNCTION update_branch_name_cache() IS 'Maintain the cached name columns in Branch.';
3732-
3733-
3734-CREATE FUNCTION update_database_disk_utilization() RETURNS void
3735+COMMENT ON FUNCTION public.update_branch_name_cache() IS 'Maintain the cached name columns in Branch.';
3736+
3737+
3738+CREATE FUNCTION public.update_database_disk_utilization() RETURNS void
3739 LANGUAGE sql SECURITY DEFINER
3740- SET search_path TO public
3741+ SET search_path TO 'public'
3742 AS $$
3743 INSERT INTO DatabaseDiskUtilization
3744 SELECT
3745@@ -3136,6 +3044,7 @@
3746 (stat).free_space,
3747 (stat).free_percent
3748 FROM (
3749+ -- Tables
3750 SELECT
3751 pg_namespace.nspname AS namespace,
3752 pg_class.relname AS name,
3753@@ -3151,6 +3060,7 @@
3754
3755 UNION ALL
3756
3757+ -- Indexes
3758 SELECT
3759 pg_namespace_table.nspname AS namespace,
3760 pg_class_table.relname AS name,
3761@@ -3163,12 +3073,15 @@
3762 pg_namespace AS pg_namespace_index,
3763 pg_class AS pg_class_table,
3764 pg_class AS pg_class_index,
3765- pg_index
3766+ pg_index,
3767+ pg_am
3768 WHERE
3769 pg_class_index.relkind = 'i'
3770+ AND pg_am.amname <> 'gin' -- pgstattuple doesn't support GIN
3771 AND pg_table_is_visible(pg_class_table.oid)
3772 AND pg_class_index.relnamespace = pg_namespace_index.oid
3773 AND pg_class_table.relnamespace = pg_namespace_table.oid
3774+ AND pg_class_index.relam = pg_am.oid
3775 AND pg_index.indexrelid = pg_class_index.oid
3776 AND pg_index.indrelid = pg_class_table.oid
3777
3778@@ -3208,20 +3121,22 @@
3779 pg_namespace AS pg_namespace_index,
3780 pg_class AS pg_class_table,
3781 pg_class AS pg_class_index,
3782- pg_class AS pg_class_toast
3783+ pg_class AS pg_class_toast,
3784+ pg_index
3785 WHERE
3786 pg_class_table.relnamespace = pg_namespace_table.oid
3787 AND pg_table_is_visible(pg_class_table.oid)
3788 AND pg_class_index.relnamespace = pg_namespace_index.oid
3789 AND pg_class_table.reltoastrelid = pg_class_toast.oid
3790- AND pg_class_index.oid = pg_class_toast.reltoastidxid
3791+ AND pg_class_index.oid = pg_index.indexrelid
3792+ AND pg_index.indrelid = pg_class_toast.oid
3793 ) AS whatever;
3794 $$;
3795
3796
3797-CREATE FUNCTION update_database_stats() RETURNS void
3798+CREATE FUNCTION public.update_database_stats() RETURNS void
3799 LANGUAGE plpythonu SECURITY DEFINER
3800- SET search_path TO public
3801+ SET search_path TO 'public'
3802 AS $_$
3803 import re
3804 import subprocess
3805@@ -3306,12 +3221,12 @@
3806 $_$;
3807
3808
3809-COMMENT ON FUNCTION update_database_stats() IS 'Copies rows from pg_stat_user_tables into DatabaseTableStats. We use a stored procedure because it is problematic for us to grant permissions on objects in the pg_catalog schema.';
3810-
3811-
3812-CREATE FUNCTION update_replication_lag_cache() RETURNS boolean
3813+COMMENT ON FUNCTION public.update_database_stats() IS 'Copies rows from pg_stat_user_tables into DatabaseTableStats. We use a stored procedure because it is problematic for us to grant permissions on objects in the pg_catalog schema.';
3814+
3815+
3816+CREATE FUNCTION public.update_replication_lag_cache() RETURNS boolean
3817 LANGUAGE plpgsql SECURITY DEFINER
3818- SET search_path TO public
3819+ SET search_path TO 'public'
3820 AS $$
3821 BEGIN
3822 DELETE FROM DatabaseReplicationLag;
3823@@ -3329,64 +3244,10 @@
3824 $$;
3825
3826
3827-COMMENT ON FUNCTION update_replication_lag_cache() IS 'Updates the DatabaseReplicationLag materialized view.';
3828-
3829-
3830-CREATE FUNCTION update_transitively_private(start_branch integer, _root_branch integer DEFAULT NULL::integer, _root_transitively_private boolean DEFAULT NULL::boolean) RETURNS void
3831- LANGUAGE plpgsql SECURITY DEFINER
3832- SET search_path TO public
3833- AS $$
3834-DECLARE
3835- root_transitively_private boolean := _root_transitively_private;
3836- root_branch int := _root_branch;
3837-BEGIN
3838- IF root_transitively_private IS NULL THEN
3839- -- We can't just trust the transitively_private flag of the
3840- -- branch we are stacked on, as if we are updating multiple
3841- -- records they will be updated in an indeterminate order.
3842- -- We need a recursive query.
3843- UPDATE Branch SET transitively_private = (
3844- WITH RECURSIVE stacked_branches AS (
3845- SELECT
3846- top_branch.id, top_branch.stacked_on, top_branch.private
3847- FROM Branch AS top_branch
3848- WHERE top_branch.id = start_branch
3849- UNION ALL
3850- SELECT
3851- sub_branch.id, sub_branch.stacked_on, sub_branch.private
3852- FROM stacked_branches, Branch AS sub_branch
3853- WHERE
3854- stacked_branches.stacked_on = sub_branch.id
3855- AND stacked_branches.stacked_on != start_branch
3856- -- Shortcircuit. No need to recurse if already private.
3857- AND stacked_branches.private IS FALSE
3858- )
3859- SELECT COUNT(*) > 0
3860- FROM stacked_branches
3861- WHERE private IS TRUE)
3862- WHERE Branch.id = start_branch
3863- RETURNING transitively_private INTO root_transitively_private;
3864- root_branch := start_branch;
3865- ELSE
3866- -- Now we have calculated the correct transitively_private flag
3867- -- we can trust it.
3868- UPDATE Branch SET
3869- transitively_private = GREATEST(private, root_transitively_private)
3870- WHERE id = root_branch;
3871- END IF;
3872-
3873- -- Recurse to branches stacked on this one.
3874- PERFORM update_transitively_private(
3875- start_branch, id, GREATEST(private, root_transitively_private))
3876- FROM Branch WHERE stacked_on = root_branch AND id != start_branch;
3877-END;
3878-$$;
3879-
3880-
3881-COMMENT ON FUNCTION update_transitively_private(start_branch integer, _root_branch integer, _root_transitively_private boolean) IS 'A branch is transitively private if it is private or is stacked on any transitively private branches.';
3882-
3883-
3884-CREATE FUNCTION valid_absolute_url(text) RETURNS boolean
3885+COMMENT ON FUNCTION public.update_replication_lag_cache() IS 'Updates the DatabaseReplicationLag materialized view.';
3886+
3887+
3888+CREATE FUNCTION public.valid_absolute_url(text) RETURNS boolean
3889 LANGUAGE plpythonu IMMUTABLE STRICT
3890 AS $$
3891 from urlparse import urlparse, uses_netloc
3892@@ -3403,10 +3264,10 @@
3893 $$;
3894
3895
3896-COMMENT ON FUNCTION valid_absolute_url(text) IS 'Ensure the given test is a valid absolute URL, containing both protocol and network location';
3897-
3898-
3899-CREATE FUNCTION valid_branch_name(text) RETURNS boolean
3900+COMMENT ON FUNCTION public.valid_absolute_url(text) IS 'Ensure the given test is a valid absolute URL, containing both protocol and network location';
3901+
3902+
3903+CREATE FUNCTION public.valid_branch_name(text) RETURNS boolean
3904 LANGUAGE plpythonu IMMUTABLE STRICT
3905 AS $$
3906 import re
3907@@ -3418,27 +3279,27 @@
3908 $$;
3909
3910
3911-COMMENT ON FUNCTION valid_branch_name(text) IS 'validate a branch name.
3912+COMMENT ON FUNCTION public.valid_branch_name(text) IS 'validate a branch name.
3913
3914 As per valid_name, except we allow uppercase and @';
3915
3916
3917-CREATE FUNCTION valid_cve(text) RETURNS boolean
3918+CREATE FUNCTION public.valid_cve(text) RETURNS boolean
3919 LANGUAGE plpythonu IMMUTABLE STRICT
3920 AS $_$
3921 import re
3922 name = args[0]
3923- pat = r"^(19|20)\d{2}-\d{4}$"
3924+ pat = r"^(19|20)\d{2}-\d{4,}$"
3925 if re.match(pat, name):
3926 return 1
3927 return 0
3928 $_$;
3929
3930
3931-COMMENT ON FUNCTION valid_cve(text) IS 'validate a common vulnerability number as defined on www.cve.mitre.org, minus the CAN- or CVE- prefix.';
3932-
3933-
3934-CREATE FUNCTION valid_debian_version(text) RETURNS boolean
3935+COMMENT ON FUNCTION public.valid_cve(text) IS 'validate a common vulnerability number as defined on www.cve.mitre.org, minus the CAN- or CVE- prefix.';
3936+
3937+
3938+CREATE FUNCTION public.valid_debian_version(text) RETURNS boolean
3939 LANGUAGE plpythonu IMMUTABLE STRICT
3940 AS $_$
3941 import re
3942@@ -3462,10 +3323,10 @@
3943 $_$;
3944
3945
3946-COMMENT ON FUNCTION valid_debian_version(text) IS 'validate a version number as per Debian Policy';
3947-
3948-
3949-CREATE FUNCTION valid_fingerprint(text) RETURNS boolean
3950+COMMENT ON FUNCTION public.valid_debian_version(text) IS 'validate a version number as per Debian Policy';
3951+
3952+
3953+CREATE FUNCTION public.valid_fingerprint(text) RETURNS boolean
3954 LANGUAGE plpythonu IMMUTABLE STRICT
3955 AS $$
3956 import re
3957@@ -3476,10 +3337,27 @@
3958 $$;
3959
3960
3961-COMMENT ON FUNCTION valid_fingerprint(text) IS 'Returns true if passed a valid GPG fingerprint. Valid GPG fingerprints are a 40 character long hexadecimal number in uppercase.';
3962-
3963-
3964-CREATE FUNCTION valid_keyid(text) RETURNS boolean
3965+COMMENT ON FUNCTION public.valid_fingerprint(text) IS 'Returns true if passed a valid GPG fingerprint. Valid GPG fingerprints are a 40 character long hexadecimal number in uppercase.';
3966+
3967+
3968+CREATE FUNCTION public.valid_git_repository_name(text) RETURNS boolean
3969+ LANGUAGE plpythonu IMMUTABLE STRICT
3970+ AS $$
3971+ import re
3972+ name = args[0]
3973+ pat = r"^(?i)[a-z0-9][a-z0-9+\.\-@_]*\Z"
3974+ if not name.endswith(".git") and re.match(pat, name):
3975+ return 1
3976+ return 0
3977+$$;
3978+
3979+
3980+COMMENT ON FUNCTION public.valid_git_repository_name(text) IS 'validate a Git repository name.
3981+
3982+ As per valid_branch_name, except we disallow names ending in ".git".';
3983+
3984+
3985+CREATE FUNCTION public.valid_keyid(text) RETURNS boolean
3986 LANGUAGE plpythonu IMMUTABLE STRICT
3987 AS $$
3988 import re
3989@@ -3490,10 +3368,10 @@
3990 $$;
3991
3992
3993-COMMENT ON FUNCTION valid_keyid(text) IS 'Returns true if passed a valid GPG keyid. Valid GPG keyids are an 8 character long hexadecimal number in uppercase (in reality, they are 16 characters long but we are using the ''common'' definition.';
3994-
3995-
3996-CREATE FUNCTION valid_regexp(text) RETURNS boolean
3997+COMMENT ON FUNCTION public.valid_keyid(text) IS 'Returns true if passed a valid GPG keyid. Valid GPG keyids are an 8 character long hexadecimal number in uppercase (in reality, they are 16 characters long but we are using the ''common'' definition.';
3998+
3999+
4000+CREATE FUNCTION public.valid_regexp(text) RETURNS boolean
4001 LANGUAGE plpythonu IMMUTABLE STRICT
4002 AS $$
4003 import re
4004@@ -3506,10 +3384,10 @@
4005 $$;
4006
4007
4008-COMMENT ON FUNCTION valid_regexp(text) IS 'Returns true if the input can be compiled as a regular expression.';
4009-
4010-
4011-CREATE FUNCTION version_sort_key(version text) RETURNS text
4012+COMMENT ON FUNCTION public.valid_regexp(text) IS 'Returns true if the input can be compiled as a regular expression.';
4013+
4014+
4015+CREATE FUNCTION public.version_sort_key(version text) RETURNS text
4016 LANGUAGE plpythonu IMMUTABLE STRICT
4017 AS $$
4018 # If this method is altered, then any functional indexes using it
4019@@ -3529,10 +3407,10 @@
4020 $$;
4021
4022
4023-COMMENT ON FUNCTION version_sort_key(version text) IS 'Sort a field as version numbers that do not necessarily conform to debian package versions (For example, when "2-2" should be considered greater than "1:1"). debversion_sort_key() should be used for debian versions. Numbers will be sorted after letters unlike typical ASCII, so that a descending sort will put the latest version number that starts with a number instead of a letter will be at the top. E.g. ascending is [a, z, 1, 9] and descending is [9, 1, z, a].';
4024-
4025-
4026-CREATE FUNCTION you_are_your_own_member() RETURNS trigger
4027+COMMENT ON FUNCTION public.version_sort_key(version text) IS 'Sort a field as version numbers that do not necessarily conform to debian package versions (For example, when "2-2" should be considered greater than "1:1"). debversion_sort_key() should be used for debian versions. Numbers will be sorted after letters unlike typical ASCII, so that a descending sort will put the latest version number that starts with a number instead of a letter will be at the top. E.g. ascending is [a, z, 1, 9] and descending is [9, 1, z, a].';
4028+
4029+
4030+CREATE FUNCTION public.you_are_your_own_member() RETURNS trigger
4031 LANGUAGE plpgsql
4032 AS $$
4033 BEGIN
4034@@ -3543,873 +3421,116 @@
4035 $$;
4036
4037
4038-COMMENT ON FUNCTION you_are_your_own_member() IS 'Trigger function to ensure that every row added to the Person table gets a corresponding row in the TeamParticipation table, as per the TeamParticipationUsage page on the Launchpad wiki';
4039-
4040-
4041-SET search_path = ts2, pg_catalog;
4042-
4043-CREATE FUNCTION _ftq(text) RETURNS text
4044- LANGUAGE plpythonu IMMUTABLE STRICT
4045- AS $_$
4046- import re
4047-
4048- # I think this method would be more robust if we used a real
4049- # tokenizer and parser to generate the query string, but we need
4050- # something suitable for use as a stored procedure which currently
4051- # means no external dependancies.
4052-
4053- # Convert to Unicode
4054- query = args[0].decode('utf8')
4055- ## plpy.debug('1 query is %s' % repr(query))
4056-
4057- # Normalize whitespace
4058- query = re.sub("(?u)\s+"," ", query)
4059-
4060- # Convert AND, OR, NOT and - to tsearch2 punctuation
4061- query = re.sub(r"(?u)(?:^|\s)-([\w\(])", r" !\1", query)
4062- query = re.sub(r"(?u)\bAND\b", "&", query)
4063- query = re.sub(r"(?u)\bOR\b", "|", query)
4064- query = re.sub(r"(?u)\bNOT\b", " !", query)
4065- ## plpy.debug('2 query is %s' % repr(query))
4066-
4067- # Deal with unwanted punctuation. We convert strings of punctuation
4068- # inside words to a '-' character for the hypenation handling below
4069- # to deal with further. Outside of words we replace with whitespace.
4070- # We don't mess with -&|!()' as they are handled later.
4071- #punctuation = re.escape(r'`~@#$%^*+=[]{}:;"<>,.?\/')
4072- punctuation = r"[^\w\s\-\&\|\!\(\)']"
4073- query = re.sub(r"(?u)(\w)%s+(\w)" % (punctuation,), r"\1-\2", query)
4074- query = re.sub(r"(?u)%s+" % (punctuation,), " ", query)
4075- ## plpy.debug('3 query is %s' % repr(query))
4076-
4077- # Strip ! characters inside and at the end of a word
4078- query = re.sub(r"(?u)(?<=\w)[\!]+", " ", query)
4079-
4080- # Now that we have handle case sensitive booleans, convert to lowercase
4081- query = query.lower()
4082-
4083- # Convert foo-bar to ((foo&bar)|foobar) and foo-bar-baz to
4084- # ((foo&bar&baz)|foobarbaz)
4085- def hyphen_repl(match):
4086- bits = match.group(0).split("-")
4087- return "((%s)|%s)" % ("&".join(bits), "".join(bits))
4088- query = re.sub(r"(?u)\b\w+-[\w\-]+\b", hyphen_repl, query)
4089- ## plpy.debug('4 query is %s' % repr(query))
4090-
4091- # Any remaining - characters are spurious
4092- query = query.replace('-','')
4093-
4094- # Remove unpartnered bracket on the left and right
4095- query = re.sub(r"(?ux) ^ ( [^(]* ) \)", r"(\1)", query)
4096- query = re.sub(r"(?ux) \( ( [^)]* ) $", r"(\1)", query)
4097-
4098- # Remove spurious brackets
4099- query = re.sub(r"(?u)\(([^\&\|]*?)\)", r" \1 ", query)
4100- ## plpy.debug('5 query is %s' % repr(query))
4101-
4102- # Insert & between tokens without an existing boolean operator
4103- # ( not proceeded by (|&!
4104- query = re.sub(r"(?u)(?<![\(\|\&\!])\s*\(", "&(", query)
4105- ## plpy.debug('6 query is %s' % repr(query))
4106- # ) not followed by )|&
4107- query = re.sub(r"(?u)\)(?!\s*(\)|\||\&|\s*$))", ")&", query)
4108- ## plpy.debug('6.1 query is %s' % repr(query))
4109- # Whitespace not proceded by (|&! not followed by &|
4110- query = re.sub(r"(?u)(?<![\(\|\&\!\s])\s+(?![\&\|\s])", "&", query)
4111- ## plpy.debug('7 query is %s' % repr(query))
4112-
4113- # Detect and repair syntax errors - we are lenient because
4114- # this input is generally from users.
4115-
4116- # Fix unbalanced brackets
4117- openings = query.count("(")
4118- closings = query.count(")")
4119- if openings > closings:
4120- query = query + " ) "*(openings-closings)
4121- elif closings > openings:
4122- query = " ( "*(closings-openings) + query
4123- ## plpy.debug('8 query is %s' % repr(query))
4124-
4125- # Strip ' character that do not have letters on both sides
4126- query = re.sub(r"(?u)((?<!\w)'|'(?!\w))", "", query)
4127-
4128- # Brackets containing nothing but whitespace and booleans, recursive
4129- last = ""
4130- while last != query:
4131- last = query
4132- query = re.sub(r"(?u)\([\s\&\|\!]*\)", "", query)
4133- ## plpy.debug('9 query is %s' % repr(query))
4134-
4135- # An & or | following a (
4136- query = re.sub(r"(?u)(?<=\()[\&\|\s]+", "", query)
4137- ## plpy.debug('10 query is %s' % repr(query))
4138-
4139- # An &, | or ! immediatly before a )
4140- query = re.sub(r"(?u)[\&\|\!\s]*[\&\|\!]+\s*(?=\))", "", query)
4141- ## plpy.debug('11 query is %s' % repr(query))
4142-
4143- # An &,| or ! followed by another boolean.
4144- query = re.sub(r"(?ux) \s* ( [\&\|\!] ) [\s\&\|]+", r"\1", query)
4145- ## plpy.debug('12 query is %s' % repr(query))
4146-
4147- # Leading & or |
4148- query = re.sub(r"(?u)^[\s\&\|]+", "", query)
4149- ## plpy.debug('13 query is %s' % repr(query))
4150-
4151- # Trailing &, | or !
4152- query = re.sub(r"(?u)[\&\|\!\s]+$", "", query)
4153- ## plpy.debug('14 query is %s' % repr(query))
4154-
4155- # If we have nothing but whitespace and tsearch2 operators,
4156- # return NULL.
4157- if re.search(r"(?u)^[\&\|\!\s\(\)]*$", query) is not None:
4158- return None
4159-
4160- # Convert back to UTF-8
4161- query = query.encode('utf8')
4162- ## plpy.debug('15 query is %s' % repr(query))
4163-
4164- return query or None
4165- $_$;
4166-
4167-
4168-CREATE FUNCTION _get_parser_from_curcfg() RETURNS text
4169- LANGUAGE sql IMMUTABLE STRICT
4170- AS $$select prsname::text from pg_catalog.pg_ts_parser p join pg_ts_config c on cfgparser = p.oid where c.oid = show_curcfg();$$;
4171-
4172-
4173-CREATE FUNCTION concat(pg_catalog.tsvector, pg_catalog.tsvector) RETURNS pg_catalog.tsvector
4174- LANGUAGE internal IMMUTABLE STRICT
4175- AS $$tsvector_concat$$;
4176-
4177-
4178-CREATE FUNCTION dex_init(internal) RETURNS internal
4179- LANGUAGE c
4180- AS '$libdir/tsearch2', 'tsa_dex_init';
4181-
4182-
4183-CREATE FUNCTION dex_lexize(internal, internal, integer) RETURNS internal
4184- LANGUAGE c STRICT
4185- AS '$libdir/tsearch2', 'tsa_dex_lexize';
4186-
4187-
4188-CREATE FUNCTION ftiupdate() RETURNS trigger
4189- LANGUAGE plpythonu
4190- AS $_$
4191- new = TD["new"]
4192- args = TD["args"][:]
4193-
4194- # Short circuit if none of the relevant columns have been
4195- # modified and fti is not being set to NULL (setting the fti
4196- # column to NULL is thus how we can force a rebuild of the fti
4197- # column).
4198- if TD["event"] == "UPDATE" and new["fti"] != None:
4199- old = TD["old"]
4200- relevant_modification = False
4201- for column_name in args[::2]:
4202- if new[column_name] != old[column_name]:
4203- relevant_modification = True
4204- break
4205- if not relevant_modification:
4206- return "OK"
4207-
4208- # Generate an SQL statement that turns the requested
4209- # column values into a weighted tsvector
4210- sql = []
4211- for i in range(0, len(args), 2):
4212- sql.append(
4213- "ts2.setweight(ts2.to_tsvector('default', coalesce("
4214- "substring(ltrim($%d) from 1 for 2500),'')),"
4215- "CAST($%d AS \"char\"))" % (i + 1, i + 2))
4216- args[i] = new[args[i]]
4217-
4218- sql = "SELECT %s AS fti" % "||".join(sql)
4219-
4220- # Execute and store in the fti column
4221- plan = plpy.prepare(sql, ["text", "char"] * (len(args)/2))
4222- new["fti"] = plpy.execute(plan, args, 1)[0]["fti"]
4223-
4224- # Tell PostgreSQL we have modified the data
4225- return "MODIFY"
4226-$_$;
4227-
4228-
4229-COMMENT ON FUNCTION ftiupdate() IS 'Trigger function that keeps the fti tsvector column up to date.';
4230-
4231-
4232-CREATE FUNCTION ftq(text) RETURNS pg_catalog.tsquery
4233- LANGUAGE plpythonu IMMUTABLE STRICT
4234- AS $_$
4235- import re
4236-
4237- # I think this method would be more robust if we used a real
4238- # tokenizer and parser to generate the query string, but we need
4239- # something suitable for use as a stored procedure which currently
4240- # means no external dependancies.
4241-
4242- # Convert to Unicode
4243- query = args[0].decode('utf8')
4244- ## plpy.debug('1 query is %s' % repr(query))
4245-
4246- # Normalize whitespace
4247- query = re.sub("(?u)\s+"," ", query)
4248-
4249- # Convert AND, OR, NOT and - to tsearch2 punctuation
4250- query = re.sub(r"(?u)(?:^|\s)-([\w\(])", r" !\1", query)
4251- query = re.sub(r"(?u)\bAND\b", "&", query)
4252- query = re.sub(r"(?u)\bOR\b", "|", query)
4253- query = re.sub(r"(?u)\bNOT\b", " !", query)
4254- ## plpy.debug('2 query is %s' % repr(query))
4255-
4256- # Deal with unwanted punctuation. We convert strings of punctuation
4257- # inside words to a '-' character for the hypenation handling below
4258- # to deal with further. Outside of words we replace with whitespace.
4259- # We don't mess with -&|!()' as they are handled later.
4260- #punctuation = re.escape(r'`~@#$%^*+=[]{}:;"<>,.?\/')
4261- punctuation = r"[^\w\s\-\&\|\!\(\)']"
4262- query = re.sub(r"(?u)(\w)%s+(\w)" % (punctuation,), r"\1-\2", query)
4263- query = re.sub(r"(?u)%s+" % (punctuation,), " ", query)
4264- ## plpy.debug('3 query is %s' % repr(query))
4265-
4266- # Strip ! characters inside and at the end of a word
4267- query = re.sub(r"(?u)(?<=\w)[\!]+", " ", query)
4268-
4269- # Now that we have handle case sensitive booleans, convert to lowercase
4270- query = query.lower()
4271-
4272- # Convert foo-bar to ((foo&bar)|foobar) and foo-bar-baz to
4273- # ((foo&bar&baz)|foobarbaz)
4274- def hyphen_repl(match):
4275- bits = match.group(0).split("-")
4276- return "((%s)|%s)" % ("&".join(bits), "".join(bits))
4277- query = re.sub(r"(?u)\b\w+-[\w\-]+\b", hyphen_repl, query)
4278- ## plpy.debug('4 query is %s' % repr(query))
4279-
4280- # Any remaining - characters are spurious
4281- query = query.replace('-','')
4282-
4283- # Remove unpartnered bracket on the left and right
4284- query = re.sub(r"(?ux) ^ ( [^(]* ) \)", r"(\1)", query)
4285- query = re.sub(r"(?ux) \( ( [^)]* ) $", r"(\1)", query)
4286-
4287- # Remove spurious brackets
4288- query = re.sub(r"(?u)\(([^\&\|]*?)\)", r" \1 ", query)
4289- ## plpy.debug('5 query is %s' % repr(query))
4290-
4291- # Insert & between tokens without an existing boolean operator
4292- # ( not proceeded by (|&!
4293- query = re.sub(r"(?u)(?<![\(\|\&\!])\s*\(", "&(", query)
4294- ## plpy.debug('6 query is %s' % repr(query))
4295- # ) not followed by )|&
4296- query = re.sub(r"(?u)\)(?!\s*(\)|\||\&|\s*$))", ")&", query)
4297- ## plpy.debug('6.1 query is %s' % repr(query))
4298- # Whitespace not proceded by (|&! not followed by &|
4299- query = re.sub(r"(?u)(?<![\(\|\&\!\s])\s+(?![\&\|\s])", "&", query)
4300- ## plpy.debug('7 query is %s' % repr(query))
4301-
4302- # Detect and repair syntax errors - we are lenient because
4303- # this input is generally from users.
4304-
4305- # Fix unbalanced brackets
4306- openings = query.count("(")
4307- closings = query.count(")")
4308- if openings > closings:
4309- query = query + " ) "*(openings-closings)
4310- elif closings > openings:
4311- query = " ( "*(closings-openings) + query
4312- ## plpy.debug('8 query is %s' % repr(query))
4313-
4314- # Strip ' character that do not have letters on both sides
4315- query = re.sub(r"(?u)((?<!\w)'|'(?!\w))", "", query)
4316-
4317- # Brackets containing nothing but whitespace and booleans, recursive
4318- last = ""
4319- while last != query:
4320- last = query
4321- query = re.sub(r"(?u)\([\s\&\|\!]*\)", "", query)
4322- ## plpy.debug('9 query is %s' % repr(query))
4323-
4324- # An & or | following a (
4325- query = re.sub(r"(?u)(?<=\()[\&\|\s]+", "", query)
4326- ## plpy.debug('10 query is %s' % repr(query))
4327-
4328- # An &, | or ! immediatly before a )
4329- query = re.sub(r"(?u)[\&\|\!\s]*[\&\|\!]+\s*(?=\))", "", query)
4330- ## plpy.debug('11 query is %s' % repr(query))
4331-
4332- # An &,| or ! followed by another boolean.
4333- query = re.sub(r"(?ux) \s* ( [\&\|\!] ) [\s\&\|]+", r"\1", query)
4334- ## plpy.debug('12 query is %s' % repr(query))
4335-
4336- # Leading & or |
4337- query = re.sub(r"(?u)^[\s\&\|]+", "", query)
4338- ## plpy.debug('13 query is %s' % repr(query))
4339-
4340- # Trailing &, | or !
4341- query = re.sub(r"(?u)[\&\|\!\s]+$", "", query)
4342- ## plpy.debug('14 query is %s' % repr(query))
4343-
4344- # If we have nothing but whitespace and tsearch2 operators,
4345- # return NULL.
4346- if re.search(r"(?u)^[\&\|\!\s\(\)]*$", query) is not None:
4347- return None
4348-
4349- # Convert back to UTF-8
4350- query = query.encode('utf8')
4351- ## plpy.debug('15 query is %s' % repr(query))
4352-
4353- p = plpy.prepare("SELECT to_tsquery('default', $1) AS x", ["text"])
4354- query = plpy.execute(p, [query], 1)[0]["x"]
4355- return query or None
4356- $_$;
4357-
4358-
4359-COMMENT ON FUNCTION ftq(text) IS 'Convert a string to an unparsed tsearch2 query';
4360-
4361-
4362-CREATE FUNCTION get_covers(pg_catalog.tsvector, pg_catalog.tsquery) RETURNS text
4363- LANGUAGE c STRICT
4364- AS '$libdir/tsearch2', 'tsa_get_covers';
4365-
4366-
4367-CREATE FUNCTION headline(oid, text, pg_catalog.tsquery, text) RETURNS text
4368- LANGUAGE internal IMMUTABLE STRICT
4369- AS $$ts_headline_byid_opt$$;
4370-
4371-
4372-CREATE FUNCTION headline(oid, text, pg_catalog.tsquery) RETURNS text
4373- LANGUAGE internal IMMUTABLE STRICT
4374- AS $$ts_headline_byid$$;
4375-
4376-
4377-CREATE FUNCTION headline(text, text, pg_catalog.tsquery, text) RETURNS text
4378- LANGUAGE c IMMUTABLE STRICT
4379- AS '$libdir/tsearch2', 'tsa_headline_byname';
4380-
4381-
4382-CREATE FUNCTION headline(text, text, pg_catalog.tsquery) RETURNS text
4383- LANGUAGE c IMMUTABLE STRICT
4384- AS '$libdir/tsearch2', 'tsa_headline_byname';
4385-
4386-
4387-CREATE FUNCTION headline(text, pg_catalog.tsquery, text) RETURNS text
4388- LANGUAGE internal IMMUTABLE STRICT
4389- AS $$ts_headline_opt$$;
4390-
4391-
4392-CREATE FUNCTION headline(text, pg_catalog.tsquery) RETURNS text
4393- LANGUAGE internal IMMUTABLE STRICT
4394- AS $$ts_headline$$;
4395-
4396-
4397-CREATE FUNCTION length(pg_catalog.tsvector) RETURNS integer
4398- LANGUAGE internal IMMUTABLE STRICT
4399- AS $$tsvector_length$$;
4400-
4401-
4402-CREATE FUNCTION lexize(oid, text) RETURNS text[]
4403- LANGUAGE internal STRICT
4404- AS $$ts_lexize$$;
4405-
4406-
4407-CREATE FUNCTION lexize(text, text) RETURNS text[]
4408- LANGUAGE c STRICT
4409- AS '$libdir/tsearch2', 'tsa_lexize_byname';
4410-
4411-
4412-CREATE FUNCTION lexize(text) RETURNS text[]
4413- LANGUAGE c STRICT
4414- AS '$libdir/tsearch2', 'tsa_lexize_bycurrent';
4415-
4416-
4417-CREATE FUNCTION numnode(pg_catalog.tsquery) RETURNS integer
4418- LANGUAGE internal IMMUTABLE STRICT
4419- AS $$tsquery_numnode$$;
4420-
4421-
4422-CREATE FUNCTION parse(oid, text) RETURNS SETOF tokenout
4423- LANGUAGE internal STRICT
4424- AS $$ts_parse_byid$$;
4425-
4426-
4427-CREATE FUNCTION parse(text, text) RETURNS SETOF tokenout
4428- LANGUAGE internal STRICT
4429- AS $$ts_parse_byname$$;
4430-
4431-
4432-CREATE FUNCTION parse(text) RETURNS SETOF tokenout
4433- LANGUAGE c STRICT
4434- AS '$libdir/tsearch2', 'tsa_parse_current';
4435-
4436-
4437-CREATE FUNCTION plainto_tsquery(oid, text) RETURNS pg_catalog.tsquery
4438- LANGUAGE internal IMMUTABLE STRICT
4439- AS $$plainto_tsquery_byid$$;
4440-
4441-
4442-CREATE FUNCTION plainto_tsquery(text, text) RETURNS pg_catalog.tsquery
4443- LANGUAGE c IMMUTABLE STRICT
4444- AS '$libdir/tsearch2', 'tsa_plainto_tsquery_name';
4445-
4446-
4447-CREATE FUNCTION plainto_tsquery(text) RETURNS pg_catalog.tsquery
4448- LANGUAGE internal IMMUTABLE STRICT
4449- AS $$plainto_tsquery$$;
4450-
4451-
4452-CREATE FUNCTION prsd_end(internal) RETURNS void
4453- LANGUAGE c
4454- AS '$libdir/tsearch2', 'tsa_prsd_end';
4455-
4456-
4457-CREATE FUNCTION prsd_getlexeme(internal, internal, internal) RETURNS integer
4458- LANGUAGE c
4459- AS '$libdir/tsearch2', 'tsa_prsd_getlexeme';
4460-
4461-
4462-CREATE FUNCTION prsd_headline(internal, internal, internal) RETURNS internal
4463- LANGUAGE c
4464- AS '$libdir/tsearch2', 'tsa_prsd_headline';
4465-
4466-
4467-CREATE FUNCTION prsd_lextype(internal) RETURNS internal
4468- LANGUAGE c
4469- AS '$libdir/tsearch2', 'tsa_prsd_lextype';
4470-
4471-
4472-CREATE FUNCTION prsd_start(internal, integer) RETURNS internal
4473- LANGUAGE c
4474- AS '$libdir/tsearch2', 'tsa_prsd_start';
4475-
4476-
4477-CREATE FUNCTION querytree(pg_catalog.tsquery) RETURNS text
4478- LANGUAGE internal STRICT
4479- AS $$tsquerytree$$;
4480-
4481-
4482-CREATE FUNCTION rank(real[], pg_catalog.tsvector, pg_catalog.tsquery) RETURNS real
4483- LANGUAGE internal IMMUTABLE STRICT
4484- AS $$ts_rank_wtt$$;
4485-
4486-
4487-CREATE FUNCTION rank(real[], pg_catalog.tsvector, pg_catalog.tsquery, integer) RETURNS real
4488- LANGUAGE internal IMMUTABLE STRICT
4489- AS $$ts_rank_wttf$$;
4490-
4491-
4492-CREATE FUNCTION rank(pg_catalog.tsvector, pg_catalog.tsquery) RETURNS real
4493- LANGUAGE internal IMMUTABLE STRICT
4494- AS $$ts_rank_tt$$;
4495-
4496-
4497-CREATE FUNCTION rank(pg_catalog.tsvector, pg_catalog.tsquery, integer) RETURNS real
4498- LANGUAGE internal IMMUTABLE STRICT
4499- AS $$ts_rank_ttf$$;
4500-
4501-
4502-CREATE FUNCTION rank_cd(real[], pg_catalog.tsvector, pg_catalog.tsquery) RETURNS real
4503- LANGUAGE internal IMMUTABLE STRICT
4504- AS $$ts_rankcd_wtt$$;
4505-
4506-
4507-CREATE FUNCTION rank_cd(real[], pg_catalog.tsvector, pg_catalog.tsquery, integer) RETURNS real
4508- LANGUAGE internal IMMUTABLE STRICT
4509- AS $$ts_rankcd_wttf$$;
4510-
4511-
4512-CREATE FUNCTION rank_cd(pg_catalog.tsvector, pg_catalog.tsquery) RETURNS real
4513- LANGUAGE internal IMMUTABLE STRICT
4514- AS $$ts_rankcd_tt$$;
4515-
4516-
4517-CREATE FUNCTION rank_cd(pg_catalog.tsvector, pg_catalog.tsquery, integer) RETURNS real
4518- LANGUAGE internal IMMUTABLE STRICT
4519- AS $$ts_rankcd_ttf$$;
4520-
4521-
4522-CREATE FUNCTION reset_tsearch() RETURNS void
4523- LANGUAGE c STRICT
4524- AS '$libdir/tsearch2', 'tsa_reset_tsearch';
4525-
4526-
4527-CREATE FUNCTION rewrite(pg_catalog.tsquery, text) RETURNS pg_catalog.tsquery
4528- LANGUAGE internal IMMUTABLE STRICT
4529- AS $$tsquery_rewrite_query$$;
4530-
4531-
4532-CREATE FUNCTION rewrite(pg_catalog.tsquery, pg_catalog.tsquery, pg_catalog.tsquery) RETURNS pg_catalog.tsquery
4533- LANGUAGE internal IMMUTABLE STRICT
4534- AS $$tsquery_rewrite$$;
4535-
4536-
4537-CREATE FUNCTION rewrite_accum(pg_catalog.tsquery, pg_catalog.tsquery[]) RETURNS pg_catalog.tsquery
4538- LANGUAGE c
4539- AS '$libdir/tsearch2', 'tsa_rewrite_accum';
4540-
4541-
4542-CREATE FUNCTION rewrite_finish(pg_catalog.tsquery) RETURNS pg_catalog.tsquery
4543- LANGUAGE c
4544- AS '$libdir/tsearch2', 'tsa_rewrite_finish';
4545-
4546-
4547-CREATE FUNCTION set_curcfg(integer) RETURNS void
4548- LANGUAGE c STRICT
4549- AS '$libdir/tsearch2', 'tsa_set_curcfg';
4550-
4551-
4552-CREATE FUNCTION set_curcfg(text) RETURNS void
4553- LANGUAGE c STRICT
4554- AS '$libdir/tsearch2', 'tsa_set_curcfg_byname';
4555-
4556-
4557-CREATE FUNCTION set_curdict(integer) RETURNS void
4558- LANGUAGE c STRICT
4559- AS '$libdir/tsearch2', 'tsa_set_curdict';
4560-
4561-
4562-CREATE FUNCTION set_curdict(text) RETURNS void
4563- LANGUAGE c STRICT
4564- AS '$libdir/tsearch2', 'tsa_set_curdict_byname';
4565-
4566-
4567-CREATE FUNCTION set_curprs(integer) RETURNS void
4568- LANGUAGE c STRICT
4569- AS '$libdir/tsearch2', 'tsa_set_curprs';
4570-
4571-
4572-CREATE FUNCTION set_curprs(text) RETURNS void
4573- LANGUAGE c STRICT
4574- AS '$libdir/tsearch2', 'tsa_set_curprs_byname';
4575-
4576-
4577-CREATE FUNCTION setweight(pg_catalog.tsvector, "char") RETURNS pg_catalog.tsvector
4578- LANGUAGE internal IMMUTABLE STRICT
4579- AS $$tsvector_setweight$$;
4580-
4581-
4582-CREATE FUNCTION show_curcfg() RETURNS oid
4583- LANGUAGE internal STABLE STRICT
4584- AS $$get_current_ts_config$$;
4585-
4586-
4587-CREATE FUNCTION snb_en_init(internal) RETURNS internal
4588- LANGUAGE c
4589- AS '$libdir/tsearch2', 'tsa_snb_en_init';
4590-
4591-
4592-CREATE FUNCTION snb_lexize(internal, internal, integer) RETURNS internal
4593- LANGUAGE c STRICT
4594- AS '$libdir/tsearch2', 'tsa_snb_lexize';
4595-
4596-
4597-CREATE FUNCTION snb_ru_init(internal) RETURNS internal
4598- LANGUAGE c
4599- AS '$libdir/tsearch2', 'tsa_snb_ru_init';
4600-
4601-
4602-CREATE FUNCTION snb_ru_init_koi8(internal) RETURNS internal
4603- LANGUAGE c
4604- AS '$libdir/tsearch2', 'tsa_snb_ru_init_koi8';
4605-
4606-
4607-CREATE FUNCTION snb_ru_init_utf8(internal) RETURNS internal
4608- LANGUAGE c
4609- AS '$libdir/tsearch2', 'tsa_snb_ru_init_utf8';
4610-
4611-
4612-CREATE FUNCTION spell_init(internal) RETURNS internal
4613- LANGUAGE c
4614- AS '$libdir/tsearch2', 'tsa_spell_init';
4615-
4616-
4617-CREATE FUNCTION spell_lexize(internal, internal, integer) RETURNS internal
4618- LANGUAGE c STRICT
4619- AS '$libdir/tsearch2', 'tsa_spell_lexize';
4620-
4621-
4622-CREATE FUNCTION stat(text) RETURNS SETOF statinfo
4623- LANGUAGE internal STRICT
4624- AS $$ts_stat1$$;
4625-
4626-
4627-CREATE FUNCTION stat(text, text) RETURNS SETOF statinfo
4628- LANGUAGE internal STRICT
4629- AS $$ts_stat2$$;
4630-
4631-
4632-CREATE FUNCTION strip(pg_catalog.tsvector) RETURNS pg_catalog.tsvector
4633- LANGUAGE internal IMMUTABLE STRICT
4634- AS $$tsvector_strip$$;
4635-
4636-
4637-CREATE FUNCTION syn_init(internal) RETURNS internal
4638- LANGUAGE c
4639- AS '$libdir/tsearch2', 'tsa_syn_init';
4640-
4641-
4642-CREATE FUNCTION syn_lexize(internal, internal, integer) RETURNS internal
4643- LANGUAGE c STRICT
4644- AS '$libdir/tsearch2', 'tsa_syn_lexize';
4645-
4646-
4647-CREATE FUNCTION thesaurus_init(internal) RETURNS internal
4648- LANGUAGE c
4649- AS '$libdir/tsearch2', 'tsa_thesaurus_init';
4650-
4651-
4652-CREATE FUNCTION thesaurus_lexize(internal, internal, integer, internal) RETURNS internal
4653- LANGUAGE c STRICT
4654- AS '$libdir/tsearch2', 'tsa_thesaurus_lexize';
4655-
4656-
4657-CREATE FUNCTION to_tsquery(oid, text) RETURNS pg_catalog.tsquery
4658- LANGUAGE internal IMMUTABLE STRICT
4659- AS $$to_tsquery_byid$$;
4660-
4661-
4662-CREATE FUNCTION to_tsquery(text, text) RETURNS pg_catalog.tsquery
4663- LANGUAGE c IMMUTABLE STRICT
4664- AS '$libdir/tsearch2', 'tsa_to_tsquery_name';
4665-
4666-
4667-CREATE FUNCTION to_tsquery(text) RETURNS pg_catalog.tsquery
4668- LANGUAGE internal IMMUTABLE STRICT
4669- AS $$to_tsquery$$;
4670-
4671-
4672-CREATE FUNCTION to_tsvector(oid, text) RETURNS pg_catalog.tsvector
4673- LANGUAGE internal IMMUTABLE STRICT
4674- AS $$to_tsvector_byid$$;
4675-
4676-
4677-CREATE FUNCTION to_tsvector(text, text) RETURNS pg_catalog.tsvector
4678- LANGUAGE c IMMUTABLE STRICT
4679- AS '$libdir/tsearch2', 'tsa_to_tsvector_name';
4680-
4681-
4682-CREATE FUNCTION to_tsvector(text) RETURNS pg_catalog.tsvector
4683- LANGUAGE internal IMMUTABLE STRICT
4684- AS $$to_tsvector$$;
4685-
4686-
4687-CREATE FUNCTION token_type(integer) RETURNS SETOF tokentype
4688- LANGUAGE internal STRICT ROWS 16
4689- AS $$ts_token_type_byid$$;
4690-
4691-
4692-CREATE FUNCTION token_type(text) RETURNS SETOF tokentype
4693- LANGUAGE internal STRICT ROWS 16
4694- AS $$ts_token_type_byname$$;
4695-
4696-
4697-CREATE FUNCTION token_type() RETURNS SETOF tokentype
4698- LANGUAGE c STRICT ROWS 16
4699- AS '$libdir/tsearch2', 'tsa_token_type_current';
4700-
4701-
4702-CREATE FUNCTION ts_debug(text) RETURNS SETOF tsdebug
4703- LANGUAGE sql STRICT
4704- AS $_$
4705-select
4706- (select c.cfgname::text from pg_catalog.pg_ts_config as c
4707- where c.oid = show_curcfg()),
4708- t.alias as tok_type,
4709- t.descr as description,
4710- p.token,
4711- ARRAY ( SELECT m.mapdict::pg_catalog.regdictionary::pg_catalog.text
4712- FROM pg_catalog.pg_ts_config_map AS m
4713- WHERE m.mapcfg = show_curcfg() AND m.maptokentype = p.tokid
4714- ORDER BY m.mapseqno )
4715- AS dict_name,
4716- strip(to_tsvector(p.token)) as tsvector
4717-from
4718- parse( _get_parser_from_curcfg(), $1 ) as p,
4719- token_type() as t
4720-where
4721- t.tokid = p.tokid
4722-$_$;
4723-
4724-
4725-CREATE FUNCTION tsearch2() RETURNS trigger
4726- LANGUAGE c
4727- AS '$libdir/tsearch2', 'tsa_tsearch2';
4728-
4729-
4730-CREATE FUNCTION tsq_mcontained(pg_catalog.tsquery, pg_catalog.tsquery) RETURNS boolean
4731- LANGUAGE internal IMMUTABLE STRICT
4732- AS $$tsq_mcontained$$;
4733-
4734-
4735-CREATE FUNCTION tsq_mcontains(pg_catalog.tsquery, pg_catalog.tsquery) RETURNS boolean
4736- LANGUAGE internal IMMUTABLE STRICT
4737- AS $$tsq_mcontains$$;
4738-
4739-
4740-CREATE FUNCTION tsquery_and(pg_catalog.tsquery, pg_catalog.tsquery) RETURNS pg_catalog.tsquery
4741- LANGUAGE internal IMMUTABLE STRICT
4742- AS $$tsquery_and$$;
4743-
4744-
4745-CREATE FUNCTION tsquery_not(pg_catalog.tsquery) RETURNS pg_catalog.tsquery
4746- LANGUAGE internal IMMUTABLE STRICT
4747- AS $$tsquery_not$$;
4748-
4749-
4750-CREATE FUNCTION tsquery_or(pg_catalog.tsquery, pg_catalog.tsquery) RETURNS pg_catalog.tsquery
4751- LANGUAGE internal IMMUTABLE STRICT
4752- AS $$tsquery_or$$;
4753-
4754-
4755-SET search_path = public, pg_catalog;
4756-
4757-CREATE OPERATOR > (
4758- PROCEDURE = debversion_gt,
4759- LEFTARG = debversion,
4760- RIGHTARG = debversion,
4761- COMMUTATOR = <,
4762- NEGATOR = >=
4763-);
4764-
4765-
4766-COMMENT ON OPERATOR > (debversion, debversion) IS 'debversion greater-than';
4767-
4768-
4769-CREATE AGGREGATE max(debversion) (
4770- SFUNC = debversion_larger,
4771- STYPE = debversion,
4772- SORTOP = >
4773-);
4774-
4775-
4776-CREATE OPERATOR < (
4777- PROCEDURE = debversion_lt,
4778- LEFTARG = debversion,
4779- RIGHTARG = debversion,
4780- COMMUTATOR = >,
4781- NEGATOR = >=
4782-);
4783-
4784-
4785-COMMENT ON OPERATOR < (debversion, debversion) IS 'debversion less-than';
4786-
4787-
4788-CREATE AGGREGATE min(debversion) (
4789- SFUNC = debversion_smaller,
4790- STYPE = debversion,
4791- SORTOP = <
4792-);
4793-
4794-
4795-SET search_path = ts2, pg_catalog;
4796-
4797-CREATE AGGREGATE rewrite(pg_catalog.tsquery[]) (
4798- SFUNC = rewrite_accum,
4799- STYPE = pg_catalog.tsquery,
4800- FINALFUNC = rewrite_finish
4801-);
4802-
4803-
4804-SET search_path = public, pg_catalog;
4805-
4806-CREATE OPERATOR <= (
4807- PROCEDURE = debversion_le,
4808- LEFTARG = debversion,
4809- RIGHTARG = debversion,
4810- COMMUTATOR = >=,
4811- NEGATOR = >
4812-);
4813-
4814-
4815-COMMENT ON OPERATOR <= (debversion, debversion) IS 'debversion less-than-or-equal';
4816-
4817-
4818-CREATE OPERATOR <> (
4819- PROCEDURE = debversion_ne,
4820- LEFTARG = debversion,
4821- RIGHTARG = debversion,
4822- COMMUTATOR = <>,
4823- NEGATOR = =
4824-);
4825-
4826-
4827-COMMENT ON OPERATOR <> (debversion, debversion) IS 'debversion not equal';
4828-
4829-
4830-CREATE OPERATOR = (
4831- PROCEDURE = debversion_eq,
4832- LEFTARG = debversion,
4833- RIGHTARG = debversion,
4834- COMMUTATOR = =,
4835- NEGATOR = <>
4836-);
4837-
4838-
4839-COMMENT ON OPERATOR = (debversion, debversion) IS 'debversion equal';
4840-
4841-
4842-CREATE OPERATOR >= (
4843- PROCEDURE = debversion_ge,
4844- LEFTARG = debversion,
4845- RIGHTARG = debversion,
4846- COMMUTATOR = <=,
4847- NEGATOR = <
4848-);
4849-
4850-
4851-COMMENT ON OPERATOR >= (debversion, debversion) IS 'debversion greater-than-or-equal';
4852-
4853-
4854-CREATE OPERATOR FAMILY debversion_ops USING btree;
4855-
4856-
4857-CREATE OPERATOR CLASS debversion_ops
4858- DEFAULT FOR TYPE debversion USING btree AS
4859- OPERATOR 1 <(debversion,debversion) ,
4860- OPERATOR 2 <=(debversion,debversion) ,
4861- OPERATOR 3 =(debversion,debversion) ,
4862- OPERATOR 4 >=(debversion,debversion) ,
4863- OPERATOR 5 >(debversion,debversion) ,
4864- FUNCTION 1 debversion_cmp(debversion,debversion);
4865-
4866-
4867-CREATE OPERATOR FAMILY debversion_ops USING hash;
4868-
4869-
4870-CREATE OPERATOR CLASS debversion_ops
4871- DEFAULT FOR TYPE debversion USING hash AS
4872- OPERATOR 1 =(debversion,debversion) ,
4873- FUNCTION 1 debversion_hash(debversion);
4874-
4875-
4876-SET search_path = ts2, pg_catalog;
4877-
4878-CREATE OPERATOR FAMILY tsquery_ops USING btree;
4879-
4880-
4881-CREATE OPERATOR CLASS tsquery_ops
4882- FOR TYPE pg_catalog.tsquery USING btree AS
4883- OPERATOR 1 <(pg_catalog.tsquery,pg_catalog.tsquery) ,
4884- OPERATOR 2 <=(pg_catalog.tsquery,pg_catalog.tsquery) ,
4885- OPERATOR 3 =(pg_catalog.tsquery,pg_catalog.tsquery) ,
4886- OPERATOR 4 >=(pg_catalog.tsquery,pg_catalog.tsquery) ,
4887- OPERATOR 5 >(pg_catalog.tsquery,pg_catalog.tsquery) ,
4888- FUNCTION 1 tsquery_cmp(pg_catalog.tsquery,pg_catalog.tsquery);
4889-
4890-
4891-CREATE OPERATOR FAMILY tsvector_ops USING btree;
4892-
4893-
4894-CREATE OPERATOR CLASS tsvector_ops
4895- FOR TYPE pg_catalog.tsvector USING btree AS
4896- OPERATOR 1 <(pg_catalog.tsvector,pg_catalog.tsvector) ,
4897- OPERATOR 2 <=(pg_catalog.tsvector,pg_catalog.tsvector) ,
4898- OPERATOR 3 =(pg_catalog.tsvector,pg_catalog.tsvector) ,
4899- OPERATOR 4 >=(pg_catalog.tsvector,pg_catalog.tsvector) ,
4900- OPERATOR 5 >(pg_catalog.tsvector,pg_catalog.tsvector) ,
4901- FUNCTION 1 tsvector_cmp(pg_catalog.tsvector,pg_catalog.tsvector);
4902-
4903-
4904-SET search_path = pg_catalog;
4905+COMMENT ON FUNCTION public.you_are_your_own_member() IS 'Trigger function to ensure that every row added to the Person table gets a corresponding row in the TeamParticipation table, as per the TeamParticipationUsage page on the Launchpad wiki';
4906+
4907+
4908+CREATE OPERATOR public.> (
4909+ PROCEDURE = public.debversion_gt,
4910+ LEFTARG = public.debversion,
4911+ RIGHTARG = public.debversion,
4912+ COMMUTATOR = OPERATOR(public.<),
4913+ NEGATOR = OPERATOR(public.>=)
4914+);
4915+
4916+
4917+COMMENT ON OPERATOR public.> (public.debversion, public.debversion) IS 'debversion greater-than';
4918+
4919+
4920+CREATE AGGREGATE public.max(public.debversion) (
4921+ SFUNC = public.debversion_larger,
4922+ STYPE = public.debversion,
4923+ SORTOP = OPERATOR(public.>)
4924+);
4925+
4926+
4927+CREATE OPERATOR public.< (
4928+ PROCEDURE = public.debversion_lt,
4929+ LEFTARG = public.debversion,
4930+ RIGHTARG = public.debversion,
4931+ COMMUTATOR = OPERATOR(public.>),
4932+ NEGATOR = OPERATOR(public.>=)
4933+);
4934+
4935+
4936+COMMENT ON OPERATOR public.< (public.debversion, public.debversion) IS 'debversion less-than';
4937+
4938+
4939+CREATE AGGREGATE public.min(public.debversion) (
4940+ SFUNC = public.debversion_smaller,
4941+ STYPE = public.debversion,
4942+ SORTOP = OPERATOR(public.<)
4943+);
4944+
4945+
4946+CREATE OPERATOR public.<= (
4947+ PROCEDURE = public.debversion_le,
4948+ LEFTARG = public.debversion,
4949+ RIGHTARG = public.debversion,
4950+ COMMUTATOR = OPERATOR(public.>=),
4951+ NEGATOR = OPERATOR(public.>)
4952+);
4953+
4954+
4955+COMMENT ON OPERATOR public.<= (public.debversion, public.debversion) IS 'debversion less-than-or-equal';
4956+
4957+
4958+CREATE OPERATOR public.<> (
4959+ PROCEDURE = public.debversion_ne,
4960+ LEFTARG = public.debversion,
4961+ RIGHTARG = public.debversion,
4962+ COMMUTATOR = OPERATOR(public.<>),
4963+ NEGATOR = OPERATOR(public.=)
4964+);
4965+
4966+
4967+COMMENT ON OPERATOR public.<> (public.debversion, public.debversion) IS 'debversion not equal';
4968+
4969+
4970+CREATE OPERATOR public.= (
4971+ PROCEDURE = public.debversion_eq,
4972+ LEFTARG = public.debversion,
4973+ RIGHTARG = public.debversion,
4974+ COMMUTATOR = OPERATOR(public.=),
4975+ NEGATOR = OPERATOR(public.<>)
4976+);
4977+
4978+
4979+COMMENT ON OPERATOR public.= (public.debversion, public.debversion) IS 'debversion equal';
4980+
4981+
4982+CREATE OPERATOR public.>= (
4983+ PROCEDURE = public.debversion_ge,
4984+ LEFTARG = public.debversion,
4985+ RIGHTARG = public.debversion,
4986+ COMMUTATOR = OPERATOR(public.<=),
4987+ NEGATOR = OPERATOR(public.<)
4988+);
4989+
4990+
4991+COMMENT ON OPERATOR public.>= (public.debversion, public.debversion) IS 'debversion greater-than-or-equal';
4992+
4993+
4994+CREATE OPERATOR FAMILY public.debversion_ops USING btree;
4995+
4996+
4997+CREATE OPERATOR CLASS public.debversion_ops
4998+ DEFAULT FOR TYPE public.debversion USING btree FAMILY public.debversion_ops AS
4999+ OPERATOR 1 public.<(public.debversion,public.debversion) ,
5000+ OPERATOR 2 public.<=(public.debversion,public.debversion) ,
The diff has been truncated for viewing.