rev |
line source |
jbe@0
|
1
|
jbe@92
|
2 -- Execute the following command manually for PostgreSQL prior version 9.0:
|
jbe@92
|
3 -- CREATE LANGUAGE plpgsql;
|
jbe@0
|
4
|
jbe@0
|
5 -- NOTE: In PostgreSQL every UNIQUE constraint implies creation of an index
|
jbe@0
|
6
|
jbe@0
|
7 BEGIN;
|
jbe@0
|
8
|
jbe@5
|
9 CREATE VIEW "liquid_feedback_version" AS
|
jbe@259
|
10 SELECT * FROM (VALUES ('2.1.0', 2, 1, 0))
|
jbe@5
|
11 AS "subquery"("string", "major", "minor", "revision");
|
jbe@5
|
12
|
jbe@0
|
13
|
jbe@0
|
14
|
jbe@7
|
15 ----------------------
|
jbe@7
|
16 -- Full text search --
|
jbe@7
|
17 ----------------------
|
jbe@7
|
18
|
jbe@7
|
19
|
jbe@7
|
20 CREATE FUNCTION "text_search_query"("query_text_p" TEXT)
|
jbe@7
|
21 RETURNS TSQUERY
|
jbe@7
|
22 LANGUAGE 'plpgsql' IMMUTABLE AS $$
|
jbe@7
|
23 BEGIN
|
jbe@7
|
24 RETURN plainto_tsquery('pg_catalog.simple', "query_text_p");
|
jbe@7
|
25 END;
|
jbe@7
|
26 $$;
|
jbe@7
|
27
|
jbe@7
|
28 COMMENT ON FUNCTION "text_search_query"(TEXT) IS 'Usage: WHERE "text_search_data" @@ "text_search_query"(''<user query>'')';
|
jbe@7
|
29
|
jbe@7
|
30
|
jbe@7
|
31 CREATE FUNCTION "highlight"
|
jbe@7
|
32 ( "body_p" TEXT,
|
jbe@7
|
33 "query_text_p" TEXT )
|
jbe@7
|
34 RETURNS TEXT
|
jbe@7
|
35 LANGUAGE 'plpgsql' IMMUTABLE AS $$
|
jbe@7
|
36 BEGIN
|
jbe@7
|
37 RETURN ts_headline(
|
jbe@7
|
38 'pg_catalog.simple',
|
jbe@8
|
39 replace(replace("body_p", e'\\', e'\\\\'), '*', e'\\*'),
|
jbe@7
|
40 "text_search_query"("query_text_p"),
|
jbe@7
|
41 'StartSel=* StopSel=* HighlightAll=TRUE' );
|
jbe@7
|
42 END;
|
jbe@7
|
43 $$;
|
jbe@7
|
44
|
jbe@7
|
45 COMMENT ON FUNCTION "highlight"
|
jbe@7
|
46 ( "body_p" TEXT,
|
jbe@7
|
47 "query_text_p" TEXT )
|
jbe@7
|
48 IS 'For a given a user query this function encapsulates all matches with asterisks. Asterisks and backslashes being already present are preceeded with one extra backslash.';
|
jbe@7
|
49
|
jbe@7
|
50
|
jbe@7
|
51
|
jbe@0
|
52 -------------------------
|
jbe@0
|
53 -- Tables and indicies --
|
jbe@0
|
54 -------------------------
|
jbe@0
|
55
|
jbe@8
|
56
|
jbe@104
|
57 CREATE TABLE "system_setting" (
|
jbe@104
|
58 "member_ttl" INTERVAL );
|
jbe@104
|
59 CREATE UNIQUE INDEX "system_setting_singleton_idx" ON "system_setting" ((1));
|
jbe@104
|
60
|
jbe@104
|
61 COMMENT ON TABLE "system_setting" IS 'This table contains only one row with different settings in each column.';
|
jbe@104
|
62 COMMENT ON INDEX "system_setting_singleton_idx" IS 'This index ensures that "system_setting" only contains one row maximum.';
|
jbe@104
|
63
|
jbe@184
|
64 COMMENT ON COLUMN "system_setting"."member_ttl" IS 'Time after members get their "active" flag set to FALSE, if they do not show any activity.';
|
jbe@104
|
65
|
jbe@104
|
66
|
jbe@111
|
67 CREATE TABLE "contingent" (
|
jbe@111
|
68 "time_frame" INTERVAL PRIMARY KEY,
|
jbe@111
|
69 "text_entry_limit" INT4,
|
jbe@111
|
70 "initiative_limit" INT4 );
|
jbe@111
|
71
|
jbe@111
|
72 COMMENT ON TABLE "contingent" IS 'Amount of text entries or initiatives a user may create within a given time frame. Only one row needs to be fulfilled for a member to be allowed to post. This table must not be empty.';
|
jbe@111
|
73
|
jbe@111
|
74 COMMENT ON COLUMN "contingent"."text_entry_limit" IS 'Number of new drafts or suggestions to be submitted by each member within the given time frame';
|
jbe@111
|
75 COMMENT ON COLUMN "contingent"."initiative_limit" IS 'Number of new initiatives to be opened by each member within a given time frame';
|
jbe@111
|
76
|
jbe@111
|
77
|
jbe@113
|
78 CREATE TYPE "notify_level" AS ENUM
|
jbe@113
|
79 ('none', 'voting', 'verification', 'discussion', 'all');
|
jbe@113
|
80
|
jbe@113
|
81 COMMENT ON TYPE "notify_level" IS 'Level of notification: ''none'' = no notifications, ''voting'' = notifications about finished issues and issues in voting, ''verification'' = notifications about finished issues, issues in voting and verification phase, ''discussion'' = notifications about everything except issues in admission phase, ''all'' = notifications about everything';
|
jbe@113
|
82
|
jbe@113
|
83
|
jbe@0
|
84 CREATE TABLE "member" (
|
jbe@0
|
85 "id" SERIAL4 PRIMARY KEY,
|
jbe@13
|
86 "created" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
jbe@181
|
87 "invite_code" TEXT UNIQUE,
|
jbe@232
|
88 "invite_code_expiry" TIMESTAMPTZ,
|
jbe@182
|
89 "admin_comment" TEXT,
|
jbe@181
|
90 "activated" TIMESTAMPTZ,
|
jbe@184
|
91 "last_activity" DATE,
|
jbe@42
|
92 "last_login" TIMESTAMPTZ,
|
jbe@45
|
93 "login" TEXT UNIQUE,
|
jbe@0
|
94 "password" TEXT,
|
jbe@99
|
95 "locked" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@181
|
96 "active" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@0
|
97 "admin" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@221
|
98 "lang" TEXT,
|
jbe@7
|
99 "notify_email" TEXT,
|
jbe@11
|
100 "notify_email_unconfirmed" TEXT,
|
jbe@11
|
101 "notify_email_secret" TEXT UNIQUE,
|
jbe@11
|
102 "notify_email_secret_expiry" TIMESTAMPTZ,
|
jbe@55
|
103 "notify_email_lock_expiry" TIMESTAMPTZ,
|
jbe@225
|
104 "notify_level" "notify_level",
|
jbe@11
|
105 "password_reset_secret" TEXT UNIQUE,
|
jbe@11
|
106 "password_reset_secret_expiry" TIMESTAMPTZ,
|
jbe@225
|
107 "name" TEXT UNIQUE,
|
jbe@7
|
108 "identification" TEXT UNIQUE,
|
jbe@214
|
109 "authentication" TEXT,
|
jbe@7
|
110 "organizational_unit" TEXT,
|
jbe@7
|
111 "internal_posts" TEXT,
|
jbe@7
|
112 "realname" TEXT,
|
jbe@7
|
113 "birthday" DATE,
|
jbe@7
|
114 "address" TEXT,
|
jbe@7
|
115 "email" TEXT,
|
jbe@7
|
116 "xmpp_address" TEXT,
|
jbe@7
|
117 "website" TEXT,
|
jbe@7
|
118 "phone" TEXT,
|
jbe@7
|
119 "mobile_phone" TEXT,
|
jbe@7
|
120 "profession" TEXT,
|
jbe@7
|
121 "external_memberships" TEXT,
|
jbe@7
|
122 "external_posts" TEXT,
|
jbe@159
|
123 "formatting_engine" TEXT,
|
jbe@7
|
124 "statement" TEXT,
|
jbe@181
|
125 "text_search_data" TSVECTOR,
|
jbe@184
|
126 CONSTRAINT "active_requires_activated_and_last_activity"
|
jbe@225
|
127 CHECK ("active" = FALSE OR ("activated" NOTNULL AND "last_activity" NOTNULL)),
|
jbe@225
|
128 CONSTRAINT "name_not_null_if_activated"
|
jbe@225
|
129 CHECK ("activated" ISNULL OR "name" NOTNULL) );
|
jbe@0
|
130 CREATE INDEX "member_active_idx" ON "member" ("active");
|
jbe@8
|
131 CREATE INDEX "member_text_search_data_idx" ON "member" USING gin ("text_search_data");
|
jbe@7
|
132 CREATE TRIGGER "update_text_search_data"
|
jbe@7
|
133 BEFORE INSERT OR UPDATE ON "member"
|
jbe@7
|
134 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@7
|
135 tsvector_update_trigger('text_search_data', 'pg_catalog.simple',
|
jbe@7
|
136 "name", "identification", "organizational_unit", "internal_posts",
|
jbe@7
|
137 "realname", "external_memberships", "external_posts", "statement" );
|
jbe@0
|
138
|
jbe@0
|
139 COMMENT ON TABLE "member" IS 'Users of the system, e.g. members of an organization';
|
jbe@0
|
140
|
jbe@181
|
141 COMMENT ON COLUMN "member"."created" IS 'Creation of member record and/or invite code';
|
jbe@181
|
142 COMMENT ON COLUMN "member"."invite_code" IS 'Optional invite code, to allow a member to initialize his/her account the first time';
|
jbe@232
|
143 COMMENT ON COLUMN "member"."invite_code_expiry" IS 'Expiry data/time for "invite_code"';
|
jbe@182
|
144 COMMENT ON COLUMN "member"."admin_comment" IS 'Hidden comment for administrative purposes';
|
jbe@207
|
145 COMMENT ON COLUMN "member"."activated" IS 'Timestamp of first activation of account (i.e. usage of "invite_code"); required to be set for "active" members';
|
jbe@184
|
146 COMMENT ON COLUMN "member"."last_activity" IS 'Date of last activity of member; required to be set for "active" members';
|
jbe@103
|
147 COMMENT ON COLUMN "member"."last_login" IS 'Timestamp of last login';
|
jbe@10
|
148 COMMENT ON COLUMN "member"."login" IS 'Login name';
|
jbe@10
|
149 COMMENT ON COLUMN "member"."password" IS 'Password (preferably as crypto-hash, depending on the frontend or access layer)';
|
jbe@99
|
150 COMMENT ON COLUMN "member"."locked" IS 'Locked members can not log in.';
|
jbe@184
|
151 COMMENT ON COLUMN "member"."active" IS 'Memberships, support and votes are taken into account when corresponding members are marked as active. Automatically set to FALSE, if "last_activity" is older than "system_setting"."member_ttl".';
|
jbe@10
|
152 COMMENT ON COLUMN "member"."admin" IS 'TRUE for admins, which can administrate other users and setup policies and areas';
|
jbe@221
|
153 COMMENT ON COLUMN "member"."lang" IS 'Language code of the preferred language of the member';
|
jbe@10
|
154 COMMENT ON COLUMN "member"."notify_email" IS 'Email address where notifications of the system are sent to';
|
jbe@10
|
155 COMMENT ON COLUMN "member"."notify_email_unconfirmed" IS 'Unconfirmed email address provided by the member to be copied into "notify_email" field after verification';
|
jbe@10
|
156 COMMENT ON COLUMN "member"."notify_email_secret" IS 'Secret sent to the address in "notify_email_unconformed"';
|
jbe@10
|
157 COMMENT ON COLUMN "member"."notify_email_secret_expiry" IS 'Expiry date/time for "notify_email_secret"';
|
jbe@55
|
158 COMMENT ON COLUMN "member"."notify_email_lock_expiry" IS 'Date/time until no further email confirmation mails may be sent (abuse protection)';
|
jbe@225
|
159 COMMENT ON COLUMN "member"."notify_level" IS 'Selects which event notifications are to be sent to the "notify_email" mail address, may be NULL if member did not make any selection yet';
|
jbe@225
|
160 COMMENT ON COLUMN "member"."name" IS 'Distinct name of the member, may be NULL if account has not been activated yet';
|
jbe@10
|
161 COMMENT ON COLUMN "member"."identification" IS 'Optional identification number or code of the member';
|
jbe@214
|
162 COMMENT ON COLUMN "member"."authentication" IS 'Information about how this member was authenticated';
|
jbe@10
|
163 COMMENT ON COLUMN "member"."organizational_unit" IS 'Branch or division of the organization the member belongs to';
|
jbe@10
|
164 COMMENT ON COLUMN "member"."internal_posts" IS 'Posts (offices) of the member inside the organization';
|
jbe@10
|
165 COMMENT ON COLUMN "member"."realname" IS 'Real name of the member, may be identical with "name"';
|
jbe@10
|
166 COMMENT ON COLUMN "member"."email" IS 'Published email address of the member; not used for system notifications';
|
jbe@10
|
167 COMMENT ON COLUMN "member"."external_memberships" IS 'Other organizations the member is involved in';
|
jbe@10
|
168 COMMENT ON COLUMN "member"."external_posts" IS 'Posts (offices) outside the organization';
|
jbe@159
|
169 COMMENT ON COLUMN "member"."formatting_engine" IS 'Allows different formatting engines (i.e. wiki formats) to be used for "member"."statement"';
|
jbe@207
|
170 COMMENT ON COLUMN "member"."statement" IS 'Freely chosen text of the member for his/her profile';
|
jbe@7
|
171
|
jbe@7
|
172
|
jbe@279
|
173 -- DEPRECATED API TABLES --
|
jbe@279
|
174
|
jbe@278
|
175 CREATE TYPE "application_access_level" AS ENUM
|
jbe@278
|
176 ('member', 'full', 'pseudonymous', 'anonymous');
|
jbe@278
|
177
|
jbe@278
|
178 COMMENT ON TYPE "application_access_level" IS 'DEPRECATED, WILL BE REMOVED! Access privileges for applications using the API';
|
jbe@278
|
179
|
jbe@278
|
180
|
jbe@278
|
181 CREATE TABLE "member_application" (
|
jbe@278
|
182 "id" SERIAL8 PRIMARY KEY,
|
jbe@278
|
183 UNIQUE ("member_id", "name"),
|
jbe@278
|
184 "member_id" INT4 NOT NULL REFERENCES "member" ("id")
|
jbe@278
|
185 ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@278
|
186 "name" TEXT NOT NULL,
|
jbe@278
|
187 "comment" TEXT,
|
jbe@278
|
188 "access_level" "application_access_level" NOT NULL,
|
jbe@278
|
189 "key" TEXT NOT NULL UNIQUE,
|
jbe@278
|
190 "last_usage" TIMESTAMPTZ );
|
jbe@278
|
191
|
jbe@278
|
192 COMMENT ON TABLE "member_application" IS 'DEPRECATED, WILL BE REMOVED! Registered application being allowed to use the API';
|
jbe@278
|
193
|
jbe@279
|
194 -- END OF DEPRECARED API TABLES --
|
jbe@279
|
195
|
jbe@279
|
196
|
jbe@13
|
197 CREATE TABLE "member_history" (
|
jbe@13
|
198 "id" SERIAL8 PRIMARY KEY,
|
jbe@13
|
199 "member_id" INT4 NOT NULL REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@13
|
200 "until" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
jbe@42
|
201 "active" BOOLEAN NOT NULL,
|
jbe@13
|
202 "name" TEXT NOT NULL );
|
jbe@45
|
203 CREATE INDEX "member_history_member_id_idx" ON "member_history" ("member_id");
|
jbe@13
|
204
|
jbe@57
|
205 COMMENT ON TABLE "member_history" IS 'Filled by trigger; keeps information about old names and active flag of members';
|
jbe@13
|
206
|
jbe@13
|
207 COMMENT ON COLUMN "member_history"."id" IS 'Primary key, which can be used to sort entries correctly (and time warp resistant)';
|
jbe@57
|
208 COMMENT ON COLUMN "member_history"."until" IS 'Timestamp until the data was valid';
|
jbe@13
|
209
|
jbe@13
|
210
|
jbe@159
|
211 CREATE TABLE "rendered_member_statement" (
|
jbe@159
|
212 PRIMARY KEY ("member_id", "format"),
|
jbe@159
|
213 "member_id" INT8 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@159
|
214 "format" TEXT,
|
jbe@159
|
215 "content" TEXT NOT NULL );
|
jbe@159
|
216
|
jbe@159
|
217 COMMENT ON TABLE "rendered_member_statement" IS 'This table may be used by frontends to cache "rendered" member statements (e.g. HTML output generated from wiki text)';
|
jbe@9
|
218
|
jbe@9
|
219
|
jbe@9
|
220 CREATE TABLE "setting" (
|
jbe@9
|
221 PRIMARY KEY ("member_id", "key"),
|
jbe@9
|
222 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@9
|
223 "key" TEXT NOT NULL,
|
jbe@9
|
224 "value" TEXT NOT NULL );
|
jbe@9
|
225 CREATE INDEX "setting_key_idx" ON "setting" ("key");
|
jbe@9
|
226
|
jbe@38
|
227 COMMENT ON TABLE "setting" IS 'Place to store a frontend specific setting for members as a string';
|
jbe@9
|
228
|
jbe@9
|
229 COMMENT ON COLUMN "setting"."key" IS 'Name of the setting, preceded by a frontend specific prefix';
|
jbe@9
|
230
|
jbe@9
|
231
|
jbe@16
|
232 CREATE TABLE "setting_map" (
|
jbe@16
|
233 PRIMARY KEY ("member_id", "key", "subkey"),
|
jbe@16
|
234 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@16
|
235 "key" TEXT NOT NULL,
|
jbe@16
|
236 "subkey" TEXT NOT NULL,
|
jbe@16
|
237 "value" TEXT NOT NULL );
|
jbe@16
|
238 CREATE INDEX "setting_map_key_idx" ON "setting_map" ("key");
|
jbe@16
|
239
|
jbe@23
|
240 COMMENT ON TABLE "setting_map" IS 'Place to store a frontend specific setting for members as a map of key value pairs';
|
jbe@16
|
241
|
jbe@16
|
242 COMMENT ON COLUMN "setting_map"."key" IS 'Name of the setting, preceded by a frontend specific prefix';
|
jbe@16
|
243 COMMENT ON COLUMN "setting_map"."subkey" IS 'Key of a map entry';
|
jbe@16
|
244 COMMENT ON COLUMN "setting_map"."value" IS 'Value of a map entry';
|
jbe@16
|
245
|
jbe@16
|
246
|
jbe@23
|
247 CREATE TABLE "member_relation_setting" (
|
jbe@23
|
248 PRIMARY KEY ("member_id", "key", "other_member_id"),
|
jbe@23
|
249 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
250 "key" TEXT NOT NULL,
|
jbe@23
|
251 "other_member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
252 "value" TEXT NOT NULL );
|
jbe@23
|
253
|
jbe@38
|
254 COMMENT ON TABLE "member_relation_setting" IS 'Place to store a frontend specific setting related to relations between members as a string';
|
jbe@23
|
255
|
jbe@23
|
256
|
jbe@7
|
257 CREATE TYPE "member_image_type" AS ENUM ('photo', 'avatar');
|
jbe@7
|
258
|
jbe@7
|
259 COMMENT ON TYPE "member_image_type" IS 'Types of images for a member';
|
jbe@7
|
260
|
jbe@7
|
261
|
jbe@7
|
262 CREATE TABLE "member_image" (
|
jbe@7
|
263 PRIMARY KEY ("member_id", "image_type", "scaled"),
|
jbe@7
|
264 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@7
|
265 "image_type" "member_image_type",
|
jbe@7
|
266 "scaled" BOOLEAN,
|
jbe@7
|
267 "content_type" TEXT,
|
jbe@7
|
268 "data" BYTEA NOT NULL );
|
jbe@7
|
269
|
jbe@7
|
270 COMMENT ON TABLE "member_image" IS 'Images of members';
|
jbe@7
|
271
|
jbe@7
|
272 COMMENT ON COLUMN "member_image"."scaled" IS 'FALSE for original image, TRUE for scaled version of the image';
|
jbe@0
|
273
|
jbe@0
|
274
|
jbe@4
|
275 CREATE TABLE "member_count" (
|
jbe@5
|
276 "calculated" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
jbe@5
|
277 "total_count" INT4 NOT NULL );
|
jbe@4
|
278
|
jbe@5
|
279 COMMENT ON TABLE "member_count" IS 'Contains one row which contains the total count of active(!) members and a timestamp indicating when the total member count and area member counts were calculated';
|
jbe@4
|
280
|
jbe@5
|
281 COMMENT ON COLUMN "member_count"."calculated" IS 'timestamp indicating when the total member count and area member counts were calculated';
|
jbe@5
|
282 COMMENT ON COLUMN "member_count"."total_count" IS 'Total count of active(!) members';
|
jbe@4
|
283
|
jbe@4
|
284
|
jbe@0
|
285 CREATE TABLE "contact" (
|
jbe@0
|
286 PRIMARY KEY ("member_id", "other_member_id"),
|
jbe@0
|
287 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
288 "other_member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@11
|
289 "public" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@11
|
290 CONSTRAINT "cant_save_yourself_as_contact"
|
jbe@11
|
291 CHECK ("member_id" != "other_member_id") );
|
jbe@113
|
292 CREATE INDEX "contact_other_member_id_idx" ON "contact" ("other_member_id");
|
jbe@0
|
293
|
jbe@0
|
294 COMMENT ON TABLE "contact" IS 'Contact lists';
|
jbe@0
|
295
|
jbe@0
|
296 COMMENT ON COLUMN "contact"."member_id" IS 'Member having the contact list';
|
jbe@0
|
297 COMMENT ON COLUMN "contact"."other_member_id" IS 'Member referenced in the contact list';
|
jbe@0
|
298 COMMENT ON COLUMN "contact"."public" IS 'TRUE = display contact publically';
|
jbe@0
|
299
|
jbe@0
|
300
|
jbe@113
|
301 CREATE TABLE "ignored_member" (
|
jbe@113
|
302 PRIMARY KEY ("member_id", "other_member_id"),
|
jbe@113
|
303 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@113
|
304 "other_member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE );
|
jbe@113
|
305 CREATE INDEX "ignored_member_other_member_id_idx" ON "ignored_member" ("other_member_id");
|
jbe@113
|
306
|
jbe@113
|
307 COMMENT ON TABLE "ignored_member" IS 'Possibility to filter other members';
|
jbe@113
|
308
|
jbe@113
|
309 COMMENT ON COLUMN "ignored_member"."member_id" IS 'Member ignoring someone';
|
jbe@113
|
310 COMMENT ON COLUMN "ignored_member"."other_member_id" IS 'Member being ignored';
|
jbe@113
|
311
|
jbe@113
|
312
|
jbe@220
|
313 CREATE TABLE "session" (
|
jbe@220
|
314 "ident" TEXT PRIMARY KEY,
|
jbe@220
|
315 "additional_secret" TEXT,
|
jbe@220
|
316 "expiry" TIMESTAMPTZ NOT NULL DEFAULT now() + '24 hours',
|
jbe@220
|
317 "member_id" INT8 REFERENCES "member" ("id") ON DELETE SET NULL,
|
jbe@220
|
318 "lang" TEXT );
|
jbe@220
|
319 CREATE INDEX "session_expiry_idx" ON "session" ("expiry");
|
jbe@220
|
320
|
jbe@220
|
321 COMMENT ON TABLE "session" IS 'Sessions, i.e. for a web-frontend or API layer';
|
jbe@220
|
322
|
jbe@220
|
323 COMMENT ON COLUMN "session"."ident" IS 'Secret session identifier (i.e. random string)';
|
jbe@220
|
324 COMMENT ON COLUMN "session"."additional_secret" IS 'Additional field to store a secret, which can be used against CSRF attacks';
|
jbe@220
|
325 COMMENT ON COLUMN "session"."member_id" IS 'Reference to member, who is logged in';
|
jbe@220
|
326 COMMENT ON COLUMN "session"."lang" IS 'Language code of the selected language';
|
jbe@220
|
327
|
jbe@220
|
328
|
jbe@0
|
329 CREATE TABLE "policy" (
|
jbe@0
|
330 "id" SERIAL4 PRIMARY KEY,
|
jbe@9
|
331 "index" INT4 NOT NULL,
|
jbe@0
|
332 "active" BOOLEAN NOT NULL DEFAULT TRUE,
|
jbe@0
|
333 "name" TEXT NOT NULL UNIQUE,
|
jbe@0
|
334 "description" TEXT NOT NULL DEFAULT '',
|
jbe@261
|
335 "polling" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@261
|
336 "admission_time" INTERVAL,
|
jbe@261
|
337 "discussion_time" INTERVAL,
|
jbe@261
|
338 "verification_time" INTERVAL,
|
jbe@261
|
339 "voting_time" INTERVAL,
|
jbe@292
|
340 "issue_quorum_num" INT4,
|
jbe@292
|
341 "issue_quorum_den" INT4,
|
jbe@0
|
342 "initiative_quorum_num" INT4 NOT NULL,
|
jbe@10
|
343 "initiative_quorum_den" INT4 NOT NULL,
|
jbe@167
|
344 "direct_majority_num" INT4 NOT NULL DEFAULT 1,
|
jbe@167
|
345 "direct_majority_den" INT4 NOT NULL DEFAULT 2,
|
jbe@167
|
346 "direct_majority_strict" BOOLEAN NOT NULL DEFAULT TRUE,
|
jbe@167
|
347 "direct_majority_positive" INT4 NOT NULL DEFAULT 0,
|
jbe@167
|
348 "direct_majority_non_negative" INT4 NOT NULL DEFAULT 0,
|
jbe@167
|
349 "indirect_majority_num" INT4 NOT NULL DEFAULT 1,
|
jbe@167
|
350 "indirect_majority_den" INT4 NOT NULL DEFAULT 2,
|
jbe@167
|
351 "indirect_majority_strict" BOOLEAN NOT NULL DEFAULT TRUE,
|
jbe@167
|
352 "indirect_majority_positive" INT4 NOT NULL DEFAULT 0,
|
jbe@167
|
353 "indirect_majority_non_negative" INT4 NOT NULL DEFAULT 0,
|
jbe@167
|
354 "no_reverse_beat_path" BOOLEAN NOT NULL DEFAULT TRUE,
|
jbe@260
|
355 "no_multistage_majority" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@260
|
356 CONSTRAINT "timing" CHECK (
|
jbe@261
|
357 ( "polling" = FALSE AND
|
jbe@260
|
358 "admission_time" NOTNULL AND "discussion_time" NOTNULL AND
|
jbe@260
|
359 "verification_time" NOTNULL AND "voting_time" NOTNULL ) OR
|
jbe@261
|
360 ( "polling" = TRUE AND
|
jbe@263
|
361 "admission_time" ISNULL AND "discussion_time" NOTNULL AND
|
jbe@261
|
362 "verification_time" NOTNULL AND "voting_time" NOTNULL ) OR
|
jbe@261
|
363 ( "polling" = TRUE AND
|
jbe@260
|
364 "admission_time" ISNULL AND "discussion_time" ISNULL AND
|
jbe@292
|
365 "verification_time" ISNULL AND "voting_time" ISNULL ) ),
|
jbe@292
|
366 CONSTRAINT "issue_quorum_if_and_only_if_not_polling" CHECK (
|
jbe@292
|
367 "polling" = "issue_quorum_num" ISNULL AND
|
jbe@292
|
368 "polling" = "issue_quorum_den" ISNULL ) );
|
jbe@0
|
369 CREATE INDEX "policy_active_idx" ON "policy" ("active");
|
jbe@0
|
370
|
jbe@0
|
371 COMMENT ON TABLE "policy" IS 'Policies for a particular proceeding type (timelimits, quorum)';
|
jbe@0
|
372
|
jbe@9
|
373 COMMENT ON COLUMN "policy"."index" IS 'Determines the order in listings';
|
jbe@0
|
374 COMMENT ON COLUMN "policy"."active" IS 'TRUE = policy can be used for new issues';
|
jbe@289
|
375 COMMENT ON COLUMN "policy"."polling" IS 'TRUE = special policy for non-user-generated issues without issue quorum, where certain initiatives (those having the "polling" flag set) do not need to pass the initiative quorum; "admission_time" MUST be set to NULL, the other timings may be set to NULL altogether, allowing individual timing for those issues';
|
jbe@207
|
376 COMMENT ON COLUMN "policy"."admission_time" IS 'Maximum duration of issue state ''admission''; Maximum time an issue stays open without being "accepted"';
|
jbe@207
|
377 COMMENT ON COLUMN "policy"."discussion_time" IS 'Duration of issue state ''discussion''; Regular time until an issue is "half_frozen" after being "accepted"';
|
jbe@207
|
378 COMMENT ON COLUMN "policy"."verification_time" IS 'Duration of issue state ''verification''; Regular time until an issue is "fully_frozen" (e.g. entering issue state ''voting'') after being "half_frozen"';
|
jbe@207
|
379 COMMENT ON COLUMN "policy"."voting_time" IS 'Duration of issue state ''voting''; Time after an issue is "fully_frozen" but not "closed" (duration of issue state ''voting'')';
|
jbe@207
|
380 COMMENT ON COLUMN "policy"."issue_quorum_num" IS 'Numerator of potential supporter quorum to be reached by one initiative of an issue to be "accepted" and enter issue state ''discussion''';
|
jbe@207
|
381 COMMENT ON COLUMN "policy"."issue_quorum_den" IS 'Denominator of potential supporter quorum to be reached by one initiative of an issue to be "accepted" and enter issue state ''discussion''';
|
jbe@10
|
382 COMMENT ON COLUMN "policy"."initiative_quorum_num" IS 'Numerator of satisfied supporter quorum to be reached by an initiative to be "admitted" for voting';
|
jbe@10
|
383 COMMENT ON COLUMN "policy"."initiative_quorum_den" IS 'Denominator of satisfied supporter quorum to be reached by an initiative to be "admitted" for voting';
|
jbe@167
|
384 COMMENT ON COLUMN "policy"."direct_majority_num" IS 'Numerator of fraction of neccessary direct majority for initiatives to be attainable as winner';
|
jbe@167
|
385 COMMENT ON COLUMN "policy"."direct_majority_den" IS 'Denominator of fraction of neccessary direct majority for initaitives to be attainable as winner';
|
jbe@167
|
386 COMMENT ON COLUMN "policy"."direct_majority_strict" IS 'If TRUE, then the direct majority must be strictly greater than "direct_majority_num"/"direct_majority_den", otherwise it may also be equal.';
|
jbe@167
|
387 COMMENT ON COLUMN "policy"."direct_majority_positive" IS 'Absolute number of "positive_votes" neccessary for an initiative to be attainable as winner';
|
jbe@167
|
388 COMMENT ON COLUMN "policy"."direct_majority_non_negative" IS 'Absolute number of sum of "positive_votes" and abstentions neccessary for an initiative to be attainable as winner';
|
jbe@167
|
389 COMMENT ON COLUMN "policy"."indirect_majority_num" IS 'Numerator of fraction of neccessary indirect majority (through beat path) for initiatives to be attainable as winner';
|
jbe@167
|
390 COMMENT ON COLUMN "policy"."indirect_majority_den" IS 'Denominator of fraction of neccessary indirect majority (through beat path) for initiatives to be attainable as winner';
|
jbe@167
|
391 COMMENT ON COLUMN "policy"."indirect_majority_strict" IS 'If TRUE, then the indirect majority must be strictly greater than "indirect_majority_num"/"indirect_majority_den", otherwise it may also be equal.';
|
jbe@167
|
392 COMMENT ON COLUMN "policy"."indirect_majority_positive" IS 'Absolute number of votes in favor of the winner neccessary in a beat path to the status quo for an initaitive to be attainable as winner';
|
jbe@167
|
393 COMMENT ON COLUMN "policy"."indirect_majority_non_negative" IS 'Absolute number of sum of votes in favor and abstentions in a beat path to the status quo for an initiative to be attainable as winner';
|
jbe@158
|
394 COMMENT ON COLUMN "policy"."no_reverse_beat_path" IS 'Causes initiatives with "reverse_beat_path" flag to not be "eligible", thus disallowing them to be winner. See comment on column "initiative"."reverse_beat_path". This option ensures both that a winning initiative is never tied in a (weak) condorcet paradox with the status quo and a winning initiative always beats the status quo directly with a simple majority.';
|
jbe@167
|
395 COMMENT ON COLUMN "policy"."no_multistage_majority" IS 'Causes initiatives with "multistage_majority" flag to not be "eligible", thus disallowing them to be winner. See comment on column "initiative"."multistage_majority". This disqualifies initiatives which could cause an instable result. An instable result in this meaning is a result such that repeating the ballot with same preferences but with the winner of the first ballot as status quo would lead to a different winner in the second ballot. If there are no direct majorities required for the winner, or if in direct comparison only simple majorities are required and "no_reverse_beat_path" is true, then results are always stable and this flag does not have any effect on the winner (but still affects the "eligible" flag of an "initiative").';
|
jbe@0
|
396
|
jbe@0
|
397
|
jbe@97
|
398 CREATE TABLE "unit" (
|
jbe@97
|
399 "id" SERIAL4 PRIMARY KEY,
|
jbe@97
|
400 "parent_id" INT4 REFERENCES "unit" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@97
|
401 "active" BOOLEAN NOT NULL DEFAULT TRUE,
|
jbe@97
|
402 "name" TEXT NOT NULL,
|
jbe@97
|
403 "description" TEXT NOT NULL DEFAULT '',
|
jbe@97
|
404 "member_count" INT4,
|
jbe@97
|
405 "text_search_data" TSVECTOR );
|
jbe@97
|
406 CREATE INDEX "unit_root_idx" ON "unit" ("id") WHERE "parent_id" ISNULL;
|
jbe@97
|
407 CREATE INDEX "unit_parent_id_idx" ON "unit" ("parent_id");
|
jbe@97
|
408 CREATE INDEX "unit_active_idx" ON "unit" ("active");
|
jbe@97
|
409 CREATE INDEX "unit_text_search_data_idx" ON "unit" USING gin ("text_search_data");
|
jbe@97
|
410 CREATE TRIGGER "update_text_search_data"
|
jbe@97
|
411 BEFORE INSERT OR UPDATE ON "unit"
|
jbe@97
|
412 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@97
|
413 tsvector_update_trigger('text_search_data', 'pg_catalog.simple',
|
jbe@97
|
414 "name", "description" );
|
jbe@97
|
415
|
jbe@97
|
416 COMMENT ON TABLE "unit" IS 'Organizational units organized as trees; Delegations are not inherited through these trees.';
|
jbe@97
|
417
|
jbe@97
|
418 COMMENT ON COLUMN "unit"."parent_id" IS 'Parent id of tree node; Multiple roots allowed';
|
jbe@212
|
419 COMMENT ON COLUMN "unit"."active" IS 'TRUE means new issues can be created in areas of this unit';
|
jbe@97
|
420 COMMENT ON COLUMN "unit"."member_count" IS 'Count of members as determined by column "voting_right" in table "privilege"';
|
jbe@97
|
421
|
jbe@97
|
422
|
jbe@203
|
423 CREATE TABLE "unit_setting" (
|
jbe@203
|
424 PRIMARY KEY ("member_id", "key", "unit_id"),
|
jbe@203
|
425 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@203
|
426 "key" TEXT NOT NULL,
|
jbe@203
|
427 "unit_id" INT4 REFERENCES "unit" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@203
|
428 "value" TEXT NOT NULL );
|
jbe@203
|
429
|
jbe@203
|
430 COMMENT ON TABLE "unit_setting" IS 'Place for frontend to store unit specific settings of members as strings';
|
jbe@203
|
431
|
jbe@203
|
432
|
jbe@0
|
433 CREATE TABLE "area" (
|
jbe@0
|
434 "id" SERIAL4 PRIMARY KEY,
|
jbe@97
|
435 "unit_id" INT4 NOT NULL REFERENCES "unit" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
436 "active" BOOLEAN NOT NULL DEFAULT TRUE,
|
jbe@0
|
437 "name" TEXT NOT NULL,
|
jbe@4
|
438 "description" TEXT NOT NULL DEFAULT '',
|
jbe@5
|
439 "direct_member_count" INT4,
|
jbe@5
|
440 "member_weight" INT4,
|
jbe@7
|
441 "text_search_data" TSVECTOR );
|
jbe@97
|
442 CREATE INDEX "area_unit_id_idx" ON "area" ("unit_id");
|
jbe@0
|
443 CREATE INDEX "area_active_idx" ON "area" ("active");
|
jbe@8
|
444 CREATE INDEX "area_text_search_data_idx" ON "area" USING gin ("text_search_data");
|
jbe@7
|
445 CREATE TRIGGER "update_text_search_data"
|
jbe@7
|
446 BEFORE INSERT OR UPDATE ON "area"
|
jbe@7
|
447 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@7
|
448 tsvector_update_trigger('text_search_data', 'pg_catalog.simple',
|
jbe@7
|
449 "name", "description" );
|
jbe@0
|
450
|
jbe@0
|
451 COMMENT ON TABLE "area" IS 'Subject areas';
|
jbe@0
|
452
|
jbe@5
|
453 COMMENT ON COLUMN "area"."active" IS 'TRUE means new issues can be created in this area';
|
jbe@5
|
454 COMMENT ON COLUMN "area"."direct_member_count" IS 'Number of active members of that area (ignoring their weight), as calculated from view "area_member_count"';
|
jbe@5
|
455 COMMENT ON COLUMN "area"."member_weight" IS 'Same as "direct_member_count" but respecting delegations';
|
jbe@0
|
456
|
jbe@0
|
457
|
jbe@23
|
458 CREATE TABLE "area_setting" (
|
jbe@23
|
459 PRIMARY KEY ("member_id", "key", "area_id"),
|
jbe@23
|
460 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
461 "key" TEXT NOT NULL,
|
jbe@23
|
462 "area_id" INT4 REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
463 "value" TEXT NOT NULL );
|
jbe@23
|
464
|
jbe@23
|
465 COMMENT ON TABLE "area_setting" IS 'Place for frontend to store area specific settings of members as strings';
|
jbe@23
|
466
|
jbe@23
|
467
|
jbe@9
|
468 CREATE TABLE "allowed_policy" (
|
jbe@9
|
469 PRIMARY KEY ("area_id", "policy_id"),
|
jbe@9
|
470 "area_id" INT4 REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@9
|
471 "policy_id" INT4 NOT NULL REFERENCES "policy" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@9
|
472 "default_policy" BOOLEAN NOT NULL DEFAULT FALSE );
|
jbe@9
|
473 CREATE UNIQUE INDEX "allowed_policy_one_default_per_area_idx" ON "allowed_policy" ("area_id") WHERE "default_policy";
|
jbe@9
|
474
|
jbe@9
|
475 COMMENT ON TABLE "allowed_policy" IS 'Selects which policies can be used in each area';
|
jbe@9
|
476
|
jbe@9
|
477 COMMENT ON COLUMN "allowed_policy"."default_policy" IS 'One policy per area can be set as default.';
|
jbe@9
|
478
|
jbe@9
|
479
|
jbe@21
|
480 CREATE TYPE "snapshot_event" AS ENUM ('periodic', 'end_of_admission', 'half_freeze', 'full_freeze');
|
jbe@21
|
481
|
jbe@21
|
482 COMMENT ON TYPE "snapshot_event" IS 'Reason for snapshots: ''periodic'' = due to periodic recalculation, ''end_of_admission'' = saved state at end of admission period, ''half_freeze'' = saved state at end of discussion period, ''full_freeze'' = saved state at end of verification period';
|
jbe@8
|
483
|
jbe@8
|
484
|
jbe@112
|
485 CREATE TYPE "issue_state" AS ENUM (
|
jbe@112
|
486 'admission', 'discussion', 'verification', 'voting',
|
jbe@113
|
487 'canceled_revoked_before_accepted',
|
jbe@113
|
488 'canceled_issue_not_accepted',
|
jbe@113
|
489 'canceled_after_revocation_during_discussion',
|
jbe@113
|
490 'canceled_after_revocation_during_verification',
|
jbe@113
|
491 'calculation',
|
jbe@113
|
492 'canceled_no_initiative_admitted',
|
jbe@112
|
493 'finished_without_winner', 'finished_with_winner');
|
jbe@111
|
494
|
jbe@111
|
495 COMMENT ON TYPE "issue_state" IS 'State of issues';
|
jbe@111
|
496
|
jbe@111
|
497
|
jbe@0
|
498 CREATE TABLE "issue" (
|
jbe@0
|
499 "id" SERIAL4 PRIMARY KEY,
|
jbe@0
|
500 "area_id" INT4 NOT NULL REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
501 "policy_id" INT4 NOT NULL REFERENCES "policy" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
jbe@111
|
502 "state" "issue_state" NOT NULL DEFAULT 'admission',
|
jbe@0
|
503 "created" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
jbe@0
|
504 "accepted" TIMESTAMPTZ,
|
jbe@3
|
505 "half_frozen" TIMESTAMPTZ,
|
jbe@3
|
506 "fully_frozen" TIMESTAMPTZ,
|
jbe@0
|
507 "closed" TIMESTAMPTZ,
|
jbe@0
|
508 "ranks_available" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@59
|
509 "cleaned" TIMESTAMPTZ,
|
jbe@291
|
510 "admission_time" INTERVAL,
|
jbe@22
|
511 "discussion_time" INTERVAL NOT NULL,
|
jbe@22
|
512 "verification_time" INTERVAL NOT NULL,
|
jbe@22
|
513 "voting_time" INTERVAL NOT NULL,
|
jbe@0
|
514 "snapshot" TIMESTAMPTZ,
|
jbe@8
|
515 "latest_snapshot_event" "snapshot_event",
|
jbe@0
|
516 "population" INT4,
|
jbe@4
|
517 "voter_count" INT4,
|
jbe@170
|
518 "status_quo_schulze_rank" INT4,
|
jbe@291
|
519 CONSTRAINT "admission_time_not_null_unless_instantly_accepted" CHECK (
|
jbe@291
|
520 "admission_time" NOTNULL OR ("accepted" NOTNULL AND "accepted" = "created") ),
|
jbe@111
|
521 CONSTRAINT "valid_state" CHECK ((
|
jbe@3
|
522 ("accepted" ISNULL AND "half_frozen" ISNULL AND "fully_frozen" ISNULL AND "closed" ISNULL AND "ranks_available" = FALSE) OR
|
jbe@3
|
523 ("accepted" ISNULL AND "half_frozen" ISNULL AND "fully_frozen" ISNULL AND "closed" NOTNULL AND "ranks_available" = FALSE) OR
|
jbe@3
|
524 ("accepted" NOTNULL AND "half_frozen" ISNULL AND "fully_frozen" ISNULL AND "closed" ISNULL AND "ranks_available" = FALSE) OR
|
jbe@34
|
525 ("accepted" NOTNULL AND "half_frozen" ISNULL AND "fully_frozen" ISNULL AND "closed" NOTNULL AND "ranks_available" = FALSE) OR
|
jbe@3
|
526 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" ISNULL AND "closed" ISNULL AND "ranks_available" = FALSE) OR
|
jbe@34
|
527 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" ISNULL AND "closed" NOTNULL AND "ranks_available" = FALSE) OR
|
jbe@3
|
528 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" NOTNULL AND "closed" ISNULL AND "ranks_available" = FALSE) OR
|
jbe@3
|
529 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" NOTNULL AND "closed" NOTNULL AND "ranks_available" = FALSE) OR
|
jbe@111
|
530 ("accepted" NOTNULL AND "half_frozen" NOTNULL AND "fully_frozen" NOTNULL AND "closed" NOTNULL AND "ranks_available" = TRUE)) AND (
|
jbe@111
|
531 ("state" = 'admission' AND "closed" ISNULL AND "accepted" ISNULL) OR
|
jbe@111
|
532 ("state" = 'discussion' AND "closed" ISNULL AND "accepted" NOTNULL AND "half_frozen" ISNULL) OR
|
jbe@111
|
533 ("state" = 'verification' AND "closed" ISNULL AND "half_frozen" NOTNULL AND "fully_frozen" ISNULL) OR
|
jbe@111
|
534 ("state" = 'voting' AND "closed" ISNULL AND "fully_frozen" NOTNULL) OR
|
jbe@113
|
535 ("state" = 'canceled_revoked_before_accepted' AND "closed" NOTNULL AND "accepted" ISNULL) OR
|
jbe@113
|
536 ("state" = 'canceled_issue_not_accepted' AND "closed" NOTNULL AND "accepted" ISNULL) OR
|
jbe@113
|
537 ("state" = 'canceled_after_revocation_during_discussion' AND "closed" NOTNULL AND "half_frozen" ISNULL) OR
|
jbe@113
|
538 ("state" = 'canceled_after_revocation_during_verification' AND "closed" NOTNULL AND "fully_frozen" ISNULL) OR
|
jbe@113
|
539 ("state" = 'calculation' AND "closed" NOTNULL AND "fully_frozen" NOTNULL AND "ranks_available" = FALSE) OR
|
jbe@113
|
540 ("state" = 'canceled_no_initiative_admitted' AND "closed" NOTNULL AND "fully_frozen" NOTNULL AND "ranks_available" = TRUE) OR
|
jbe@113
|
541 ("state" = 'finished_without_winner' AND "closed" NOTNULL AND "fully_frozen" NOTNULL AND "ranks_available" = TRUE) OR
|
jbe@113
|
542 ("state" = 'finished_with_winner' AND "closed" NOTNULL AND "fully_frozen" NOTNULL AND "ranks_available" = TRUE)
|
jbe@111
|
543 )),
|
jbe@3
|
544 CONSTRAINT "state_change_order" CHECK (
|
jbe@10
|
545 "created" <= "accepted" AND
|
jbe@10
|
546 "accepted" <= "half_frozen" AND
|
jbe@10
|
547 "half_frozen" <= "fully_frozen" AND
|
jbe@3
|
548 "fully_frozen" <= "closed" ),
|
jbe@61
|
549 CONSTRAINT "only_closed_issues_may_be_cleaned" CHECK (
|
jbe@61
|
550 "cleaned" ISNULL OR "closed" NOTNULL ),
|
jbe@10
|
551 CONSTRAINT "last_snapshot_on_full_freeze"
|
jbe@10
|
552 CHECK ("snapshot" = "fully_frozen"), -- NOTE: snapshot can be set, while frozen is NULL yet
|
jbe@10
|
553 CONSTRAINT "freeze_requires_snapshot"
|
jbe@10
|
554 CHECK ("fully_frozen" ISNULL OR "snapshot" NOTNULL),
|
jbe@10
|
555 CONSTRAINT "set_both_or_none_of_snapshot_and_latest_snapshot_event"
|
jbe@10
|
556 CHECK ("snapshot" NOTNULL = "latest_snapshot_event" NOTNULL) );
|
jbe@0
|
557 CREATE INDEX "issue_area_id_idx" ON "issue" ("area_id");
|
jbe@0
|
558 CREATE INDEX "issue_policy_id_idx" ON "issue" ("policy_id");
|
jbe@16
|
559 CREATE INDEX "issue_created_idx" ON "issue" ("created");
|
jbe@16
|
560 CREATE INDEX "issue_accepted_idx" ON "issue" ("accepted");
|
jbe@16
|
561 CREATE INDEX "issue_half_frozen_idx" ON "issue" ("half_frozen");
|
jbe@16
|
562 CREATE INDEX "issue_fully_frozen_idx" ON "issue" ("fully_frozen");
|
jbe@16
|
563 CREATE INDEX "issue_closed_idx" ON "issue" ("closed");
|
jbe@0
|
564 CREATE INDEX "issue_created_idx_open" ON "issue" ("created") WHERE "closed" ISNULL;
|
jbe@16
|
565 CREATE INDEX "issue_closed_idx_canceled" ON "issue" ("closed") WHERE "fully_frozen" ISNULL;
|
jbe@0
|
566
|
jbe@0
|
567 COMMENT ON TABLE "issue" IS 'Groups of initiatives';
|
jbe@0
|
568
|
jbe@170
|
569 COMMENT ON COLUMN "issue"."accepted" IS 'Point in time, when one initiative of issue reached the "issue_quorum"';
|
jbe@170
|
570 COMMENT ON COLUMN "issue"."half_frozen" IS 'Point in time, when "discussion_time" has elapsed; Frontends must ensure that for half_frozen issues a) initiatives are not revoked, b) no new drafts are created, c) no initiators are added or removed.';
|
jbe@170
|
571 COMMENT ON COLUMN "issue"."fully_frozen" IS 'Point in time, when "verification_time" has elapsed and voting has started; Frontends must ensure that for fully_frozen issues additionally to the restrictions for half_frozen issues a) initiatives are not created, b) no interest is created or removed, c) no supporters are added or removed, d) no opinions are created, changed or deleted.';
|
jbe@170
|
572 COMMENT ON COLUMN "issue"."closed" IS 'Point in time, when "admission_time" or "voting_time" have elapsed, and issue is no longer active; Frontends must ensure that for closed issues additionally to the restrictions for half_frozen and fully_frozen issues a) no voter is added or removed to/from the direct_voter table, b) no votes are added, modified or removed.';
|
jbe@170
|
573 COMMENT ON COLUMN "issue"."ranks_available" IS 'TRUE = ranks have been calculated';
|
jbe@170
|
574 COMMENT ON COLUMN "issue"."cleaned" IS 'Point in time, when discussion data and votes had been deleted';
|
jbe@170
|
575 COMMENT ON COLUMN "issue"."admission_time" IS 'Copied from "policy" table at creation of issue';
|
jbe@170
|
576 COMMENT ON COLUMN "issue"."discussion_time" IS 'Copied from "policy" table at creation of issue';
|
jbe@170
|
577 COMMENT ON COLUMN "issue"."verification_time" IS 'Copied from "policy" table at creation of issue';
|
jbe@170
|
578 COMMENT ON COLUMN "issue"."voting_time" IS 'Copied from "policy" table at creation of issue';
|
jbe@170
|
579 COMMENT ON COLUMN "issue"."snapshot" IS 'Point in time, when snapshot tables have been updated and "population" and *_count values were precalculated';
|
jbe@170
|
580 COMMENT ON COLUMN "issue"."latest_snapshot_event" IS 'Event type of latest snapshot for issue; Can be used to select the latest snapshot data in the snapshot tables';
|
jbe@170
|
581 COMMENT ON COLUMN "issue"."population" IS 'Sum of "weight" column in table "direct_population_snapshot"';
|
jbe@170
|
582 COMMENT ON COLUMN "issue"."voter_count" IS 'Total number of direct and delegating voters; This value is related to the final voting, while "population" is related to snapshots before the final voting';
|
jbe@170
|
583 COMMENT ON COLUMN "issue"."status_quo_schulze_rank" IS 'Schulze rank of status quo, as calculated by "calculate_ranks" function';
|
jbe@0
|
584
|
jbe@0
|
585
|
jbe@23
|
586 CREATE TABLE "issue_setting" (
|
jbe@23
|
587 PRIMARY KEY ("member_id", "key", "issue_id"),
|
jbe@23
|
588 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
589 "key" TEXT NOT NULL,
|
jbe@23
|
590 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
591 "value" TEXT NOT NULL );
|
jbe@23
|
592
|
jbe@23
|
593 COMMENT ON TABLE "issue_setting" IS 'Place for frontend to store issue specific settings of members as strings';
|
jbe@23
|
594
|
jbe@23
|
595
|
jbe@0
|
596 CREATE TABLE "initiative" (
|
jbe@0
|
597 UNIQUE ("issue_id", "id"), -- index needed for foreign-key on table "vote"
|
jbe@0
|
598 "issue_id" INT4 NOT NULL REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
599 "id" SERIAL4 PRIMARY KEY,
|
jbe@0
|
600 "name" TEXT NOT NULL,
|
jbe@261
|
601 "polling" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@8
|
602 "discussion_url" TEXT,
|
jbe@0
|
603 "created" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
jbe@0
|
604 "revoked" TIMESTAMPTZ,
|
jbe@112
|
605 "revoked_by_member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
jbe@14
|
606 "suggested_initiative_id" INT4 REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
607 "admitted" BOOLEAN,
|
jbe@0
|
608 "supporter_count" INT4,
|
jbe@0
|
609 "informed_supporter_count" INT4,
|
jbe@0
|
610 "satisfied_supporter_count" INT4,
|
jbe@0
|
611 "satisfied_informed_supporter_count" INT4,
|
jbe@0
|
612 "positive_votes" INT4,
|
jbe@0
|
613 "negative_votes" INT4,
|
jbe@167
|
614 "direct_majority" BOOLEAN,
|
jbe@167
|
615 "indirect_majority" BOOLEAN,
|
jbe@170
|
616 "schulze_rank" INT4,
|
jbe@167
|
617 "better_than_status_quo" BOOLEAN,
|
jbe@167
|
618 "worse_than_status_quo" BOOLEAN,
|
jbe@158
|
619 "reverse_beat_path" BOOLEAN,
|
jbe@154
|
620 "multistage_majority" BOOLEAN,
|
jbe@154
|
621 "eligible" BOOLEAN,
|
jbe@126
|
622 "winner" BOOLEAN,
|
jbe@0
|
623 "rank" INT4,
|
jbe@7
|
624 "text_search_data" TSVECTOR,
|
jbe@112
|
625 CONSTRAINT "all_or_none_of_revoked_and_revoked_by_member_id_must_be_null"
|
jbe@112
|
626 CHECK ("revoked" NOTNULL = "revoked_by_member_id" NOTNULL),
|
jbe@14
|
627 CONSTRAINT "non_revoked_initiatives_cant_suggest_other"
|
jbe@14
|
628 CHECK ("revoked" NOTNULL OR "suggested_initiative_id" ISNULL),
|
jbe@0
|
629 CONSTRAINT "revoked_initiatives_cant_be_admitted"
|
jbe@0
|
630 CHECK ("revoked" ISNULL OR "admitted" ISNULL),
|
jbe@128
|
631 CONSTRAINT "non_admitted_initiatives_cant_contain_voting_results" CHECK (
|
jbe@128
|
632 ( "admitted" NOTNULL AND "admitted" = TRUE ) OR
|
jbe@167
|
633 ( "positive_votes" ISNULL AND "negative_votes" ISNULL AND
|
jbe@167
|
634 "direct_majority" ISNULL AND "indirect_majority" ISNULL AND
|
jbe@173
|
635 "schulze_rank" ISNULL AND
|
jbe@167
|
636 "better_than_status_quo" ISNULL AND "worse_than_status_quo" ISNULL AND
|
jbe@167
|
637 "reverse_beat_path" ISNULL AND "multistage_majority" ISNULL AND
|
jbe@173
|
638 "eligible" ISNULL AND "winner" ISNULL AND "rank" ISNULL ) ),
|
jbe@173
|
639 CONSTRAINT "better_excludes_worse" CHECK (NOT ("better_than_status_quo" AND "worse_than_status_quo")),
|
jbe@175
|
640 CONSTRAINT "minimum_requirement_to_be_eligible" CHECK (
|
jbe@175
|
641 "eligible" = FALSE OR
|
jbe@175
|
642 ("direct_majority" AND "indirect_majority" AND "better_than_status_quo") ),
|
jbe@175
|
643 CONSTRAINT "winner_must_be_eligible" CHECK ("winner"=FALSE OR "eligible"=TRUE),
|
jbe@175
|
644 CONSTRAINT "winner_must_have_first_rank" CHECK ("winner"=FALSE OR "rank"=1),
|
jbe@176
|
645 CONSTRAINT "eligible_at_first_rank_is_winner" CHECK ("eligible"=FALSE OR "rank"!=1 OR "winner"=TRUE),
|
jbe@173
|
646 CONSTRAINT "unique_rank_per_issue" UNIQUE ("issue_id", "rank") );
|
jbe@16
|
647 CREATE INDEX "initiative_created_idx" ON "initiative" ("created");
|
jbe@16
|
648 CREATE INDEX "initiative_revoked_idx" ON "initiative" ("revoked");
|
jbe@8
|
649 CREATE INDEX "initiative_text_search_data_idx" ON "initiative" USING gin ("text_search_data");
|
jbe@7
|
650 CREATE TRIGGER "update_text_search_data"
|
jbe@7
|
651 BEFORE INSERT OR UPDATE ON "initiative"
|
jbe@7
|
652 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@8
|
653 tsvector_update_trigger('text_search_data', 'pg_catalog.simple',
|
jbe@8
|
654 "name", "discussion_url");
|
jbe@0
|
655
|
jbe@10
|
656 COMMENT ON TABLE "initiative" IS 'Group of members publishing drafts for resolutions to be passed; Frontends must ensure that initiatives of half_frozen issues are not revoked, and that initiatives of fully_frozen or closed issues are neither revoked nor created.';
|
jbe@0
|
657
|
jbe@289
|
658 COMMENT ON COLUMN "initiative"."polling" IS 'Initiative does not need to pass the initiative quorum (see "policy"."polling")';
|
jbe@210
|
659 COMMENT ON COLUMN "initiative"."discussion_url" IS 'URL pointing to a discussion platform for this initiative';
|
jbe@210
|
660 COMMENT ON COLUMN "initiative"."revoked" IS 'Point in time, when one initiator decided to revoke the initiative';
|
jbe@210
|
661 COMMENT ON COLUMN "initiative"."revoked_by_member_id" IS 'Member, who decided to revoke the initiative';
|
jbe@210
|
662 COMMENT ON COLUMN "initiative"."admitted" IS 'TRUE, if initiative reaches the "initiative_quorum" when freezing the issue';
|
jbe@0
|
663 COMMENT ON COLUMN "initiative"."supporter_count" IS 'Calculated from table "direct_supporter_snapshot"';
|
jbe@0
|
664 COMMENT ON COLUMN "initiative"."informed_supporter_count" IS 'Calculated from table "direct_supporter_snapshot"';
|
jbe@0
|
665 COMMENT ON COLUMN "initiative"."satisfied_supporter_count" IS 'Calculated from table "direct_supporter_snapshot"';
|
jbe@0
|
666 COMMENT ON COLUMN "initiative"."satisfied_informed_supporter_count" IS 'Calculated from table "direct_supporter_snapshot"';
|
jbe@210
|
667 COMMENT ON COLUMN "initiative"."positive_votes" IS 'Calculated from table "direct_voter"';
|
jbe@210
|
668 COMMENT ON COLUMN "initiative"."negative_votes" IS 'Calculated from table "direct_voter"';
|
jbe@210
|
669 COMMENT ON COLUMN "initiative"."direct_majority" IS 'TRUE, if "positive_votes"/("positive_votes"+"negative_votes") is strictly greater or greater-equal than "direct_majority_num"/"direct_majority_den", and "positive_votes" is greater-equal than "direct_majority_positive", and ("positive_votes"+abstentions) is greater-equal than "direct_majority_non_negative"';
|
jbe@210
|
670 COMMENT ON COLUMN "initiative"."indirect_majority" IS 'Same as "direct_majority", but also considering indirect beat paths';
|
jbe@210
|
671 COMMENT ON COLUMN "initiative"."schulze_rank" IS 'Schulze-Ranking without tie-breaking';
|
jbe@210
|
672 COMMENT ON COLUMN "initiative"."better_than_status_quo" IS 'TRUE, if initiative has a schulze-ranking better than the status quo (without tie-breaking)';
|
jbe@210
|
673 COMMENT ON COLUMN "initiative"."worse_than_status_quo" IS 'TRUE, if initiative has a schulze-ranking worse than the status quo (without tie-breaking)';
|
jbe@210
|
674 COMMENT ON COLUMN "initiative"."reverse_beat_path" IS 'TRUE, if there is a beat path (may include ties) from this initiative to the status quo';
|
jbe@210
|
675 COMMENT ON COLUMN "initiative"."multistage_majority" IS 'TRUE, if either (a) this initiative has no better rank than the status quo, or (b) there exists a better ranked initiative X, which directly beats this initiative, and either more voters prefer X to this initiative than voters preferring X to the status quo or less voters prefer this initiative to X than voters preferring the status quo to X';
|
jbe@210
|
676 COMMENT ON COLUMN "initiative"."eligible" IS 'Initiative has a "direct_majority" and an "indirect_majority", is "better_than_status_quo" and depending on selected policy the initiative has no "reverse_beat_path" or "multistage_majority"';
|
jbe@210
|
677 COMMENT ON COLUMN "initiative"."winner" IS 'Winner is the "eligible" initiative with best "schulze_rank" and in case of ties with lowest "id"';
|
jbe@210
|
678 COMMENT ON COLUMN "initiative"."rank" IS 'Unique ranking for all "admitted" initiatives per issue; lower rank is better; a winner always has rank 1, but rank 1 does not imply that an initiative is winner; initiatives with "direct_majority" AND "indirect_majority" always have a better (lower) rank than other initiatives';
|
jbe@0
|
679
|
jbe@0
|
680
|
jbe@61
|
681 CREATE TABLE "battle" (
|
jbe@126
|
682 "issue_id" INT4 NOT NULL,
|
jbe@61
|
683 "winning_initiative_id" INT4,
|
jbe@61
|
684 FOREIGN KEY ("issue_id", "winning_initiative_id") REFERENCES "initiative" ("issue_id", "id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@61
|
685 "losing_initiative_id" INT4,
|
jbe@61
|
686 FOREIGN KEY ("issue_id", "losing_initiative_id") REFERENCES "initiative" ("issue_id", "id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@126
|
687 "count" INT4 NOT NULL,
|
jbe@126
|
688 CONSTRAINT "initiative_ids_not_equal" CHECK (
|
jbe@126
|
689 "winning_initiative_id" != "losing_initiative_id" OR
|
jbe@126
|
690 ( ("winning_initiative_id" NOTNULL AND "losing_initiative_id" ISNULL) OR
|
jbe@126
|
691 ("winning_initiative_id" ISNULL AND "losing_initiative_id" NOTNULL) ) ) );
|
jbe@126
|
692 CREATE UNIQUE INDEX "battle_winning_losing_idx" ON "battle" ("issue_id", "winning_initiative_id", "losing_initiative_id");
|
jbe@126
|
693 CREATE UNIQUE INDEX "battle_winning_null_idx" ON "battle" ("issue_id", "winning_initiative_id") WHERE "losing_initiative_id" ISNULL;
|
jbe@126
|
694 CREATE UNIQUE INDEX "battle_null_losing_idx" ON "battle" ("issue_id", "losing_initiative_id") WHERE "winning_initiative_id" ISNULL;
|
jbe@126
|
695
|
jbe@126
|
696 COMMENT ON TABLE "battle" IS 'Number of members preferring one initiative to another; Filled by "battle_view" when closing an issue; NULL as initiative_id denotes virtual "status-quo" initiative';
|
jbe@61
|
697
|
jbe@61
|
698
|
jbe@113
|
699 CREATE TABLE "ignored_initiative" (
|
jbe@113
|
700 PRIMARY KEY ("initiative_id", "member_id"),
|
jbe@113
|
701 "initiative_id" INT4 REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@113
|
702 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE );
|
jbe@113
|
703 CREATE INDEX "ignored_initiative_member_id_idx" ON "ignored_initiative" ("member_id");
|
jbe@113
|
704
|
jbe@113
|
705 COMMENT ON TABLE "ignored_initiative" IS 'Possibility to filter initiatives';
|
jbe@113
|
706
|
jbe@113
|
707
|
jbe@23
|
708 CREATE TABLE "initiative_setting" (
|
jbe@23
|
709 PRIMARY KEY ("member_id", "key", "initiative_id"),
|
jbe@23
|
710 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
711 "key" TEXT NOT NULL,
|
jbe@23
|
712 "initiative_id" INT4 REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
713 "value" TEXT NOT NULL );
|
jbe@23
|
714
|
jbe@23
|
715 COMMENT ON TABLE "initiative_setting" IS 'Place for frontend to store initiative specific settings of members as strings';
|
jbe@23
|
716
|
jbe@23
|
717
|
jbe@0
|
718 CREATE TABLE "draft" (
|
jbe@0
|
719 UNIQUE ("initiative_id", "id"), -- index needed for foreign-key on table "supporter"
|
jbe@0
|
720 "initiative_id" INT4 NOT NULL REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
721 "id" SERIAL8 PRIMARY KEY,
|
jbe@0
|
722 "created" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
jbe@0
|
723 "author_id" INT4 NOT NULL REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
jbe@9
|
724 "formatting_engine" TEXT,
|
jbe@7
|
725 "content" TEXT NOT NULL,
|
jbe@7
|
726 "text_search_data" TSVECTOR );
|
jbe@16
|
727 CREATE INDEX "draft_created_idx" ON "draft" ("created");
|
jbe@9
|
728 CREATE INDEX "draft_author_id_created_idx" ON "draft" ("author_id", "created");
|
jbe@8
|
729 CREATE INDEX "draft_text_search_data_idx" ON "draft" USING gin ("text_search_data");
|
jbe@7
|
730 CREATE TRIGGER "update_text_search_data"
|
jbe@7
|
731 BEFORE INSERT OR UPDATE ON "draft"
|
jbe@7
|
732 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@7
|
733 tsvector_update_trigger('text_search_data', 'pg_catalog.simple', "content");
|
jbe@0
|
734
|
jbe@10
|
735 COMMENT ON TABLE "draft" IS 'Drafts of initiatives to solve issues; Frontends must ensure that new drafts for initiatives of half_frozen, fully_frozen or closed issues can''t be created.';
|
jbe@0
|
736
|
jbe@9
|
737 COMMENT ON COLUMN "draft"."formatting_engine" IS 'Allows different formatting engines (i.e. wiki formats) to be used';
|
jbe@9
|
738 COMMENT ON COLUMN "draft"."content" IS 'Text of the draft in a format depending on the field "formatting_engine"';
|
jbe@9
|
739
|
jbe@0
|
740
|
jbe@63
|
741 CREATE TABLE "rendered_draft" (
|
jbe@63
|
742 PRIMARY KEY ("draft_id", "format"),
|
jbe@63
|
743 "draft_id" INT8 REFERENCES "draft" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@63
|
744 "format" TEXT,
|
jbe@63
|
745 "content" TEXT NOT NULL );
|
jbe@63
|
746
|
jbe@63
|
747 COMMENT ON TABLE "rendered_draft" IS 'This table may be used by frontends to cache "rendered" drafts (e.g. HTML output generated from wiki text)';
|
jbe@63
|
748
|
jbe@63
|
749
|
jbe@0
|
750 CREATE TABLE "suggestion" (
|
jbe@0
|
751 UNIQUE ("initiative_id", "id"), -- index needed for foreign-key on table "opinion"
|
jbe@0
|
752 "initiative_id" INT4 NOT NULL REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
753 "id" SERIAL8 PRIMARY KEY,
|
jbe@160
|
754 "draft_id" INT8 NOT NULL,
|
jbe@160
|
755 FOREIGN KEY ("initiative_id", "draft_id") REFERENCES "draft" ("initiative_id", "id") ON DELETE NO ACTION ON UPDATE CASCADE,
|
jbe@0
|
756 "created" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
jbe@0
|
757 "author_id" INT4 NOT NULL REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
jbe@0
|
758 "name" TEXT NOT NULL,
|
jbe@159
|
759 "formatting_engine" TEXT,
|
jbe@159
|
760 "content" TEXT NOT NULL DEFAULT '',
|
jbe@7
|
761 "text_search_data" TSVECTOR,
|
jbe@0
|
762 "minus2_unfulfilled_count" INT4,
|
jbe@0
|
763 "minus2_fulfilled_count" INT4,
|
jbe@0
|
764 "minus1_unfulfilled_count" INT4,
|
jbe@0
|
765 "minus1_fulfilled_count" INT4,
|
jbe@0
|
766 "plus1_unfulfilled_count" INT4,
|
jbe@0
|
767 "plus1_fulfilled_count" INT4,
|
jbe@0
|
768 "plus2_unfulfilled_count" INT4,
|
jbe@0
|
769 "plus2_fulfilled_count" INT4 );
|
jbe@16
|
770 CREATE INDEX "suggestion_created_idx" ON "suggestion" ("created");
|
jbe@9
|
771 CREATE INDEX "suggestion_author_id_created_idx" ON "suggestion" ("author_id", "created");
|
jbe@8
|
772 CREATE INDEX "suggestion_text_search_data_idx" ON "suggestion" USING gin ("text_search_data");
|
jbe@7
|
773 CREATE TRIGGER "update_text_search_data"
|
jbe@7
|
774 BEFORE INSERT OR UPDATE ON "suggestion"
|
jbe@7
|
775 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@7
|
776 tsvector_update_trigger('text_search_data', 'pg_catalog.simple',
|
jbe@159
|
777 "name", "content");
|
jbe@0
|
778
|
jbe@10
|
779 COMMENT ON TABLE "suggestion" IS 'Suggestions to initiators, to change the current draft; must not be deleted explicitly, as they vanish automatically if the last opinion is deleted';
|
jbe@0
|
780
|
jbe@160
|
781 COMMENT ON COLUMN "suggestion"."draft_id" IS 'Draft, which the author has seen when composing the suggestion; should always be set by a frontend, but defaults to current draft of the initiative (implemented by trigger "default_for_draft_id")';
|
jbe@0
|
782 COMMENT ON COLUMN "suggestion"."minus2_unfulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters';
|
jbe@0
|
783 COMMENT ON COLUMN "suggestion"."minus2_fulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters';
|
jbe@0
|
784 COMMENT ON COLUMN "suggestion"."minus1_unfulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters';
|
jbe@0
|
785 COMMENT ON COLUMN "suggestion"."minus1_fulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters';
|
jbe@0
|
786 COMMENT ON COLUMN "suggestion"."plus1_unfulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters';
|
jbe@0
|
787 COMMENT ON COLUMN "suggestion"."plus1_fulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters';
|
jbe@0
|
788 COMMENT ON COLUMN "suggestion"."plus2_unfulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters';
|
jbe@0
|
789 COMMENT ON COLUMN "suggestion"."plus2_fulfilled_count" IS 'Calculated from table "direct_supporter_snapshot", not requiring informed supporters';
|
jbe@0
|
790
|
jbe@0
|
791
|
jbe@159
|
792 CREATE TABLE "rendered_suggestion" (
|
jbe@159
|
793 PRIMARY KEY ("suggestion_id", "format"),
|
jbe@159
|
794 "suggestion_id" INT8 REFERENCES "suggestion" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@159
|
795 "format" TEXT,
|
jbe@159
|
796 "content" TEXT NOT NULL );
|
jbe@159
|
797
|
jbe@159
|
798 COMMENT ON TABLE "rendered_suggestion" IS 'This table may be used by frontends to cache "rendered" drafts (e.g. HTML output generated from wiki text)';
|
jbe@159
|
799
|
jbe@159
|
800
|
jbe@23
|
801 CREATE TABLE "suggestion_setting" (
|
jbe@23
|
802 PRIMARY KEY ("member_id", "key", "suggestion_id"),
|
jbe@23
|
803 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
804 "key" TEXT NOT NULL,
|
jbe@23
|
805 "suggestion_id" INT8 REFERENCES "suggestion" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@23
|
806 "value" TEXT NOT NULL );
|
jbe@23
|
807
|
jbe@23
|
808 COMMENT ON TABLE "suggestion_setting" IS 'Place for frontend to store suggestion specific settings of members as strings';
|
jbe@23
|
809
|
jbe@23
|
810
|
jbe@97
|
811 CREATE TABLE "privilege" (
|
jbe@97
|
812 PRIMARY KEY ("unit_id", "member_id"),
|
jbe@97
|
813 "unit_id" INT4 REFERENCES "unit" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@97
|
814 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@97
|
815 "admin_manager" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@97
|
816 "unit_manager" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@97
|
817 "area_manager" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@261
|
818 "member_manager" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@261
|
819 "initiative_right" BOOLEAN NOT NULL DEFAULT TRUE,
|
jbe@261
|
820 "voting_right" BOOLEAN NOT NULL DEFAULT TRUE,
|
jbe@261
|
821 "polling_right" BOOLEAN NOT NULL DEFAULT FALSE );
|
jbe@97
|
822
|
jbe@97
|
823 COMMENT ON TABLE "privilege" IS 'Members rights related to each unit';
|
jbe@97
|
824
|
jbe@289
|
825 COMMENT ON COLUMN "privilege"."admin_manager" IS 'Grant/revoke any privileges to/from other members';
|
jbe@289
|
826 COMMENT ON COLUMN "privilege"."unit_manager" IS 'Create and disable sub units';
|
jbe@289
|
827 COMMENT ON COLUMN "privilege"."area_manager" IS 'Create and disable areas and set area parameters';
|
jbe@289
|
828 COMMENT ON COLUMN "privilege"."member_manager" IS 'Adding/removing members from the unit, granting or revoking "initiative_right" and "voting_right"';
|
jbe@289
|
829 COMMENT ON COLUMN "privilege"."initiative_right" IS 'Right to create an initiative';
|
jbe@289
|
830 COMMENT ON COLUMN "privilege"."voting_right" IS 'Right to support initiatives, create and rate suggestions, and to vote';
|
jbe@289
|
831 COMMENT ON COLUMN "privilege"."polling_right" IS 'Right to create issues with policies having the "policy"."polling" flag set, and to add initiatives having the "initiative"."polling" flag set to those issues';
|
jbe@97
|
832
|
jbe@97
|
833
|
jbe@0
|
834 CREATE TABLE "membership" (
|
jbe@0
|
835 PRIMARY KEY ("area_id", "member_id"),
|
jbe@0
|
836 "area_id" INT4 REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@169
|
837 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE );
|
jbe@0
|
838 CREATE INDEX "membership_member_id_idx" ON "membership" ("member_id");
|
jbe@0
|
839
|
jbe@0
|
840 COMMENT ON TABLE "membership" IS 'Interest of members in topic areas';
|
jbe@0
|
841
|
jbe@0
|
842
|
jbe@0
|
843 CREATE TABLE "interest" (
|
jbe@0
|
844 PRIMARY KEY ("issue_id", "member_id"),
|
jbe@0
|
845 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@148
|
846 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE );
|
jbe@0
|
847 CREATE INDEX "interest_member_id_idx" ON "interest" ("member_id");
|
jbe@0
|
848
|
jbe@10
|
849 COMMENT ON TABLE "interest" IS 'Interest of members in a particular issue; Frontends must ensure that interest for fully_frozen or closed issues is not added or removed.';
|
jbe@0
|
850
|
jbe@0
|
851
|
jbe@0
|
852 CREATE TABLE "initiator" (
|
jbe@0
|
853 PRIMARY KEY ("initiative_id", "member_id"),
|
jbe@0
|
854 "initiative_id" INT4 REFERENCES "initiative" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
855 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@14
|
856 "accepted" BOOLEAN );
|
jbe@0
|
857 CREATE INDEX "initiator_member_id_idx" ON "initiator" ("member_id");
|
jbe@0
|
858
|
jbe@10
|
859 COMMENT ON TABLE "initiator" IS 'Members who are allowed to post new drafts; Frontends must ensure that initiators are not added or removed from half_frozen, fully_frozen or closed initiatives.';
|
jbe@0
|
860
|
jbe@14
|
861 COMMENT ON COLUMN "initiator"."accepted" IS 'If "accepted" is NULL, then the member was invited to be a co-initiator, but has not answered yet. If it is TRUE, the member has accepted the invitation, if it is FALSE, the member has rejected the invitation.';
|
jbe@0
|
862
|
jbe@0
|
863
|
jbe@0
|
864 CREATE TABLE "supporter" (
|
jbe@0
|
865 "issue_id" INT4 NOT NULL,
|
jbe@0
|
866 PRIMARY KEY ("initiative_id", "member_id"),
|
jbe@0
|
867 "initiative_id" INT4,
|
jbe@0
|
868 "member_id" INT4,
|
jbe@0
|
869 "draft_id" INT8 NOT NULL,
|
jbe@10
|
870 FOREIGN KEY ("issue_id", "member_id") REFERENCES "interest" ("issue_id", "member_id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@160
|
871 FOREIGN KEY ("initiative_id", "draft_id") REFERENCES "draft" ("initiative_id", "id") ON DELETE NO ACTION ON UPDATE CASCADE );
|
jbe@0
|
872 CREATE INDEX "supporter_member_id_idx" ON "supporter" ("member_id");
|
jbe@0
|
873
|
jbe@10
|
874 COMMENT ON TABLE "supporter" IS 'Members who support an initiative (conditionally); Frontends must ensure that supporters are not added or removed from fully_frozen or closed initiatives.';
|
jbe@0
|
875
|
jbe@207
|
876 COMMENT ON COLUMN "supporter"."issue_id" IS 'WARNING: No index: For selections use column "initiative_id" and join via table "initiative" where neccessary';
|
jbe@160
|
877 COMMENT ON COLUMN "supporter"."draft_id" IS 'Latest seen draft; should always be set by a frontend, but defaults to current draft of the initiative (implemented by trigger "default_for_draft_id")';
|
jbe@84
|
878
|
jbe@0
|
879
|
jbe@0
|
880 CREATE TABLE "opinion" (
|
jbe@0
|
881 "initiative_id" INT4 NOT NULL,
|
jbe@0
|
882 PRIMARY KEY ("suggestion_id", "member_id"),
|
jbe@0
|
883 "suggestion_id" INT8,
|
jbe@0
|
884 "member_id" INT4,
|
jbe@0
|
885 "degree" INT2 NOT NULL CHECK ("degree" >= -2 AND "degree" <= 2 AND "degree" != 0),
|
jbe@0
|
886 "fulfilled" BOOLEAN NOT NULL DEFAULT FALSE,
|
jbe@42
|
887 FOREIGN KEY ("initiative_id", "suggestion_id") REFERENCES "suggestion" ("initiative_id", "id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
888 FOREIGN KEY ("initiative_id", "member_id") REFERENCES "supporter" ("initiative_id", "member_id") ON DELETE CASCADE ON UPDATE CASCADE );
|
jbe@10
|
889 CREATE INDEX "opinion_member_id_initiative_id_idx" ON "opinion" ("member_id", "initiative_id");
|
jbe@0
|
890
|
jbe@10
|
891 COMMENT ON TABLE "opinion" IS 'Opinion on suggestions (criticism related to initiatives); Frontends must ensure that opinions are not created modified or deleted when related to fully_frozen or closed issues.';
|
jbe@0
|
892
|
jbe@0
|
893 COMMENT ON COLUMN "opinion"."degree" IS '2 = fulfillment required for support; 1 = fulfillment desired; -1 = fulfillment unwanted; -2 = fulfillment cancels support';
|
jbe@0
|
894
|
jbe@0
|
895
|
jbe@97
|
896 CREATE TYPE "delegation_scope" AS ENUM ('unit', 'area', 'issue');
|
jbe@97
|
897
|
jbe@97
|
898 COMMENT ON TYPE "delegation_scope" IS 'Scope for delegations: ''unit'', ''area'', or ''issue'' (order is relevant)';
|
jbe@10
|
899
|
jbe@10
|
900
|
jbe@0
|
901 CREATE TABLE "delegation" (
|
jbe@0
|
902 "id" SERIAL8 PRIMARY KEY,
|
jbe@0
|
903 "truster_id" INT4 NOT NULL REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@86
|
904 "trustee_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@10
|
905 "scope" "delegation_scope" NOT NULL,
|
jbe@97
|
906 "unit_id" INT4 REFERENCES "unit" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
907 "area_id" INT4 REFERENCES "area" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
908 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
909 CONSTRAINT "cant_delegate_to_yourself" CHECK ("truster_id" != "trustee_id"),
|
jbe@97
|
910 CONSTRAINT "no_unit_delegation_to_null"
|
jbe@97
|
911 CHECK ("trustee_id" NOTNULL OR "scope" != 'unit'),
|
jbe@10
|
912 CONSTRAINT "area_id_and_issue_id_set_according_to_scope" CHECK (
|
jbe@97
|
913 ("scope" = 'unit' AND "unit_id" NOTNULL AND "area_id" ISNULL AND "issue_id" ISNULL ) OR
|
jbe@97
|
914 ("scope" = 'area' AND "unit_id" ISNULL AND "area_id" NOTNULL AND "issue_id" ISNULL ) OR
|
jbe@97
|
915 ("scope" = 'issue' AND "unit_id" ISNULL AND "area_id" ISNULL AND "issue_id" NOTNULL) ),
|
jbe@97
|
916 UNIQUE ("unit_id", "truster_id"),
|
jbe@74
|
917 UNIQUE ("area_id", "truster_id"),
|
jbe@74
|
918 UNIQUE ("issue_id", "truster_id") );
|
jbe@0
|
919 CREATE INDEX "delegation_truster_id_idx" ON "delegation" ("truster_id");
|
jbe@0
|
920 CREATE INDEX "delegation_trustee_id_idx" ON "delegation" ("trustee_id");
|
jbe@0
|
921
|
jbe@0
|
922 COMMENT ON TABLE "delegation" IS 'Delegation of vote-weight to other members';
|
jbe@0
|
923
|
jbe@97
|
924 COMMENT ON COLUMN "delegation"."unit_id" IS 'Reference to unit, if delegation is unit-wide, otherwise NULL';
|
jbe@0
|
925 COMMENT ON COLUMN "delegation"."area_id" IS 'Reference to area, if delegation is area-wide, otherwise NULL';
|
jbe@0
|
926 COMMENT ON COLUMN "delegation"."issue_id" IS 'Reference to issue, if delegation is issue-wide, otherwise NULL';
|
jbe@0
|
927
|
jbe@0
|
928
|
jbe@0
|
929 CREATE TABLE "direct_population_snapshot" (
|
jbe@0
|
930 PRIMARY KEY ("issue_id", "event", "member_id"),
|
jbe@0
|
931 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
932 "event" "snapshot_event",
|
jbe@45
|
933 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT,
|
jbe@54
|
934 "weight" INT4 );
|
jbe@0
|
935 CREATE INDEX "direct_population_snapshot_member_id_idx" ON "direct_population_snapshot" ("member_id");
|
jbe@0
|
936
|
jbe@0
|
937 COMMENT ON TABLE "direct_population_snapshot" IS 'Snapshot of active members having either a "membership" in the "area" or an "interest" in the "issue"';
|
jbe@0
|
938
|
jbe@148
|
939 COMMENT ON COLUMN "direct_population_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details';
|
jbe@148
|
940 COMMENT ON COLUMN "direct_population_snapshot"."weight" IS 'Weight of member (1 or higher) according to "delegating_population_snapshot"';
|
jbe@0
|
941
|
jbe@0
|
942
|
jbe@0
|
943 CREATE TABLE "delegating_population_snapshot" (
|
jbe@0
|
944 PRIMARY KEY ("issue_id", "event", "member_id"),
|
jbe@0
|
945 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
946 "event" "snapshot_event",
|
jbe@45
|
947 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT,
|
jbe@8
|
948 "weight" INT4,
|
jbe@10
|
949 "scope" "delegation_scope" NOT NULL,
|
jbe@0
|
950 "delegate_member_ids" INT4[] NOT NULL );
|
jbe@0
|
951 CREATE INDEX "delegating_population_snapshot_member_id_idx" ON "delegating_population_snapshot" ("member_id");
|
jbe@0
|
952
|
jbe@0
|
953 COMMENT ON TABLE "direct_population_snapshot" IS 'Delegations increasing the weight of entries in the "direct_population_snapshot" table';
|
jbe@0
|
954
|
jbe@0
|
955 COMMENT ON COLUMN "delegating_population_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details';
|
jbe@0
|
956 COMMENT ON COLUMN "delegating_population_snapshot"."member_id" IS 'Delegating member';
|
jbe@8
|
957 COMMENT ON COLUMN "delegating_population_snapshot"."weight" IS 'Intermediate weight';
|
jbe@0
|
958 COMMENT ON COLUMN "delegating_population_snapshot"."delegate_member_ids" IS 'Chain of members who act as delegates; last entry referes to "member_id" column of table "direct_population_snapshot"';
|
jbe@0
|
959
|
jbe@0
|
960
|
jbe@0
|
961 CREATE TABLE "direct_interest_snapshot" (
|
jbe@0
|
962 PRIMARY KEY ("issue_id", "event", "member_id"),
|
jbe@0
|
963 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
964 "event" "snapshot_event",
|
jbe@45
|
965 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT,
|
jbe@144
|
966 "weight" INT4 );
|
jbe@0
|
967 CREATE INDEX "direct_interest_snapshot_member_id_idx" ON "direct_interest_snapshot" ("member_id");
|
jbe@0
|
968
|
jbe@0
|
969 COMMENT ON TABLE "direct_interest_snapshot" IS 'Snapshot of active members having an "interest" in the "issue"';
|
jbe@0
|
970
|
jbe@0
|
971 COMMENT ON COLUMN "direct_interest_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details';
|
jbe@0
|
972 COMMENT ON COLUMN "direct_interest_snapshot"."weight" IS 'Weight of member (1 or higher) according to "delegating_interest_snapshot"';
|
jbe@0
|
973
|
jbe@0
|
974
|
jbe@0
|
975 CREATE TABLE "delegating_interest_snapshot" (
|
jbe@0
|
976 PRIMARY KEY ("issue_id", "event", "member_id"),
|
jbe@0
|
977 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
978 "event" "snapshot_event",
|
jbe@45
|
979 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT,
|
jbe@8
|
980 "weight" INT4,
|
jbe@10
|
981 "scope" "delegation_scope" NOT NULL,
|
jbe@0
|
982 "delegate_member_ids" INT4[] NOT NULL );
|
jbe@0
|
983 CREATE INDEX "delegating_interest_snapshot_member_id_idx" ON "delegating_interest_snapshot" ("member_id");
|
jbe@0
|
984
|
jbe@0
|
985 COMMENT ON TABLE "delegating_interest_snapshot" IS 'Delegations increasing the weight of entries in the "direct_interest_snapshot" table';
|
jbe@0
|
986
|
jbe@0
|
987 COMMENT ON COLUMN "delegating_interest_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details';
|
jbe@0
|
988 COMMENT ON COLUMN "delegating_interest_snapshot"."member_id" IS 'Delegating member';
|
jbe@8
|
989 COMMENT ON COLUMN "delegating_interest_snapshot"."weight" IS 'Intermediate weight';
|
jbe@0
|
990 COMMENT ON COLUMN "delegating_interest_snapshot"."delegate_member_ids" IS 'Chain of members who act as delegates; last entry referes to "member_id" column of table "direct_interest_snapshot"';
|
jbe@0
|
991
|
jbe@0
|
992
|
jbe@0
|
993 CREATE TABLE "direct_supporter_snapshot" (
|
jbe@0
|
994 "issue_id" INT4 NOT NULL,
|
jbe@0
|
995 PRIMARY KEY ("initiative_id", "event", "member_id"),
|
jbe@0
|
996 "initiative_id" INT4,
|
jbe@0
|
997 "event" "snapshot_event",
|
jbe@45
|
998 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT,
|
jbe@204
|
999 "draft_id" INT8 NOT NULL,
|
jbe@0
|
1000 "informed" BOOLEAN NOT NULL,
|
jbe@0
|
1001 "satisfied" BOOLEAN NOT NULL,
|
jbe@0
|
1002 FOREIGN KEY ("issue_id", "initiative_id") REFERENCES "initiative" ("issue_id", "id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@204
|
1003 FOREIGN KEY ("initiative_id", "draft_id") REFERENCES "draft" ("initiative_id", "id") ON DELETE NO ACTION ON UPDATE CASCADE,
|
jbe@0
|
1004 FOREIGN KEY ("issue_id", "event", "member_id") REFERENCES "direct_interest_snapshot" ("issue_id", "event", "member_id") ON DELETE CASCADE ON UPDATE CASCADE );
|
jbe@0
|
1005 CREATE INDEX "direct_supporter_snapshot_member_id_idx" ON "direct_supporter_snapshot" ("member_id");
|
jbe@0
|
1006
|
jbe@8
|
1007 COMMENT ON TABLE "direct_supporter_snapshot" IS 'Snapshot of supporters of initiatives (weight is stored in "direct_interest_snapshot")';
|
jbe@0
|
1008
|
jbe@207
|
1009 COMMENT ON COLUMN "direct_supporter_snapshot"."issue_id" IS 'WARNING: No index: For selections use column "initiative_id" and join via table "initiative" where neccessary';
|
jbe@0
|
1010 COMMENT ON COLUMN "direct_supporter_snapshot"."event" IS 'Reason for snapshot, see "snapshot_event" type for details';
|
jbe@0
|
1011 COMMENT ON COLUMN "direct_supporter_snapshot"."informed" IS 'Supporter has seen the latest draft of the initiative';
|
jbe@0
|
1012 COMMENT ON COLUMN "direct_supporter_snapshot"."satisfied" IS 'Supporter has no "critical_opinion"s';
|
jbe@0
|
1013
|
jbe@0
|
1014
|
jbe@113
|
1015 CREATE TABLE "non_voter" (
|
jbe@113
|
1016 PRIMARY KEY ("issue_id", "member_id"),
|
jbe@113
|
1017 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@113
|
1018 "member_id" INT4 REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE );
|
jbe@113
|
1019 CREATE INDEX "non_voter_member_id_idx" ON "non_voter" ("member_id");
|
jbe@113
|
1020
|
jbe@113
|
1021 COMMENT ON TABLE "non_voter" IS 'Members who decided to not vote directly on an issue';
|
jbe@113
|
1022
|
jbe@113
|
1023
|
jbe@0
|
1024 CREATE TABLE "direct_voter" (
|
jbe@0
|
1025 PRIMARY KEY ("issue_id", "member_id"),
|
jbe@0
|
1026 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@45
|
1027 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT,
|
jbe@285
|
1028 "weight" INT4,
|
jbe@285
|
1029 "comment_changed" TIMESTAMPTZ,
|
jbe@285
|
1030 "formatting_engine" TEXT,
|
jbe@285
|
1031 "comment" TEXT,
|
jbe@285
|
1032 "text_search_data" TSVECTOR );
|
jbe@0
|
1033 CREATE INDEX "direct_voter_member_id_idx" ON "direct_voter" ("member_id");
|
jbe@285
|
1034 CREATE INDEX "direct_voter_text_search_data_idx" ON "direct_voter" USING gin ("text_search_data");
|
jbe@285
|
1035 CREATE TRIGGER "update_text_search_data"
|
jbe@285
|
1036 BEFORE INSERT OR UPDATE ON "direct_voter"
|
jbe@285
|
1037 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@285
|
1038 tsvector_update_trigger('text_search_data', 'pg_catalog.simple', "comment");
|
jbe@0
|
1039
|
jbe@10
|
1040 COMMENT ON TABLE "direct_voter" IS 'Members having directly voted for/against initiatives of an issue; Frontends must ensure that no voters are added or removed to/from this table when the issue has been closed.';
|
jbe@0
|
1041
|
jbe@285
|
1042 COMMENT ON COLUMN "direct_voter"."weight" IS 'Weight of member (1 or higher) according to "delegating_voter" table';
|
jbe@285
|
1043 COMMENT ON COLUMN "direct_voter"."comment_changed" IS 'Shall be set on comment change, to indicate a comment being modified after voting has been finished; Automatically set to NULL after voting phase; Automatically set to NULL by trigger, if "comment" is set to NULL';
|
jbe@285
|
1044 COMMENT ON COLUMN "direct_voter"."formatting_engine" IS 'Allows different formatting engines (i.e. wiki formats) to be used for "direct_voter"."comment"; Automatically set to NULL by trigger, if "comment" is set to NULL';
|
jbe@285
|
1045 COMMENT ON COLUMN "direct_voter"."comment" IS 'Is to be set or updated by the frontend, if comment was inserted or updated AFTER the issue has been closed. Otherwise it shall be set to NULL.';
|
jbe@285
|
1046
|
jbe@285
|
1047
|
jbe@285
|
1048 CREATE TABLE "rendered_voter_comment" (
|
jbe@285
|
1049 PRIMARY KEY ("issue_id", "member_id", "format"),
|
jbe@285
|
1050 FOREIGN KEY ("issue_id", "member_id")
|
jbe@285
|
1051 REFERENCES "direct_voter" ("issue_id", "member_id")
|
jbe@285
|
1052 ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@285
|
1053 "issue_id" INT4,
|
jbe@285
|
1054 "member_id" INT4,
|
jbe@285
|
1055 "format" TEXT,
|
jbe@285
|
1056 "content" TEXT NOT NULL );
|
jbe@285
|
1057
|
jbe@285
|
1058 COMMENT ON TABLE "rendered_voter_comment" IS 'This table may be used by frontends to cache "rendered" voter comments (e.g. HTML output generated from wiki text)';
|
jbe@0
|
1059
|
jbe@0
|
1060
|
jbe@0
|
1061 CREATE TABLE "delegating_voter" (
|
jbe@0
|
1062 PRIMARY KEY ("issue_id", "member_id"),
|
jbe@0
|
1063 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@45
|
1064 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT,
|
jbe@8
|
1065 "weight" INT4,
|
jbe@10
|
1066 "scope" "delegation_scope" NOT NULL,
|
jbe@0
|
1067 "delegate_member_ids" INT4[] NOT NULL );
|
jbe@52
|
1068 CREATE INDEX "delegating_voter_member_id_idx" ON "delegating_voter" ("member_id");
|
jbe@0
|
1069
|
jbe@0
|
1070 COMMENT ON TABLE "delegating_voter" IS 'Delegations increasing the weight of entries in the "direct_voter" table';
|
jbe@0
|
1071
|
jbe@0
|
1072 COMMENT ON COLUMN "delegating_voter"."member_id" IS 'Delegating member';
|
jbe@8
|
1073 COMMENT ON COLUMN "delegating_voter"."weight" IS 'Intermediate weight';
|
jbe@0
|
1074 COMMENT ON COLUMN "delegating_voter"."delegate_member_ids" IS 'Chain of members who act as delegates; last entry referes to "member_id" column of table "direct_voter"';
|
jbe@0
|
1075
|
jbe@0
|
1076
|
jbe@0
|
1077 CREATE TABLE "vote" (
|
jbe@0
|
1078 "issue_id" INT4 NOT NULL,
|
jbe@0
|
1079 PRIMARY KEY ("initiative_id", "member_id"),
|
jbe@0
|
1080 "initiative_id" INT4,
|
jbe@0
|
1081 "member_id" INT4,
|
jbe@0
|
1082 "grade" INT4,
|
jbe@0
|
1083 FOREIGN KEY ("issue_id", "initiative_id") REFERENCES "initiative" ("issue_id", "id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@0
|
1084 FOREIGN KEY ("issue_id", "member_id") REFERENCES "direct_voter" ("issue_id", "member_id") ON DELETE CASCADE ON UPDATE CASCADE );
|
jbe@0
|
1085 CREATE INDEX "vote_member_id_idx" ON "vote" ("member_id");
|
jbe@0
|
1086
|
jbe@10
|
1087 COMMENT ON TABLE "vote" IS 'Manual and delegated votes without abstentions; Frontends must ensure that no votes are added modified or removed when the issue has been closed.';
|
jbe@0
|
1088
|
jbe@207
|
1089 COMMENT ON COLUMN "vote"."issue_id" IS 'WARNING: No index: For selections use column "initiative_id" and join via table "initiative" where neccessary';
|
jbe@207
|
1090 COMMENT ON COLUMN "vote"."grade" IS 'Values smaller than zero mean reject, values greater than zero mean acceptance, zero or missing row means abstention. Preferences are expressed by different positive or negative numbers.';
|
jbe@0
|
1091
|
jbe@0
|
1092
|
jbe@112
|
1093 CREATE TYPE "event_type" AS ENUM (
|
jbe@112
|
1094 'issue_state_changed',
|
jbe@112
|
1095 'initiative_created_in_new_issue',
|
jbe@112
|
1096 'initiative_created_in_existing_issue',
|
jbe@112
|
1097 'initiative_revoked',
|
jbe@112
|
1098 'new_draft_created',
|
jbe@112
|
1099 'suggestion_created');
|
jbe@112
|
1100
|
jbe@112
|
1101 COMMENT ON TYPE "event_type" IS 'Type used for column "event" of table "event"';
|
jbe@112
|
1102
|
jbe@112
|
1103
|
jbe@112
|
1104 CREATE TABLE "event" (
|
jbe@112
|
1105 "id" SERIAL8 PRIMARY KEY,
|
jbe@112
|
1106 "occurrence" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
jbe@112
|
1107 "event" "event_type" NOT NULL,
|
jbe@112
|
1108 "member_id" INT4 REFERENCES "member" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
jbe@112
|
1109 "issue_id" INT4 REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@112
|
1110 "state" "issue_state" CHECK ("state" != 'calculation'),
|
jbe@112
|
1111 "initiative_id" INT4,
|
jbe@112
|
1112 "draft_id" INT8,
|
jbe@112
|
1113 "suggestion_id" INT8,
|
jbe@112
|
1114 FOREIGN KEY ("issue_id", "initiative_id")
|
jbe@112
|
1115 REFERENCES "initiative" ("issue_id", "id")
|
jbe@112
|
1116 ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@112
|
1117 FOREIGN KEY ("initiative_id", "draft_id")
|
jbe@112
|
1118 REFERENCES "draft" ("initiative_id", "id")
|
jbe@112
|
1119 ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@112
|
1120 FOREIGN KEY ("initiative_id", "suggestion_id")
|
jbe@112
|
1121 REFERENCES "suggestion" ("initiative_id", "id")
|
jbe@112
|
1122 ON DELETE CASCADE ON UPDATE CASCADE,
|
jbe@112
|
1123 CONSTRAINT "null_constraints_for_issue_state_changed" CHECK (
|
jbe@112
|
1124 "event" != 'issue_state_changed' OR (
|
jbe@112
|
1125 "member_id" ISNULL AND
|
jbe@112
|
1126 "issue_id" NOTNULL AND
|
jbe@113
|
1127 "state" NOTNULL AND
|
jbe@112
|
1128 "initiative_id" ISNULL AND
|
jbe@112
|
1129 "draft_id" ISNULL AND
|
jbe@112
|
1130 "suggestion_id" ISNULL )),
|
jbe@112
|
1131 CONSTRAINT "null_constraints_for_initiative_creation_or_revocation_or_new_draft" CHECK (
|
jbe@112
|
1132 "event" NOT IN (
|
jbe@112
|
1133 'initiative_created_in_new_issue',
|
jbe@112
|
1134 'initiative_created_in_existing_issue',
|
jbe@112
|
1135 'initiative_revoked',
|
jbe@112
|
1136 'new_draft_created'
|
jbe@112
|
1137 ) OR (
|
jbe@112
|
1138 "member_id" NOTNULL AND
|
jbe@112
|
1139 "issue_id" NOTNULL AND
|
jbe@113
|
1140 "state" NOTNULL AND
|
jbe@112
|
1141 "initiative_id" NOTNULL AND
|
jbe@112
|
1142 "draft_id" NOTNULL AND
|
jbe@112
|
1143 "suggestion_id" ISNULL )),
|
jbe@112
|
1144 CONSTRAINT "null_constraints_for_suggestion_creation" CHECK (
|
jbe@112
|
1145 "event" != 'suggestion_created' OR (
|
jbe@112
|
1146 "member_id" NOTNULL AND
|
jbe@112
|
1147 "issue_id" NOTNULL AND
|
jbe@113
|
1148 "state" NOTNULL AND
|
jbe@112
|
1149 "initiative_id" NOTNULL AND
|
jbe@112
|
1150 "draft_id" ISNULL AND
|
jbe@112
|
1151 "suggestion_id" NOTNULL )) );
|
jbe@223
|
1152 CREATE INDEX "event_occurrence_idx" ON "event" ("occurrence");
|
jbe@112
|
1153
|
jbe@112
|
1154 COMMENT ON TABLE "event" IS 'Event table, automatically filled by triggers';
|
jbe@112
|
1155
|
jbe@114
|
1156 COMMENT ON COLUMN "event"."occurrence" IS 'Point in time, when event occurred';
|
jbe@114
|
1157 COMMENT ON COLUMN "event"."event" IS 'Type of event (see TYPE "event_type")';
|
jbe@114
|
1158 COMMENT ON COLUMN "event"."member_id" IS 'Member who caused the event, if applicable';
|
jbe@114
|
1159 COMMENT ON COLUMN "event"."state" IS 'If issue_id is set: state of affected issue; If state changed: new state';
|
jbe@114
|
1160
|
jbe@112
|
1161
|
jbe@222
|
1162 CREATE TABLE "notification_sent" (
|
jbe@222
|
1163 "event_id" INT8 NOT NULL );
|
jbe@222
|
1164 CREATE UNIQUE INDEX "notification_sent_singleton_idx" ON "notification_sent" ((1));
|
jbe@222
|
1165
|
jbe@222
|
1166 COMMENT ON TABLE "notification_sent" IS 'This table stores one row with the last event_id, for which notifications have been sent out';
|
jbe@222
|
1167 COMMENT ON INDEX "notification_sent_singleton_idx" IS 'This index ensures that "notification_sent" only contains one row maximum.';
|
jbe@222
|
1168
|
jbe@222
|
1169
|
jbe@112
|
1170
|
jbe@112
|
1171 ----------------------------------------------
|
jbe@112
|
1172 -- Writing of history entries and event log --
|
jbe@112
|
1173 ----------------------------------------------
|
jbe@13
|
1174
|
jbe@181
|
1175
|
jbe@13
|
1176 CREATE FUNCTION "write_member_history_trigger"()
|
jbe@13
|
1177 RETURNS TRIGGER
|
jbe@13
|
1178 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@13
|
1179 BEGIN
|
jbe@42
|
1180 IF
|
jbe@230
|
1181 ( NEW."active" != OLD."active" OR
|
jbe@230
|
1182 NEW."name" != OLD."name" ) AND
|
jbe@230
|
1183 OLD."activated" NOTNULL
|
jbe@42
|
1184 THEN
|
jbe@42
|
1185 INSERT INTO "member_history"
|
jbe@57
|
1186 ("member_id", "active", "name")
|
jbe@57
|
1187 VALUES (NEW."id", OLD."active", OLD."name");
|
jbe@13
|
1188 END IF;
|
jbe@13
|
1189 RETURN NULL;
|
jbe@13
|
1190 END;
|
jbe@13
|
1191 $$;
|
jbe@13
|
1192
|
jbe@13
|
1193 CREATE TRIGGER "write_member_history"
|
jbe@13
|
1194 AFTER UPDATE ON "member" FOR EACH ROW EXECUTE PROCEDURE
|
jbe@13
|
1195 "write_member_history_trigger"();
|
jbe@13
|
1196
|
jbe@13
|
1197 COMMENT ON FUNCTION "write_member_history_trigger"() IS 'Implementation of trigger "write_member_history" on table "member"';
|
jbe@57
|
1198 COMMENT ON TRIGGER "write_member_history" ON "member" IS 'When changing certain fields of a member, create a history entry in "member_history" table';
|
jbe@13
|
1199
|
jbe@13
|
1200
|
jbe@112
|
1201 CREATE FUNCTION "write_event_issue_state_changed_trigger"()
|
jbe@112
|
1202 RETURNS TRIGGER
|
jbe@112
|
1203 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@112
|
1204 BEGIN
|
jbe@112
|
1205 IF NEW."state" != OLD."state" AND NEW."state" != 'calculation' THEN
|
jbe@112
|
1206 INSERT INTO "event" ("event", "issue_id", "state")
|
jbe@112
|
1207 VALUES ('issue_state_changed', NEW."id", NEW."state");
|
jbe@112
|
1208 END IF;
|
jbe@112
|
1209 RETURN NULL;
|
jbe@112
|
1210 END;
|
jbe@112
|
1211 $$;
|
jbe@112
|
1212
|
jbe@112
|
1213 CREATE TRIGGER "write_event_issue_state_changed"
|
jbe@112
|
1214 AFTER UPDATE ON "issue" FOR EACH ROW EXECUTE PROCEDURE
|
jbe@112
|
1215 "write_event_issue_state_changed_trigger"();
|
jbe@112
|
1216
|
jbe@112
|
1217 COMMENT ON FUNCTION "write_event_issue_state_changed_trigger"() IS 'Implementation of trigger "write_event_issue_state_changed" on table "issue"';
|
jbe@112
|
1218 COMMENT ON TRIGGER "write_event_issue_state_changed" ON "issue" IS 'Create entry in "event" table on "state" change';
|
jbe@112
|
1219
|
jbe@112
|
1220
|
jbe@112
|
1221 CREATE FUNCTION "write_event_initiative_or_draft_created_trigger"()
|
jbe@112
|
1222 RETURNS TRIGGER
|
jbe@112
|
1223 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@112
|
1224 DECLARE
|
jbe@112
|
1225 "initiative_row" "initiative"%ROWTYPE;
|
jbe@113
|
1226 "issue_row" "issue"%ROWTYPE;
|
jbe@112
|
1227 "event_v" "event_type";
|
jbe@112
|
1228 BEGIN
|
jbe@112
|
1229 SELECT * INTO "initiative_row" FROM "initiative"
|
jbe@112
|
1230 WHERE "id" = NEW."initiative_id";
|
jbe@113
|
1231 SELECT * INTO "issue_row" FROM "issue"
|
jbe@113
|
1232 WHERE "id" = "initiative_row"."issue_id";
|
jbe@112
|
1233 IF EXISTS (
|
jbe@112
|
1234 SELECT NULL FROM "draft"
|
jbe@112
|
1235 WHERE "initiative_id" = NEW."initiative_id"
|
jbe@112
|
1236 AND "id" != NEW."id"
|
jbe@112
|
1237 ) THEN
|
jbe@112
|
1238 "event_v" := 'new_draft_created';
|
jbe@112
|
1239 ELSE
|
jbe@112
|
1240 IF EXISTS (
|
jbe@112
|
1241 SELECT NULL FROM "initiative"
|
jbe@112
|
1242 WHERE "issue_id" = "initiative_row"."issue_id"
|
jbe@112
|
1243 AND "id" != "initiative_row"."id"
|
jbe@112
|
1244 ) THEN
|
jbe@112
|
1245 "event_v" := 'initiative_created_in_existing_issue';
|
jbe@112
|
1246 ELSE
|
jbe@112
|
1247 "event_v" := 'initiative_created_in_new_issue';
|
jbe@112
|
1248 END IF;
|
jbe@112
|
1249 END IF;
|
jbe@112
|
1250 INSERT INTO "event" (
|
jbe@112
|
1251 "event", "member_id",
|
jbe@113
|
1252 "issue_id", "state", "initiative_id", "draft_id"
|
jbe@112
|
1253 ) VALUES (
|
jbe@112
|
1254 "event_v",
|
jbe@112
|
1255 NEW."author_id",
|
jbe@112
|
1256 "initiative_row"."issue_id",
|
jbe@113
|
1257 "issue_row"."state",
|
jbe@112
|
1258 "initiative_row"."id",
|
jbe@112
|
1259 NEW."id" );
|
jbe@112
|
1260 RETURN NULL;
|
jbe@112
|
1261 END;
|
jbe@112
|
1262 $$;
|
jbe@112
|
1263
|
jbe@112
|
1264 CREATE TRIGGER "write_event_initiative_or_draft_created"
|
jbe@112
|
1265 AFTER INSERT ON "draft" FOR EACH ROW EXECUTE PROCEDURE
|
jbe@112
|
1266 "write_event_initiative_or_draft_created_trigger"();
|
jbe@112
|
1267
|
jbe@112
|
1268 COMMENT ON FUNCTION "write_event_initiative_or_draft_created_trigger"() IS 'Implementation of trigger "write_event_initiative_or_draft_created" on table "issue"';
|
jbe@112
|
1269 COMMENT ON TRIGGER "write_event_initiative_or_draft_created" ON "draft" IS 'Create entry in "event" table on draft creation';
|
jbe@112
|
1270
|
jbe@112
|
1271
|
jbe@112
|
1272 CREATE FUNCTION "write_event_initiative_revoked_trigger"()
|
jbe@112
|
1273 RETURNS TRIGGER
|
jbe@112
|
1274 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@113
|
1275 DECLARE
|
jbe@231
|
1276 "issue_row" "issue"%ROWTYPE;
|
jbe@231
|
1277 "draft_id_v" "draft"."id"%TYPE;
|
jbe@112
|
1278 BEGIN
|
jbe@112
|
1279 IF OLD."revoked" ISNULL AND NEW."revoked" NOTNULL THEN
|
jbe@231
|
1280 SELECT * INTO "issue_row" FROM "issue"
|
jbe@231
|
1281 WHERE "id" = NEW."issue_id";
|
jbe@231
|
1282 SELECT "id" INTO "draft_id_v" FROM "current_draft"
|
jbe@231
|
1283 WHERE "initiative_id" = NEW."id";
|
jbe@112
|
1284 INSERT INTO "event" (
|
jbe@231
|
1285 "event", "member_id", "issue_id", "state", "initiative_id", "draft_id"
|
jbe@112
|
1286 ) VALUES (
|
jbe@112
|
1287 'initiative_revoked',
|
jbe@112
|
1288 NEW."revoked_by_member_id",
|
jbe@112
|
1289 NEW."issue_id",
|
jbe@113
|
1290 "issue_row"."state",
|
jbe@231
|
1291 NEW."id",
|
jbe@231
|
1292 "draft_id_v");
|
jbe@112
|
1293 END IF;
|
jbe@112
|
1294 RETURN NULL;
|
jbe@112
|
1295 END;
|
jbe@112
|
1296 $$;
|
jbe@112
|
1297
|
jbe@112
|
1298 CREATE TRIGGER "write_event_initiative_revoked"
|
jbe@112
|
1299 AFTER UPDATE ON "initiative" FOR EACH ROW EXECUTE PROCEDURE
|
jbe@112
|
1300 "write_event_initiative_revoked_trigger"();
|
jbe@112
|
1301
|
jbe@112
|
1302 COMMENT ON FUNCTION "write_event_initiative_revoked_trigger"() IS 'Implementation of trigger "write_event_initiative_revoked" on table "issue"';
|
jbe@112
|
1303 COMMENT ON TRIGGER "write_event_initiative_revoked" ON "initiative" IS 'Create entry in "event" table, when an initiative is revoked';
|
jbe@112
|
1304
|
jbe@112
|
1305
|
jbe@112
|
1306 CREATE FUNCTION "write_event_suggestion_created_trigger"()
|
jbe@112
|
1307 RETURNS TRIGGER
|
jbe@112
|
1308 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@112
|
1309 DECLARE
|
jbe@112
|
1310 "initiative_row" "initiative"%ROWTYPE;
|
jbe@113
|
1311 "issue_row" "issue"%ROWTYPE;
|
jbe@112
|
1312 BEGIN
|
jbe@112
|
1313 SELECT * INTO "initiative_row" FROM "initiative"
|
jbe@112
|
1314 WHERE "id" = NEW."initiative_id";
|
jbe@113
|
1315 SELECT * INTO "issue_row" FROM "issue"
|
jbe@113
|
1316 WHERE "id" = "initiative_row"."issue_id";
|
jbe@112
|
1317 INSERT INTO "event" (
|
jbe@112
|
1318 "event", "member_id",
|
jbe@113
|
1319 "issue_id", "state", "initiative_id", "suggestion_id"
|
jbe@112
|
1320 ) VALUES (
|
jbe@112
|
1321 'suggestion_created',
|
jbe@112
|
1322 NEW."author_id",
|
jbe@112
|
1323 "initiative_row"."issue_id",
|
jbe@113
|
1324 "issue_row"."state",
|
jbe@112
|
1325 "initiative_row"."id",
|
jbe@112
|
1326 NEW."id" );
|
jbe@112
|
1327 RETURN NULL;
|
jbe@112
|
1328 END;
|
jbe@112
|
1329 $$;
|
jbe@112
|
1330
|
jbe@112
|
1331 CREATE TRIGGER "write_event_suggestion_created"
|
jbe@112
|
1332 AFTER INSERT ON "suggestion" FOR EACH ROW EXECUTE PROCEDURE
|
jbe@112
|
1333 "write_event_suggestion_created_trigger"();
|
jbe@112
|
1334
|
jbe@112
|
1335 COMMENT ON FUNCTION "write_event_suggestion_created_trigger"() IS 'Implementation of trigger "write_event_suggestion_created" on table "issue"';
|
jbe@112
|
1336 COMMENT ON TRIGGER "write_event_suggestion_created" ON "suggestion" IS 'Create entry in "event" table on suggestion creation';
|
jbe@112
|
1337
|
jbe@112
|
1338
|
jbe@13
|
1339
|
jbe@0
|
1340 ----------------------------
|
jbe@0
|
1341 -- Additional constraints --
|
jbe@0
|
1342 ----------------------------
|
jbe@0
|
1343
|
jbe@0
|
1344
|
jbe@0
|
1345 CREATE FUNCTION "issue_requires_first_initiative_trigger"()
|
jbe@0
|
1346 RETURNS TRIGGER
|
jbe@0
|
1347 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1348 BEGIN
|
jbe@0
|
1349 IF NOT EXISTS (
|
jbe@0
|
1350 SELECT NULL FROM "initiative" WHERE "issue_id" = NEW."id"
|
jbe@0
|
1351 ) THEN
|
jbe@0
|
1352 --RAISE 'Cannot create issue without an initial initiative.' USING
|
jbe@0
|
1353 -- ERRCODE = 'integrity_constraint_violation',
|
jbe@0
|
1354 -- HINT = 'Create issue, initiative, and draft within the same transaction.';
|
jbe@0
|
1355 RAISE EXCEPTION 'Cannot create issue without an initial initiative.';
|
jbe@0
|
1356 END IF;
|
jbe@0
|
1357 RETURN NULL;
|
jbe@0
|
1358 END;
|
jbe@0
|
1359 $$;
|
jbe@0
|
1360
|
jbe@0
|
1361 CREATE CONSTRAINT TRIGGER "issue_requires_first_initiative"
|
jbe@0
|
1362 AFTER INSERT OR UPDATE ON "issue" DEFERRABLE INITIALLY DEFERRED
|
jbe@0
|
1363 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@0
|
1364 "issue_requires_first_initiative_trigger"();
|
jbe@0
|
1365
|
jbe@0
|
1366 COMMENT ON FUNCTION "issue_requires_first_initiative_trigger"() IS 'Implementation of trigger "issue_requires_first_initiative" on table "issue"';
|
jbe@0
|
1367 COMMENT ON TRIGGER "issue_requires_first_initiative" ON "issue" IS 'Ensure that new issues have at least one initiative';
|
jbe@0
|
1368
|
jbe@0
|
1369
|
jbe@0
|
1370 CREATE FUNCTION "last_initiative_deletes_issue_trigger"()
|
jbe@0
|
1371 RETURNS TRIGGER
|
jbe@0
|
1372 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1373 DECLARE
|
jbe@0
|
1374 "reference_lost" BOOLEAN;
|
jbe@0
|
1375 BEGIN
|
jbe@0
|
1376 IF TG_OP = 'DELETE' THEN
|
jbe@0
|
1377 "reference_lost" := TRUE;
|
jbe@0
|
1378 ELSE
|
jbe@0
|
1379 "reference_lost" := NEW."issue_id" != OLD."issue_id";
|
jbe@0
|
1380 END IF;
|
jbe@0
|
1381 IF
|
jbe@0
|
1382 "reference_lost" AND NOT EXISTS (
|
jbe@0
|
1383 SELECT NULL FROM "initiative" WHERE "issue_id" = OLD."issue_id"
|
jbe@0
|
1384 )
|
jbe@0
|
1385 THEN
|
jbe@0
|
1386 DELETE FROM "issue" WHERE "id" = OLD."issue_id";
|
jbe@0
|
1387 END IF;
|
jbe@0
|
1388 RETURN NULL;
|
jbe@0
|
1389 END;
|
jbe@0
|
1390 $$;
|
jbe@0
|
1391
|
jbe@0
|
1392 CREATE CONSTRAINT TRIGGER "last_initiative_deletes_issue"
|
jbe@0
|
1393 AFTER UPDATE OR DELETE ON "initiative" DEFERRABLE INITIALLY DEFERRED
|
jbe@0
|
1394 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@0
|
1395 "last_initiative_deletes_issue_trigger"();
|
jbe@0
|
1396
|
jbe@0
|
1397 COMMENT ON FUNCTION "last_initiative_deletes_issue_trigger"() IS 'Implementation of trigger "last_initiative_deletes_issue" on table "initiative"';
|
jbe@0
|
1398 COMMENT ON TRIGGER "last_initiative_deletes_issue" ON "initiative" IS 'Removing the last initiative of an issue deletes the issue';
|
jbe@0
|
1399
|
jbe@0
|
1400
|
jbe@0
|
1401 CREATE FUNCTION "initiative_requires_first_draft_trigger"()
|
jbe@0
|
1402 RETURNS TRIGGER
|
jbe@0
|
1403 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1404 BEGIN
|
jbe@0
|
1405 IF NOT EXISTS (
|
jbe@0
|
1406 SELECT NULL FROM "draft" WHERE "initiative_id" = NEW."id"
|
jbe@0
|
1407 ) THEN
|
jbe@0
|
1408 --RAISE 'Cannot create initiative without an initial draft.' USING
|
jbe@0
|
1409 -- ERRCODE = 'integrity_constraint_violation',
|
jbe@0
|
1410 -- HINT = 'Create issue, initiative and draft within the same transaction.';
|
jbe@0
|
1411 RAISE EXCEPTION 'Cannot create initiative without an initial draft.';
|
jbe@0
|
1412 END IF;
|
jbe@0
|
1413 RETURN NULL;
|
jbe@0
|
1414 END;
|
jbe@0
|
1415 $$;
|
jbe@0
|
1416
|
jbe@0
|
1417 CREATE CONSTRAINT TRIGGER "initiative_requires_first_draft"
|
jbe@0
|
1418 AFTER INSERT OR UPDATE ON "initiative" DEFERRABLE INITIALLY DEFERRED
|
jbe@0
|
1419 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@0
|
1420 "initiative_requires_first_draft_trigger"();
|
jbe@0
|
1421
|
jbe@0
|
1422 COMMENT ON FUNCTION "initiative_requires_first_draft_trigger"() IS 'Implementation of trigger "initiative_requires_first_draft" on table "initiative"';
|
jbe@0
|
1423 COMMENT ON TRIGGER "initiative_requires_first_draft" ON "initiative" IS 'Ensure that new initiatives have at least one draft';
|
jbe@0
|
1424
|
jbe@0
|
1425
|
jbe@0
|
1426 CREATE FUNCTION "last_draft_deletes_initiative_trigger"()
|
jbe@0
|
1427 RETURNS TRIGGER
|
jbe@0
|
1428 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1429 DECLARE
|
jbe@0
|
1430 "reference_lost" BOOLEAN;
|
jbe@0
|
1431 BEGIN
|
jbe@0
|
1432 IF TG_OP = 'DELETE' THEN
|
jbe@0
|
1433 "reference_lost" := TRUE;
|
jbe@0
|
1434 ELSE
|
jbe@0
|
1435 "reference_lost" := NEW."initiative_id" != OLD."initiative_id";
|
jbe@0
|
1436 END IF;
|
jbe@0
|
1437 IF
|
jbe@0
|
1438 "reference_lost" AND NOT EXISTS (
|
jbe@0
|
1439 SELECT NULL FROM "draft" WHERE "initiative_id" = OLD."initiative_id"
|
jbe@0
|
1440 )
|
jbe@0
|
1441 THEN
|
jbe@0
|
1442 DELETE FROM "initiative" WHERE "id" = OLD."initiative_id";
|
jbe@0
|
1443 END IF;
|
jbe@0
|
1444 RETURN NULL;
|
jbe@0
|
1445 END;
|
jbe@0
|
1446 $$;
|
jbe@0
|
1447
|
jbe@0
|
1448 CREATE CONSTRAINT TRIGGER "last_draft_deletes_initiative"
|
jbe@0
|
1449 AFTER UPDATE OR DELETE ON "draft" DEFERRABLE INITIALLY DEFERRED
|
jbe@0
|
1450 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@0
|
1451 "last_draft_deletes_initiative_trigger"();
|
jbe@0
|
1452
|
jbe@0
|
1453 COMMENT ON FUNCTION "last_draft_deletes_initiative_trigger"() IS 'Implementation of trigger "last_draft_deletes_initiative" on table "draft"';
|
jbe@0
|
1454 COMMENT ON TRIGGER "last_draft_deletes_initiative" ON "draft" IS 'Removing the last draft of an initiative deletes the initiative';
|
jbe@0
|
1455
|
jbe@0
|
1456
|
jbe@0
|
1457 CREATE FUNCTION "suggestion_requires_first_opinion_trigger"()
|
jbe@0
|
1458 RETURNS TRIGGER
|
jbe@0
|
1459 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1460 BEGIN
|
jbe@0
|
1461 IF NOT EXISTS (
|
jbe@0
|
1462 SELECT NULL FROM "opinion" WHERE "suggestion_id" = NEW."id"
|
jbe@0
|
1463 ) THEN
|
jbe@0
|
1464 RAISE EXCEPTION 'Cannot create a suggestion without an opinion.';
|
jbe@0
|
1465 END IF;
|
jbe@0
|
1466 RETURN NULL;
|
jbe@0
|
1467 END;
|
jbe@0
|
1468 $$;
|
jbe@0
|
1469
|
jbe@0
|
1470 CREATE CONSTRAINT TRIGGER "suggestion_requires_first_opinion"
|
jbe@0
|
1471 AFTER INSERT OR UPDATE ON "suggestion" DEFERRABLE INITIALLY DEFERRED
|
jbe@0
|
1472 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@0
|
1473 "suggestion_requires_first_opinion_trigger"();
|
jbe@0
|
1474
|
jbe@0
|
1475 COMMENT ON FUNCTION "suggestion_requires_first_opinion_trigger"() IS 'Implementation of trigger "suggestion_requires_first_opinion" on table "suggestion"';
|
jbe@0
|
1476 COMMENT ON TRIGGER "suggestion_requires_first_opinion" ON "suggestion" IS 'Ensure that new suggestions have at least one opinion';
|
jbe@0
|
1477
|
jbe@0
|
1478
|
jbe@0
|
1479 CREATE FUNCTION "last_opinion_deletes_suggestion_trigger"()
|
jbe@0
|
1480 RETURNS TRIGGER
|
jbe@0
|
1481 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1482 DECLARE
|
jbe@0
|
1483 "reference_lost" BOOLEAN;
|
jbe@0
|
1484 BEGIN
|
jbe@0
|
1485 IF TG_OP = 'DELETE' THEN
|
jbe@0
|
1486 "reference_lost" := TRUE;
|
jbe@0
|
1487 ELSE
|
jbe@0
|
1488 "reference_lost" := NEW."suggestion_id" != OLD."suggestion_id";
|
jbe@0
|
1489 END IF;
|
jbe@0
|
1490 IF
|
jbe@0
|
1491 "reference_lost" AND NOT EXISTS (
|
jbe@0
|
1492 SELECT NULL FROM "opinion" WHERE "suggestion_id" = OLD."suggestion_id"
|
jbe@0
|
1493 )
|
jbe@0
|
1494 THEN
|
jbe@0
|
1495 DELETE FROM "suggestion" WHERE "id" = OLD."suggestion_id";
|
jbe@0
|
1496 END IF;
|
jbe@0
|
1497 RETURN NULL;
|
jbe@0
|
1498 END;
|
jbe@0
|
1499 $$;
|
jbe@0
|
1500
|
jbe@0
|
1501 CREATE CONSTRAINT TRIGGER "last_opinion_deletes_suggestion"
|
jbe@0
|
1502 AFTER UPDATE OR DELETE ON "opinion" DEFERRABLE INITIALLY DEFERRED
|
jbe@0
|
1503 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@0
|
1504 "last_opinion_deletes_suggestion_trigger"();
|
jbe@0
|
1505
|
jbe@0
|
1506 COMMENT ON FUNCTION "last_opinion_deletes_suggestion_trigger"() IS 'Implementation of trigger "last_opinion_deletes_suggestion" on table "opinion"';
|
jbe@0
|
1507 COMMENT ON TRIGGER "last_opinion_deletes_suggestion" ON "opinion" IS 'Removing the last opinion of a suggestion deletes the suggestion';
|
jbe@0
|
1508
|
jbe@0
|
1509
|
jbe@284
|
1510 CREATE FUNCTION "non_voter_deletes_direct_voter_trigger"()
|
jbe@284
|
1511 RETURNS TRIGGER
|
jbe@284
|
1512 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@284
|
1513 BEGIN
|
jbe@284
|
1514 DELETE FROM "direct_voter"
|
jbe@284
|
1515 WHERE "issue_id" = NEW."issue_id" AND "member_id" = NEW."member_id";
|
jbe@284
|
1516 RETURN NULL;
|
jbe@284
|
1517 END;
|
jbe@284
|
1518 $$;
|
jbe@284
|
1519
|
jbe@284
|
1520 CREATE TRIGGER "non_voter_deletes_direct_voter"
|
jbe@284
|
1521 AFTER INSERT OR UPDATE ON "non_voter"
|
jbe@284
|
1522 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@284
|
1523 "non_voter_deletes_direct_voter_trigger"();
|
jbe@284
|
1524
|
jbe@284
|
1525 COMMENT ON FUNCTION "non_voter_deletes_direct_voter_trigger"() IS 'Implementation of trigger "non_voter_deletes_direct_voter" on table "non_voter"';
|
jbe@284
|
1526 COMMENT ON TRIGGER "non_voter_deletes_direct_voter" ON "non_voter" IS 'An entry in the "non_voter" table deletes an entry in the "direct_voter" table (and vice versa due to trigger "direct_voter_deletes_non_voter" on table "direct_voter")';
|
jbe@284
|
1527
|
jbe@284
|
1528
|
jbe@284
|
1529 CREATE FUNCTION "direct_voter_deletes_non_voter_trigger"()
|
jbe@284
|
1530 RETURNS TRIGGER
|
jbe@284
|
1531 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@284
|
1532 BEGIN
|
jbe@284
|
1533 DELETE FROM "non_voter"
|
jbe@284
|
1534 WHERE "issue_id" = NEW."issue_id" AND "member_id" = NEW."member_id";
|
jbe@284
|
1535 RETURN NULL;
|
jbe@284
|
1536 END;
|
jbe@284
|
1537 $$;
|
jbe@284
|
1538
|
jbe@284
|
1539 CREATE TRIGGER "direct_voter_deletes_non_voter"
|
jbe@284
|
1540 AFTER INSERT OR UPDATE ON "direct_voter"
|
jbe@284
|
1541 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@284
|
1542 "direct_voter_deletes_non_voter_trigger"();
|
jbe@284
|
1543
|
jbe@284
|
1544 COMMENT ON FUNCTION "direct_voter_deletes_non_voter_trigger"() IS 'Implementation of trigger "direct_voter_deletes_non_voter" on table "direct_voter"';
|
jbe@284
|
1545 COMMENT ON TRIGGER "direct_voter_deletes_non_voter" ON "direct_voter" IS 'An entry in the "direct_voter" table deletes an entry in the "non_voter" table (and vice versa due to trigger "non_voter_deletes_direct_voter" on table "non_voter")';
|
jbe@284
|
1546
|
jbe@284
|
1547
|
jbe@285
|
1548 CREATE FUNCTION "voter_comment_fields_only_set_when_voter_comment_is_set_trigger"()
|
jbe@285
|
1549 RETURNS TRIGGER
|
jbe@285
|
1550 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@285
|
1551 BEGIN
|
jbe@285
|
1552 IF NEW."comment" ISNULL THEN
|
jbe@285
|
1553 NEW."comment_changed" := NULL;
|
jbe@285
|
1554 NEW."formatting_engine" := NULL;
|
jbe@285
|
1555 END IF;
|
jbe@285
|
1556 RETURN NEW;
|
jbe@285
|
1557 END;
|
jbe@285
|
1558 $$;
|
jbe@285
|
1559
|
jbe@285
|
1560 CREATE TRIGGER "voter_comment_fields_only_set_when_voter_comment_is_set"
|
jbe@285
|
1561 BEFORE INSERT OR UPDATE ON "direct_voter"
|
jbe@285
|
1562 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@285
|
1563 "voter_comment_fields_only_set_when_voter_comment_is_set_trigger"();
|
jbe@285
|
1564
|
jbe@285
|
1565 COMMENT ON FUNCTION "voter_comment_fields_only_set_when_voter_comment_is_set_trigger"() IS 'Implementation of trigger "voter_comment_fields_only_set_when_voter_comment_is_set" ON table "direct_voter"';
|
jbe@285
|
1566 COMMENT ON TRIGGER "voter_comment_fields_only_set_when_voter_comment_is_set" ON "direct_voter" IS 'If "comment" is set to NULL, then other comment related fields are also set to NULL.';
|
jbe@285
|
1567
|
jbe@0
|
1568
|
jbe@20
|
1569 ---------------------------------------------------------------
|
jbe@20
|
1570 -- Ensure that votes are not modified when issues are frozen --
|
jbe@20
|
1571 ---------------------------------------------------------------
|
jbe@20
|
1572
|
jbe@20
|
1573 -- NOTE: Frontends should ensure this anyway, but in case of programming
|
jbe@20
|
1574 -- errors the following triggers ensure data integrity.
|
jbe@20
|
1575
|
jbe@20
|
1576
|
jbe@20
|
1577 CREATE FUNCTION "forbid_changes_on_closed_issue_trigger"()
|
jbe@20
|
1578 RETURNS TRIGGER
|
jbe@20
|
1579 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@20
|
1580 DECLARE
|
jbe@32
|
1581 "issue_id_v" "issue"."id"%TYPE;
|
jbe@32
|
1582 "issue_row" "issue"%ROWTYPE;
|
jbe@20
|
1583 BEGIN
|
jbe@290
|
1584 IF TG_RELID = 'direct_voter'::regclass AND TG_OP = 'UPDATE' THEN
|
jbe@290
|
1585 IF
|
jbe@290
|
1586 OLD."issue_id" = NEW."issue_id" AND
|
jbe@290
|
1587 OLD."member_id" = NEW."member_id" AND
|
jbe@290
|
1588 OLD."weight" = NEW."weight"
|
jbe@290
|
1589 THEN
|
jbe@290
|
1590 RETURN NULL; -- allows changing of voter comment
|
jbe@290
|
1591 END IF;
|
jbe@290
|
1592 END IF;
|
jbe@32
|
1593 IF TG_OP = 'DELETE' THEN
|
jbe@32
|
1594 "issue_id_v" := OLD."issue_id";
|
jbe@32
|
1595 ELSE
|
jbe@32
|
1596 "issue_id_v" := NEW."issue_id";
|
jbe@32
|
1597 END IF;
|
jbe@20
|
1598 SELECT INTO "issue_row" * FROM "issue"
|
jbe@32
|
1599 WHERE "id" = "issue_id_v" FOR SHARE;
|
jbe@20
|
1600 IF "issue_row"."closed" NOTNULL THEN
|
jbe@20
|
1601 RAISE EXCEPTION 'Tried to modify data belonging to a closed issue.';
|
jbe@20
|
1602 END IF;
|
jbe@20
|
1603 RETURN NULL;
|
jbe@20
|
1604 END;
|
jbe@20
|
1605 $$;
|
jbe@20
|
1606
|
jbe@20
|
1607 CREATE TRIGGER "forbid_changes_on_closed_issue"
|
jbe@20
|
1608 AFTER INSERT OR UPDATE OR DELETE ON "direct_voter"
|
jbe@20
|
1609 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@20
|
1610 "forbid_changes_on_closed_issue_trigger"();
|
jbe@20
|
1611
|
jbe@20
|
1612 CREATE TRIGGER "forbid_changes_on_closed_issue"
|
jbe@20
|
1613 AFTER INSERT OR UPDATE OR DELETE ON "delegating_voter"
|
jbe@20
|
1614 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@20
|
1615 "forbid_changes_on_closed_issue_trigger"();
|
jbe@20
|
1616
|
jbe@20
|
1617 CREATE TRIGGER "forbid_changes_on_closed_issue"
|
jbe@20
|
1618 AFTER INSERT OR UPDATE OR DELETE ON "vote"
|
jbe@20
|
1619 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@20
|
1620 "forbid_changes_on_closed_issue_trigger"();
|
jbe@20
|
1621
|
jbe@20
|
1622 COMMENT ON FUNCTION "forbid_changes_on_closed_issue_trigger"() IS 'Implementation of triggers "forbid_changes_on_closed_issue" on tables "direct_voter", "delegating_voter" and "vote"';
|
jbe@20
|
1623 COMMENT ON TRIGGER "forbid_changes_on_closed_issue" ON "direct_voter" IS 'Ensures that frontends can''t tamper with votings of closed issues, in case of programming errors';
|
jbe@20
|
1624 COMMENT ON TRIGGER "forbid_changes_on_closed_issue" ON "delegating_voter" IS 'Ensures that frontends can''t tamper with votings of closed issues, in case of programming errors';
|
jbe@20
|
1625 COMMENT ON TRIGGER "forbid_changes_on_closed_issue" ON "vote" IS 'Ensures that frontends can''t tamper with votings of closed issues, in case of programming errors';
|
jbe@20
|
1626
|
jbe@20
|
1627
|
jbe@20
|
1628
|
jbe@0
|
1629 --------------------------------------------------------------------
|
jbe@0
|
1630 -- Auto-retrieval of fields only needed for referential integrity --
|
jbe@0
|
1631 --------------------------------------------------------------------
|
jbe@0
|
1632
|
jbe@20
|
1633
|
jbe@0
|
1634 CREATE FUNCTION "autofill_issue_id_trigger"()
|
jbe@0
|
1635 RETURNS TRIGGER
|
jbe@0
|
1636 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1637 BEGIN
|
jbe@0
|
1638 IF NEW."issue_id" ISNULL THEN
|
jbe@0
|
1639 SELECT "issue_id" INTO NEW."issue_id"
|
jbe@0
|
1640 FROM "initiative" WHERE "id" = NEW."initiative_id";
|
jbe@0
|
1641 END IF;
|
jbe@0
|
1642 RETURN NEW;
|
jbe@0
|
1643 END;
|
jbe@0
|
1644 $$;
|
jbe@0
|
1645
|
jbe@0
|
1646 CREATE TRIGGER "autofill_issue_id" BEFORE INSERT ON "supporter"
|
jbe@0
|
1647 FOR EACH ROW EXECUTE PROCEDURE "autofill_issue_id_trigger"();
|
jbe@0
|
1648
|
jbe@0
|
1649 CREATE TRIGGER "autofill_issue_id" BEFORE INSERT ON "vote"
|
jbe@0
|
1650 FOR EACH ROW EXECUTE PROCEDURE "autofill_issue_id_trigger"();
|
jbe@0
|
1651
|
jbe@0
|
1652 COMMENT ON FUNCTION "autofill_issue_id_trigger"() IS 'Implementation of triggers "autofill_issue_id" on tables "supporter" and "vote"';
|
jbe@0
|
1653 COMMENT ON TRIGGER "autofill_issue_id" ON "supporter" IS 'Set "issue_id" field automatically, if NULL';
|
jbe@0
|
1654 COMMENT ON TRIGGER "autofill_issue_id" ON "vote" IS 'Set "issue_id" field automatically, if NULL';
|
jbe@0
|
1655
|
jbe@0
|
1656
|
jbe@0
|
1657 CREATE FUNCTION "autofill_initiative_id_trigger"()
|
jbe@0
|
1658 RETURNS TRIGGER
|
jbe@0
|
1659 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1660 BEGIN
|
jbe@0
|
1661 IF NEW."initiative_id" ISNULL THEN
|
jbe@0
|
1662 SELECT "initiative_id" INTO NEW."initiative_id"
|
jbe@0
|
1663 FROM "suggestion" WHERE "id" = NEW."suggestion_id";
|
jbe@0
|
1664 END IF;
|
jbe@0
|
1665 RETURN NEW;
|
jbe@0
|
1666 END;
|
jbe@0
|
1667 $$;
|
jbe@0
|
1668
|
jbe@0
|
1669 CREATE TRIGGER "autofill_initiative_id" BEFORE INSERT ON "opinion"
|
jbe@0
|
1670 FOR EACH ROW EXECUTE PROCEDURE "autofill_initiative_id_trigger"();
|
jbe@0
|
1671
|
jbe@0
|
1672 COMMENT ON FUNCTION "autofill_initiative_id_trigger"() IS 'Implementation of trigger "autofill_initiative_id" on table "opinion"';
|
jbe@0
|
1673 COMMENT ON TRIGGER "autofill_initiative_id" ON "opinion" IS 'Set "initiative_id" field automatically, if NULL';
|
jbe@0
|
1674
|
jbe@0
|
1675
|
jbe@0
|
1676
|
jbe@4
|
1677 -----------------------------------------------------
|
jbe@4
|
1678 -- Automatic calculation of certain default values --
|
jbe@4
|
1679 -----------------------------------------------------
|
jbe@0
|
1680
|
jbe@22
|
1681
|
jbe@22
|
1682 CREATE FUNCTION "copy_timings_trigger"()
|
jbe@22
|
1683 RETURNS TRIGGER
|
jbe@22
|
1684 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@22
|
1685 DECLARE
|
jbe@22
|
1686 "policy_row" "policy"%ROWTYPE;
|
jbe@22
|
1687 BEGIN
|
jbe@22
|
1688 SELECT * INTO "policy_row" FROM "policy"
|
jbe@22
|
1689 WHERE "id" = NEW."policy_id";
|
jbe@22
|
1690 IF NEW."admission_time" ISNULL THEN
|
jbe@22
|
1691 NEW."admission_time" := "policy_row"."admission_time";
|
jbe@22
|
1692 END IF;
|
jbe@22
|
1693 IF NEW."discussion_time" ISNULL THEN
|
jbe@22
|
1694 NEW."discussion_time" := "policy_row"."discussion_time";
|
jbe@22
|
1695 END IF;
|
jbe@22
|
1696 IF NEW."verification_time" ISNULL THEN
|
jbe@22
|
1697 NEW."verification_time" := "policy_row"."verification_time";
|
jbe@22
|
1698 END IF;
|
jbe@22
|
1699 IF NEW."voting_time" ISNULL THEN
|
jbe@22
|
1700 NEW."voting_time" := "policy_row"."voting_time";
|
jbe@22
|
1701 END IF;
|
jbe@22
|
1702 RETURN NEW;
|
jbe@22
|
1703 END;
|
jbe@22
|
1704 $$;
|
jbe@22
|
1705
|
jbe@22
|
1706 CREATE TRIGGER "copy_timings" BEFORE INSERT OR UPDATE ON "issue"
|
jbe@22
|
1707 FOR EACH ROW EXECUTE PROCEDURE "copy_timings_trigger"();
|
jbe@22
|
1708
|
jbe@22
|
1709 COMMENT ON FUNCTION "copy_timings_trigger"() IS 'Implementation of trigger "copy_timings" on table "issue"';
|
jbe@22
|
1710 COMMENT ON TRIGGER "copy_timings" ON "issue" IS 'If timing fields are NULL, copy values from policy.';
|
jbe@22
|
1711
|
jbe@22
|
1712
|
jbe@160
|
1713 CREATE FUNCTION "default_for_draft_id_trigger"()
|
jbe@2
|
1714 RETURNS TRIGGER
|
jbe@2
|
1715 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@2
|
1716 BEGIN
|
jbe@2
|
1717 IF NEW."draft_id" ISNULL THEN
|
jbe@2
|
1718 SELECT "id" INTO NEW."draft_id" FROM "current_draft"
|
jbe@2
|
1719 WHERE "initiative_id" = NEW."initiative_id";
|
jbe@2
|
1720 END IF;
|
jbe@2
|
1721 RETURN NEW;
|
jbe@2
|
1722 END;
|
jbe@2
|
1723 $$;
|
jbe@2
|
1724
|
jbe@160
|
1725 CREATE TRIGGER "default_for_draft_id" BEFORE INSERT OR UPDATE ON "suggestion"
|
jbe@160
|
1726 FOR EACH ROW EXECUTE PROCEDURE "default_for_draft_id_trigger"();
|
jbe@2
|
1727 CREATE TRIGGER "default_for_draft_id" BEFORE INSERT OR UPDATE ON "supporter"
|
jbe@160
|
1728 FOR EACH ROW EXECUTE PROCEDURE "default_for_draft_id_trigger"();
|
jbe@160
|
1729
|
jbe@160
|
1730 COMMENT ON FUNCTION "default_for_draft_id_trigger"() IS 'Implementation of trigger "default_for_draft" on tables "supporter" and "suggestion"';
|
jbe@160
|
1731 COMMENT ON TRIGGER "default_for_draft_id" ON "suggestion" IS 'If "draft_id" is NULL, then use the current draft of the initiative as default';
|
jbe@160
|
1732 COMMENT ON TRIGGER "default_for_draft_id" ON "supporter" IS 'If "draft_id" is NULL, then use the current draft of the initiative as default';
|
jbe@2
|
1733
|
jbe@2
|
1734
|
jbe@0
|
1735
|
jbe@0
|
1736 ----------------------------------------
|
jbe@0
|
1737 -- Automatic creation of dependencies --
|
jbe@0
|
1738 ----------------------------------------
|
jbe@0
|
1739
|
jbe@22
|
1740
|
jbe@0
|
1741 CREATE FUNCTION "autocreate_interest_trigger"()
|
jbe@0
|
1742 RETURNS TRIGGER
|
jbe@0
|
1743 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1744 BEGIN
|
jbe@0
|
1745 IF NOT EXISTS (
|
jbe@0
|
1746 SELECT NULL FROM "initiative" JOIN "interest"
|
jbe@0
|
1747 ON "initiative"."issue_id" = "interest"."issue_id"
|
jbe@0
|
1748 WHERE "initiative"."id" = NEW."initiative_id"
|
jbe@0
|
1749 AND "interest"."member_id" = NEW."member_id"
|
jbe@0
|
1750 ) THEN
|
jbe@0
|
1751 BEGIN
|
jbe@0
|
1752 INSERT INTO "interest" ("issue_id", "member_id")
|
jbe@0
|
1753 SELECT "issue_id", NEW."member_id"
|
jbe@0
|
1754 FROM "initiative" WHERE "id" = NEW."initiative_id";
|
jbe@0
|
1755 EXCEPTION WHEN unique_violation THEN END;
|
jbe@0
|
1756 END IF;
|
jbe@0
|
1757 RETURN NEW;
|
jbe@0
|
1758 END;
|
jbe@0
|
1759 $$;
|
jbe@0
|
1760
|
jbe@0
|
1761 CREATE TRIGGER "autocreate_interest" BEFORE INSERT ON "supporter"
|
jbe@0
|
1762 FOR EACH ROW EXECUTE PROCEDURE "autocreate_interest_trigger"();
|
jbe@0
|
1763
|
jbe@0
|
1764 COMMENT ON FUNCTION "autocreate_interest_trigger"() IS 'Implementation of trigger "autocreate_interest" on table "supporter"';
|
jbe@0
|
1765 COMMENT ON TRIGGER "autocreate_interest" ON "supporter" IS 'Supporting an initiative implies interest in the issue, thus automatically creates an entry in the "interest" table';
|
jbe@0
|
1766
|
jbe@0
|
1767
|
jbe@0
|
1768 CREATE FUNCTION "autocreate_supporter_trigger"()
|
jbe@0
|
1769 RETURNS TRIGGER
|
jbe@0
|
1770 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
1771 BEGIN
|
jbe@0
|
1772 IF NOT EXISTS (
|
jbe@0
|
1773 SELECT NULL FROM "suggestion" JOIN "supporter"
|
jbe@0
|
1774 ON "suggestion"."initiative_id" = "supporter"."initiative_id"
|
jbe@0
|
1775 WHERE "suggestion"."id" = NEW."suggestion_id"
|
jbe@0
|
1776 AND "supporter"."member_id" = NEW."member_id"
|
jbe@0
|
1777 ) THEN
|
jbe@0
|
1778 BEGIN
|
jbe@0
|
1779 INSERT INTO "supporter" ("initiative_id", "member_id")
|
jbe@0
|
1780 SELECT "initiative_id", NEW."member_id"
|
jbe@0
|
1781 FROM "suggestion" WHERE "id" = NEW."suggestion_id";
|
jbe@0
|
1782 EXCEPTION WHEN unique_violation THEN END;
|
jbe@0
|
1783 END IF;
|
jbe@0
|
1784 RETURN NEW;
|
jbe@0
|
1785 END;
|
jbe@0
|
1786 $$;
|
jbe@0
|
1787
|
jbe@0
|
1788 CREATE TRIGGER "autocreate_supporter" BEFORE INSERT ON "opinion"
|
jbe@0
|
1789 FOR EACH ROW EXECUTE PROCEDURE "autocreate_supporter_trigger"();
|
jbe@0
|
1790
|
jbe@0
|
1791 COMMENT ON FUNCTION "autocreate_supporter_trigger"() IS 'Implementation of trigger "autocreate_supporter" on table "opinion"';
|
jbe@0
|
1792 COMMENT ON TRIGGER "autocreate_supporter" ON "opinion" IS 'Opinions can only be added for supported initiatives. This trigger automatrically creates an entry in the "supporter" table, if not existent yet.';
|
jbe@0
|
1793
|
jbe@0
|
1794
|
jbe@0
|
1795
|
jbe@0
|
1796 ------------------------------------------
|
jbe@0
|
1797 -- Views and helper functions for views --
|
jbe@0
|
1798 ------------------------------------------
|
jbe@0
|
1799
|
jbe@5
|
1800
|
jbe@97
|
1801 CREATE VIEW "unit_delegation" AS
|
jbe@97
|
1802 SELECT
|
jbe@97
|
1803 "unit"."id" AS "unit_id",
|
jbe@97
|
1804 "delegation"."id",
|
jbe@97
|
1805 "delegation"."truster_id",
|
jbe@97
|
1806 "delegation"."trustee_id",
|
jbe@97
|
1807 "delegation"."scope"
|
jbe@97
|
1808 FROM "unit"
|
jbe@97
|
1809 JOIN "delegation"
|
jbe@97
|
1810 ON "delegation"."unit_id" = "unit"."id"
|
jbe@97
|
1811 JOIN "member"
|
jbe@97
|
1812 ON "delegation"."truster_id" = "member"."id"
|
jbe@97
|
1813 JOIN "privilege"
|
jbe@97
|
1814 ON "delegation"."unit_id" = "privilege"."unit_id"
|
jbe@97
|
1815 AND "delegation"."truster_id" = "privilege"."member_id"
|
jbe@97
|
1816 WHERE "member"."active" AND "privilege"."voting_right";
|
jbe@97
|
1817
|
jbe@97
|
1818 COMMENT ON VIEW "unit_delegation" IS 'Unit delegations where trusters are active and have voting right';
|
jbe@5
|
1819
|
jbe@5
|
1820
|
jbe@5
|
1821 CREATE VIEW "area_delegation" AS
|
jbe@70
|
1822 SELECT DISTINCT ON ("area"."id", "delegation"."truster_id")
|
jbe@70
|
1823 "area"."id" AS "area_id",
|
jbe@70
|
1824 "delegation"."id",
|
jbe@70
|
1825 "delegation"."truster_id",
|
jbe@70
|
1826 "delegation"."trustee_id",
|
jbe@70
|
1827 "delegation"."scope"
|
jbe@97
|
1828 FROM "area"
|
jbe@97
|
1829 JOIN "delegation"
|
jbe@97
|
1830 ON "delegation"."unit_id" = "area"."unit_id"
|
jbe@97
|
1831 OR "delegation"."area_id" = "area"."id"
|
jbe@97
|
1832 JOIN "member"
|
jbe@97
|
1833 ON "delegation"."truster_id" = "member"."id"
|
jbe@97
|
1834 JOIN "privilege"
|
jbe@97
|
1835 ON "area"."unit_id" = "privilege"."unit_id"
|
jbe@97
|
1836 AND "delegation"."truster_id" = "privilege"."member_id"
|
jbe@97
|
1837 WHERE "member"."active" AND "privilege"."voting_right"
|
jbe@70
|
1838 ORDER BY
|
jbe@70
|
1839 "area"."id",
|
jbe@70
|
1840 "delegation"."truster_id",
|
jbe@70
|
1841 "delegation"."scope" DESC;
|
jbe@70
|
1842
|
jbe@97
|
1843 COMMENT ON VIEW "area_delegation" IS 'Area delegations where trusters are active and have voting right';
|
jbe@5
|
1844
|
jbe@5
|
1845
|
jbe@5
|
1846 CREATE VIEW "issue_delegation" AS
|
jbe@70
|
1847 SELECT DISTINCT ON ("issue"."id", "delegation"."truster_id")
|
jbe@70
|
1848 "issue"."id" AS "issue_id",
|
jbe@70
|
1849 "delegation"."id",
|
jbe@70
|
1850 "delegation"."truster_id",
|
jbe@70
|
1851 "delegation"."trustee_id",
|
jbe@70
|
1852 "delegation"."scope"
|
jbe@97
|
1853 FROM "issue"
|
jbe@97
|
1854 JOIN "area"
|
jbe@97
|
1855 ON "area"."id" = "issue"."area_id"
|
jbe@97
|
1856 JOIN "delegation"
|
jbe@97
|
1857 ON "delegation"."unit_id" = "area"."unit_id"
|
jbe@97
|
1858 OR "delegation"."area_id" = "area"."id"
|
jbe@97
|
1859 OR "delegation"."issue_id" = "issue"."id"
|
jbe@97
|
1860 JOIN "member"
|
jbe@97
|
1861 ON "delegation"."truster_id" = "member"."id"
|
jbe@97
|
1862 JOIN "privilege"
|
jbe@97
|
1863 ON "area"."unit_id" = "privilege"."unit_id"
|
jbe@97
|
1864 AND "delegation"."truster_id" = "privilege"."member_id"
|
jbe@97
|
1865 WHERE "member"."active" AND "privilege"."voting_right"
|
jbe@70
|
1866 ORDER BY
|
jbe@70
|
1867 "issue"."id",
|
jbe@70
|
1868 "delegation"."truster_id",
|
jbe@70
|
1869 "delegation"."scope" DESC;
|
jbe@70
|
1870
|
jbe@97
|
1871 COMMENT ON VIEW "issue_delegation" IS 'Issue delegations where trusters are active and have voting right';
|
jbe@5
|
1872
|
jbe@5
|
1873
|
jbe@5
|
1874 CREATE FUNCTION "membership_weight_with_skipping"
|
jbe@5
|
1875 ( "area_id_p" "area"."id"%TYPE,
|
jbe@5
|
1876 "member_id_p" "member"."id"%TYPE,
|
jbe@5
|
1877 "skip_member_ids_p" INT4[] ) -- "member"."id"%TYPE[]
|
jbe@5
|
1878 RETURNS INT4
|
jbe@5
|
1879 LANGUAGE 'plpgsql' STABLE AS $$
|
jbe@5
|
1880 DECLARE
|
jbe@5
|
1881 "sum_v" INT4;
|
jbe@5
|
1882 "delegation_row" "area_delegation"%ROWTYPE;
|
jbe@5
|
1883 BEGIN
|
jbe@5
|
1884 "sum_v" := 1;
|
jbe@5
|
1885 FOR "delegation_row" IN
|
jbe@5
|
1886 SELECT "area_delegation".*
|
jbe@5
|
1887 FROM "area_delegation" LEFT JOIN "membership"
|
jbe@5
|
1888 ON "membership"."area_id" = "area_id_p"
|
jbe@5
|
1889 AND "membership"."member_id" = "area_delegation"."truster_id"
|
jbe@5
|
1890 WHERE "area_delegation"."area_id" = "area_id_p"
|
jbe@5
|
1891 AND "area_delegation"."trustee_id" = "member_id_p"
|
jbe@5
|
1892 AND "membership"."member_id" ISNULL
|
jbe@5
|
1893 LOOP
|
jbe@5
|
1894 IF NOT
|
jbe@5
|
1895 "skip_member_ids_p" @> ARRAY["delegation_row"."truster_id"]
|
jbe@5
|
1896 THEN
|
jbe@5
|
1897 "sum_v" := "sum_v" + "membership_weight_with_skipping"(
|
jbe@5
|
1898 "area_id_p",
|
jbe@5
|
1899 "delegation_row"."truster_id",
|
jbe@5
|
1900 "skip_member_ids_p" || "delegation_row"."truster_id"
|
jbe@5
|
1901 );
|
jbe@5
|
1902 END IF;
|
jbe@5
|
1903 END LOOP;
|
jbe@5
|
1904 RETURN "sum_v";
|
jbe@5
|
1905 END;
|
jbe@5
|
1906 $$;
|
jbe@5
|
1907
|
jbe@8
|
1908 COMMENT ON FUNCTION "membership_weight_with_skipping"
|
jbe@8
|
1909 ( "area"."id"%TYPE,
|
jbe@8
|
1910 "member"."id"%TYPE,
|
jbe@8
|
1911 INT4[] )
|
jbe@8
|
1912 IS 'Helper function for "membership_weight" function';
|
jbe@8
|
1913
|
jbe@8
|
1914
|
jbe@5
|
1915 CREATE FUNCTION "membership_weight"
|
jbe@5
|
1916 ( "area_id_p" "area"."id"%TYPE,
|
jbe@5
|
1917 "member_id_p" "member"."id"%TYPE ) -- "member"."id"%TYPE[]
|
jbe@5
|
1918 RETURNS INT4
|
jbe@5
|
1919 LANGUAGE 'plpgsql' STABLE AS $$
|
jbe@5
|
1920 BEGIN
|
jbe@5
|
1921 RETURN "membership_weight_with_skipping"(
|
jbe@5
|
1922 "area_id_p",
|
jbe@5
|
1923 "member_id_p",
|
jbe@5
|
1924 ARRAY["member_id_p"]
|
jbe@5
|
1925 );
|
jbe@5
|
1926 END;
|
jbe@5
|
1927 $$;
|
jbe@5
|
1928
|
jbe@8
|
1929 COMMENT ON FUNCTION "membership_weight"
|
jbe@8
|
1930 ( "area"."id"%TYPE,
|
jbe@8
|
1931 "member"."id"%TYPE )
|
jbe@8
|
1932 IS 'Calculates the potential voting weight of a member in a given area';
|
jbe@8
|
1933
|
jbe@5
|
1934
|
jbe@4
|
1935 CREATE VIEW "member_count_view" AS
|
jbe@5
|
1936 SELECT count(1) AS "total_count" FROM "member" WHERE "active";
|
jbe@4
|
1937
|
jbe@4
|
1938 COMMENT ON VIEW "member_count_view" IS 'View used to update "member_count" table';
|
jbe@4
|
1939
|
jbe@4
|
1940
|
jbe@97
|
1941 CREATE VIEW "unit_member_count" AS
|
jbe@97
|
1942 SELECT
|
jbe@97
|
1943 "unit"."id" AS "unit_id",
|
jbe@248
|
1944 count("member"."id") AS "member_count"
|
jbe@97
|
1945 FROM "unit"
|
jbe@97
|
1946 LEFT JOIN "privilege"
|
jbe@97
|
1947 ON "privilege"."unit_id" = "unit"."id"
|
jbe@97
|
1948 AND "privilege"."voting_right"
|
jbe@97
|
1949 LEFT JOIN "member"
|
jbe@97
|
1950 ON "member"."id" = "privilege"."member_id"
|
jbe@97
|
1951 AND "member"."active"
|
jbe@97
|
1952 GROUP BY "unit"."id";
|
jbe@97
|
1953
|
jbe@97
|
1954 COMMENT ON VIEW "unit_member_count" IS 'View used to update "member_count" column of "unit" table';
|
jbe@97
|
1955
|
jbe@97
|
1956
|
jbe@4
|
1957 CREATE VIEW "area_member_count" AS
|
jbe@5
|
1958 SELECT
|
jbe@5
|
1959 "area"."id" AS "area_id",
|
jbe@5
|
1960 count("member"."id") AS "direct_member_count",
|
jbe@5
|
1961 coalesce(
|
jbe@5
|
1962 sum(
|
jbe@5
|
1963 CASE WHEN "member"."id" NOTNULL THEN
|
jbe@5
|
1964 "membership_weight"("area"."id", "member"."id")
|
jbe@5
|
1965 ELSE 0 END
|
jbe@5
|
1966 )
|
jbe@169
|
1967 ) AS "member_weight"
|
jbe@4
|
1968 FROM "area"
|
jbe@4
|
1969 LEFT JOIN "membership"
|
jbe@4
|
1970 ON "area"."id" = "membership"."area_id"
|
jbe@97
|
1971 LEFT JOIN "privilege"
|
jbe@97
|
1972 ON "privilege"."unit_id" = "area"."unit_id"
|
jbe@97
|
1973 AND "privilege"."member_id" = "membership"."member_id"
|
jbe@97
|
1974 AND "privilege"."voting_right"
|
jbe@4
|
1975 LEFT JOIN "member"
|
jbe@97
|
1976 ON "member"."id" = "privilege"."member_id" -- NOTE: no membership here!
|
jbe@4
|
1977 AND "member"."active"
|
jbe@4
|
1978 GROUP BY "area"."id";
|
jbe@4
|
1979
|
jbe@169
|
1980 COMMENT ON VIEW "area_member_count" IS 'View used to update "direct_member_count" and "member_weight" columns of table "area"';
|
jbe@4
|
1981
|
jbe@4
|
1982
|
jbe@9
|
1983 CREATE VIEW "opening_draft" AS
|
jbe@9
|
1984 SELECT "draft".* FROM (
|
jbe@9
|
1985 SELECT
|
jbe@9
|
1986 "initiative"."id" AS "initiative_id",
|
jbe@9
|
1987 min("draft"."id") AS "draft_id"
|
jbe@9
|
1988 FROM "initiative" JOIN "draft"
|
jbe@9
|
1989 ON "initiative"."id" = "draft"."initiative_id"
|
jbe@9
|
1990 GROUP BY "initiative"."id"
|
jbe@9
|
1991 ) AS "subquery"
|
jbe@9
|
1992 JOIN "draft" ON "subquery"."draft_id" = "draft"."id";
|
jbe@9
|
1993
|
jbe@9
|
1994 COMMENT ON VIEW "opening_draft" IS 'First drafts of all initiatives';
|
jbe@9
|
1995
|
jbe@9
|
1996
|
jbe@0
|
1997 CREATE VIEW "current_draft" AS
|
jbe@0
|
1998 SELECT "draft".* FROM (
|
jbe@0
|
1999 SELECT
|
jbe@0
|
2000 "initiative"."id" AS "initiative_id",
|
jbe@0
|
2001 max("draft"."id") AS "draft_id"
|
jbe@0
|
2002 FROM "initiative" JOIN "draft"
|
jbe@0
|
2003 ON "initiative"."id" = "draft"."initiative_id"
|
jbe@0
|
2004 GROUP BY "initiative"."id"
|
jbe@0
|
2005 ) AS "subquery"
|
jbe@0
|
2006 JOIN "draft" ON "subquery"."draft_id" = "draft"."id";
|
jbe@0
|
2007
|
jbe@0
|
2008 COMMENT ON VIEW "current_draft" IS 'All latest drafts for each initiative';
|
jbe@0
|
2009
|
jbe@0
|
2010
|
jbe@0
|
2011 CREATE VIEW "critical_opinion" AS
|
jbe@0
|
2012 SELECT * FROM "opinion"
|
jbe@0
|
2013 WHERE ("degree" = 2 AND "fulfilled" = FALSE)
|
jbe@0
|
2014 OR ("degree" = -2 AND "fulfilled" = TRUE);
|
jbe@0
|
2015
|
jbe@0
|
2016 COMMENT ON VIEW "critical_opinion" IS 'Opinions currently causing dissatisfaction';
|
jbe@0
|
2017
|
jbe@0
|
2018
|
jbe@126
|
2019 CREATE VIEW "battle_participant" AS
|
jbe@126
|
2020 SELECT "initiative"."id", "initiative"."issue_id"
|
jbe@126
|
2021 FROM "issue" JOIN "initiative"
|
jbe@126
|
2022 ON "issue"."id" = "initiative"."issue_id"
|
jbe@126
|
2023 WHERE "initiative"."admitted"
|
jbe@126
|
2024 UNION ALL
|
jbe@126
|
2025 SELECT NULL, "id" AS "issue_id"
|
jbe@126
|
2026 FROM "issue";
|
jbe@126
|
2027
|
jbe@126
|
2028 COMMENT ON VIEW "battle_participant" IS 'Helper view for "battle_view" containing admitted initiatives plus virtual "status-quo" initiative denoted by NULL reference';
|
jbe@126
|
2029
|
jbe@126
|
2030
|
jbe@61
|
2031 CREATE VIEW "battle_view" AS
|
jbe@0
|
2032 SELECT
|
jbe@0
|
2033 "issue"."id" AS "issue_id",
|
jbe@10
|
2034 "winning_initiative"."id" AS "winning_initiative_id",
|
jbe@10
|
2035 "losing_initiative"."id" AS "losing_initiative_id",
|
jbe@0
|
2036 sum(
|
jbe@0
|
2037 CASE WHEN
|
jbe@0
|
2038 coalesce("better_vote"."grade", 0) >
|
jbe@0
|
2039 coalesce("worse_vote"."grade", 0)
|
jbe@0
|
2040 THEN "direct_voter"."weight" ELSE 0 END
|
jbe@0
|
2041 ) AS "count"
|
jbe@0
|
2042 FROM "issue"
|
jbe@0
|
2043 LEFT JOIN "direct_voter"
|
jbe@0
|
2044 ON "issue"."id" = "direct_voter"."issue_id"
|
jbe@126
|
2045 JOIN "battle_participant" AS "winning_initiative"
|
jbe@10
|
2046 ON "issue"."id" = "winning_initiative"."issue_id"
|
jbe@126
|
2047 JOIN "battle_participant" AS "losing_initiative"
|
jbe@10
|
2048 ON "issue"."id" = "losing_initiative"."issue_id"
|
jbe@0
|
2049 LEFT JOIN "vote" AS "better_vote"
|
jbe@10
|
2050 ON "direct_voter"."member_id" = "better_vote"."member_id"
|
jbe@10
|
2051 AND "winning_initiative"."id" = "better_vote"."initiative_id"
|
jbe@0
|
2052 LEFT JOIN "vote" AS "worse_vote"
|
jbe@10
|
2053 ON "direct_voter"."member_id" = "worse_vote"."member_id"
|
jbe@10
|
2054 AND "losing_initiative"."id" = "worse_vote"."initiative_id"
|
jbe@61
|
2055 WHERE "issue"."closed" NOTNULL
|
jbe@61
|
2056 AND "issue"."cleaned" ISNULL
|
jbe@126
|
2057 AND (
|
jbe@126
|
2058 "winning_initiative"."id" != "losing_initiative"."id" OR
|
jbe@126
|
2059 ( ("winning_initiative"."id" NOTNULL AND "losing_initiative"."id" ISNULL) OR
|
jbe@126
|
2060 ("winning_initiative"."id" ISNULL AND "losing_initiative"."id" NOTNULL) ) )
|
jbe@0
|
2061 GROUP BY
|
jbe@0
|
2062 "issue"."id",
|
jbe@10
|
2063 "winning_initiative"."id",
|
jbe@10
|
2064 "losing_initiative"."id";
|
jbe@0
|
2065
|
jbe@126
|
2066 COMMENT ON VIEW "battle_view" IS 'Number of members preferring one initiative (or status-quo) to another initiative (or status-quo); Used to fill "battle" table';
|
jbe@1
|
2067
|
jbe@1
|
2068
|
jbe@235
|
2069 CREATE VIEW "expired_session" AS
|
jbe@235
|
2070 SELECT * FROM "session" WHERE now() > "expiry";
|
jbe@235
|
2071
|
jbe@235
|
2072 CREATE RULE "delete" AS ON DELETE TO "expired_session" DO INSTEAD
|
jbe@235
|
2073 DELETE FROM "session" WHERE "ident" = OLD."ident";
|
jbe@235
|
2074
|
jbe@235
|
2075 COMMENT ON VIEW "expired_session" IS 'View containing all expired sessions where DELETE is possible';
|
jbe@235
|
2076 COMMENT ON RULE "delete" ON "expired_session" IS 'Rule allowing DELETE on rows in "expired_session" view, i.e. DELETE FROM "expired_session"';
|
jbe@235
|
2077
|
jbe@235
|
2078
|
jbe@0
|
2079 CREATE VIEW "open_issue" AS
|
jbe@0
|
2080 SELECT * FROM "issue" WHERE "closed" ISNULL;
|
jbe@0
|
2081
|
jbe@0
|
2082 COMMENT ON VIEW "open_issue" IS 'All open issues';
|
jbe@0
|
2083
|
jbe@0
|
2084
|
jbe@0
|
2085 CREATE VIEW "issue_with_ranks_missing" AS
|
jbe@0
|
2086 SELECT * FROM "issue"
|
jbe@3
|
2087 WHERE "fully_frozen" NOTNULL
|
jbe@0
|
2088 AND "closed" NOTNULL
|
jbe@0
|
2089 AND "ranks_available" = FALSE;
|
jbe@0
|
2090
|
jbe@0
|
2091 COMMENT ON VIEW "issue_with_ranks_missing" IS 'Issues where voting was finished, but no ranks have been calculated yet';
|
jbe@0
|
2092
|
jbe@0
|
2093
|
jbe@9
|
2094 CREATE VIEW "member_contingent" AS
|
jbe@9
|
2095 SELECT
|
jbe@9
|
2096 "member"."id" AS "member_id",
|
jbe@9
|
2097 "contingent"."time_frame",
|
jbe@9
|
2098 CASE WHEN "contingent"."text_entry_limit" NOTNULL THEN
|
jbe@9
|
2099 (
|
jbe@9
|
2100 SELECT count(1) FROM "draft"
|
jbe@9
|
2101 WHERE "draft"."author_id" = "member"."id"
|
jbe@9
|
2102 AND "draft"."created" > now() - "contingent"."time_frame"
|
jbe@9
|
2103 ) + (
|
jbe@9
|
2104 SELECT count(1) FROM "suggestion"
|
jbe@9
|
2105 WHERE "suggestion"."author_id" = "member"."id"
|
jbe@9
|
2106 AND "suggestion"."created" > now() - "contingent"."time_frame"
|
jbe@9
|
2107 )
|
jbe@9
|
2108 ELSE NULL END AS "text_entry_count",
|
jbe@9
|
2109 "contingent"."text_entry_limit",
|
jbe@9
|
2110 CASE WHEN "contingent"."initiative_limit" NOTNULL THEN (
|
jbe@9
|
2111 SELECT count(1) FROM "opening_draft"
|
jbe@9
|
2112 WHERE "opening_draft"."author_id" = "member"."id"
|
jbe@9
|
2113 AND "opening_draft"."created" > now() - "contingent"."time_frame"
|
jbe@9
|
2114 ) ELSE NULL END AS "initiative_count",
|
jbe@9
|
2115 "contingent"."initiative_limit"
|
jbe@9
|
2116 FROM "member" CROSS JOIN "contingent";
|
jbe@9
|
2117
|
jbe@9
|
2118 COMMENT ON VIEW "member_contingent" IS 'Actual counts of text entries and initiatives are calculated per member for each limit in the "contingent" table.';
|
jbe@9
|
2119
|
jbe@9
|
2120 COMMENT ON COLUMN "member_contingent"."text_entry_count" IS 'Only calculated when "text_entry_limit" is not null in the same row';
|
jbe@9
|
2121 COMMENT ON COLUMN "member_contingent"."initiative_count" IS 'Only calculated when "initiative_limit" is not null in the same row';
|
jbe@9
|
2122
|
jbe@9
|
2123
|
jbe@9
|
2124 CREATE VIEW "member_contingent_left" AS
|
jbe@9
|
2125 SELECT
|
jbe@9
|
2126 "member_id",
|
jbe@9
|
2127 max("text_entry_limit" - "text_entry_count") AS "text_entries_left",
|
jbe@9
|
2128 max("initiative_limit" - "initiative_count") AS "initiatives_left"
|
jbe@9
|
2129 FROM "member_contingent" GROUP BY "member_id";
|
jbe@9
|
2130
|
jbe@9
|
2131 COMMENT ON VIEW "member_contingent_left" IS 'Amount of text entries or initiatives which can be posted now instantly by a member. This view should be used by a frontend to determine, if the contingent for posting is exhausted.';
|
jbe@9
|
2132
|
jbe@9
|
2133
|
jbe@113
|
2134 CREATE VIEW "event_seen_by_member" AS
|
jbe@113
|
2135 SELECT
|
jbe@113
|
2136 "member"."id" AS "seen_by_member_id",
|
jbe@113
|
2137 CASE WHEN "event"."state" IN (
|
jbe@113
|
2138 'voting',
|
jbe@113
|
2139 'finished_without_winner',
|
jbe@113
|
2140 'finished_with_winner'
|
jbe@113
|
2141 ) THEN
|
jbe@113
|
2142 'voting'::"notify_level"
|
jbe@113
|
2143 ELSE
|
jbe@113
|
2144 CASE WHEN "event"."state" IN (
|
jbe@113
|
2145 'verification',
|
jbe@113
|
2146 'canceled_after_revocation_during_verification',
|
jbe@113
|
2147 'canceled_no_initiative_admitted'
|
jbe@113
|
2148 ) THEN
|
jbe@113
|
2149 'verification'::"notify_level"
|
jbe@113
|
2150 ELSE
|
jbe@113
|
2151 CASE WHEN "event"."state" IN (
|
jbe@113
|
2152 'discussion',
|
jbe@113
|
2153 'canceled_after_revocation_during_discussion'
|
jbe@113
|
2154 ) THEN
|
jbe@113
|
2155 'discussion'::"notify_level"
|
jbe@113
|
2156 ELSE
|
jbe@113
|
2157 'all'::"notify_level"
|
jbe@113
|
2158 END
|
jbe@113
|
2159 END
|
jbe@113
|
2160 END AS "notify_level",
|
jbe@113
|
2161 "event".*
|
jbe@113
|
2162 FROM "member" CROSS JOIN "event"
|
jbe@113
|
2163 LEFT JOIN "issue"
|
jbe@113
|
2164 ON "event"."issue_id" = "issue"."id"
|
jbe@113
|
2165 LEFT JOIN "membership"
|
jbe@113
|
2166 ON "member"."id" = "membership"."member_id"
|
jbe@113
|
2167 AND "issue"."area_id" = "membership"."area_id"
|
jbe@113
|
2168 LEFT JOIN "interest"
|
jbe@113
|
2169 ON "member"."id" = "interest"."member_id"
|
jbe@113
|
2170 AND "event"."issue_id" = "interest"."issue_id"
|
jbe@113
|
2171 LEFT JOIN "supporter"
|
jbe@113
|
2172 ON "member"."id" = "supporter"."member_id"
|
jbe@113
|
2173 AND "event"."initiative_id" = "supporter"."initiative_id"
|
jbe@113
|
2174 LEFT JOIN "ignored_member"
|
jbe@113
|
2175 ON "member"."id" = "ignored_member"."member_id"
|
jbe@113
|
2176 AND "event"."member_id" = "ignored_member"."other_member_id"
|
jbe@113
|
2177 LEFT JOIN "ignored_initiative"
|
jbe@113
|
2178 ON "member"."id" = "ignored_initiative"."member_id"
|
jbe@113
|
2179 AND "event"."initiative_id" = "ignored_initiative"."initiative_id"
|
jbe@113
|
2180 WHERE (
|
jbe@113
|
2181 "supporter"."member_id" NOTNULL OR
|
jbe@113
|
2182 "interest"."member_id" NOTNULL OR
|
jbe@113
|
2183 ( "membership"."member_id" NOTNULL AND
|
jbe@113
|
2184 "event"."event" IN (
|
jbe@113
|
2185 'issue_state_changed',
|
jbe@113
|
2186 'initiative_created_in_new_issue',
|
jbe@113
|
2187 'initiative_created_in_existing_issue',
|
jbe@113
|
2188 'initiative_revoked' ) ) )
|
jbe@113
|
2189 AND "ignored_member"."member_id" ISNULL
|
jbe@113
|
2190 AND "ignored_initiative"."member_id" ISNULL;
|
jbe@113
|
2191
|
jbe@222
|
2192 COMMENT ON VIEW "event_seen_by_member" IS 'Events as seen by a member, depending on its memberships, interests and support, but ignoring members "notify_level"';
|
jbe@222
|
2193
|
jbe@222
|
2194
|
jbe@222
|
2195 CREATE VIEW "selected_event_seen_by_member" AS
|
jbe@113
|
2196 SELECT
|
jbe@113
|
2197 "member"."id" AS "seen_by_member_id",
|
jbe@222
|
2198 CASE WHEN "event"."state" IN (
|
jbe@222
|
2199 'voting',
|
jbe@222
|
2200 'finished_without_winner',
|
jbe@222
|
2201 'finished_with_winner'
|
jbe@222
|
2202 ) THEN
|
jbe@222
|
2203 'voting'::"notify_level"
|
jbe@222
|
2204 ELSE
|
jbe@222
|
2205 CASE WHEN "event"."state" IN (
|
jbe@222
|
2206 'verification',
|
jbe@222
|
2207 'canceled_after_revocation_during_verification',
|
jbe@222
|
2208 'canceled_no_initiative_admitted'
|
jbe@222
|
2209 ) THEN
|
jbe@222
|
2210 'verification'::"notify_level"
|
jbe@222
|
2211 ELSE
|
jbe@222
|
2212 CASE WHEN "event"."state" IN (
|
jbe@222
|
2213 'discussion',
|
jbe@222
|
2214 'canceled_after_revocation_during_discussion'
|
jbe@222
|
2215 ) THEN
|
jbe@222
|
2216 'discussion'::"notify_level"
|
jbe@222
|
2217 ELSE
|
jbe@222
|
2218 'all'::"notify_level"
|
jbe@222
|
2219 END
|
jbe@222
|
2220 END
|
jbe@222
|
2221 END AS "notify_level",
|
jbe@113
|
2222 "event".*
|
jbe@113
|
2223 FROM "member" CROSS JOIN "event"
|
jbe@113
|
2224 LEFT JOIN "issue"
|
jbe@113
|
2225 ON "event"."issue_id" = "issue"."id"
|
jbe@113
|
2226 LEFT JOIN "membership"
|
jbe@113
|
2227 ON "member"."id" = "membership"."member_id"
|
jbe@113
|
2228 AND "issue"."area_id" = "membership"."area_id"
|
jbe@113
|
2229 LEFT JOIN "interest"
|
jbe@113
|
2230 ON "member"."id" = "interest"."member_id"
|
jbe@113
|
2231 AND "event"."issue_id" = "interest"."issue_id"
|
jbe@113
|
2232 LEFT JOIN "supporter"
|
jbe@113
|
2233 ON "member"."id" = "supporter"."member_id"
|
jbe@113
|
2234 AND "event"."initiative_id" = "supporter"."initiative_id"
|
jbe@113
|
2235 LEFT JOIN "ignored_member"
|
jbe@113
|
2236 ON "member"."id" = "ignored_member"."member_id"
|
jbe@113
|
2237 AND "event"."member_id" = "ignored_member"."other_member_id"
|
jbe@113
|
2238 LEFT JOIN "ignored_initiative"
|
jbe@113
|
2239 ON "member"."id" = "ignored_initiative"."member_id"
|
jbe@113
|
2240 AND "event"."initiative_id" = "ignored_initiative"."initiative_id"
|
jbe@113
|
2241 WHERE (
|
jbe@113
|
2242 ( "member"."notify_level" >= 'all' ) OR
|
jbe@113
|
2243 ( "member"."notify_level" >= 'voting' AND
|
jbe@113
|
2244 "event"."state" IN (
|
jbe@113
|
2245 'voting',
|
jbe@113
|
2246 'finished_without_winner',
|
jbe@113
|
2247 'finished_with_winner' ) ) OR
|
jbe@113
|
2248 ( "member"."notify_level" >= 'verification' AND
|
jbe@113
|
2249 "event"."state" IN (
|
jbe@113
|
2250 'verification',
|
jbe@113
|
2251 'canceled_after_revocation_during_verification',
|
jbe@113
|
2252 'canceled_no_initiative_admitted' ) ) OR
|
jbe@113
|
2253 ( "member"."notify_level" >= 'discussion' AND
|
jbe@113
|
2254 "event"."state" IN (
|
jbe@113
|
2255 'discussion',
|
jbe@113
|
2256 'canceled_after_revocation_during_discussion' ) ) )
|
jbe@113
|
2257 AND (
|
jbe@113
|
2258 "supporter"."member_id" NOTNULL OR
|
jbe@113
|
2259 "interest"."member_id" NOTNULL OR
|
jbe@113
|
2260 ( "membership"."member_id" NOTNULL AND
|
jbe@113
|
2261 "event"."event" IN (
|
jbe@113
|
2262 'issue_state_changed',
|
jbe@113
|
2263 'initiative_created_in_new_issue',
|
jbe@113
|
2264 'initiative_created_in_existing_issue',
|
jbe@113
|
2265 'initiative_revoked' ) ) )
|
jbe@113
|
2266 AND "ignored_member"."member_id" ISNULL
|
jbe@113
|
2267 AND "ignored_initiative"."member_id" ISNULL;
|
jbe@113
|
2268
|
jbe@222
|
2269 COMMENT ON VIEW "selected_event_seen_by_member" IS 'Events as seen by a member, depending on its memberships, interests, support and members "notify_level"';
|
jbe@113
|
2270
|
jbe@113
|
2271
|
jbe@16
|
2272 CREATE TYPE "timeline_event" AS ENUM (
|
jbe@16
|
2273 'issue_created',
|
jbe@16
|
2274 'issue_canceled',
|
jbe@16
|
2275 'issue_accepted',
|
jbe@16
|
2276 'issue_half_frozen',
|
jbe@16
|
2277 'issue_finished_without_voting',
|
jbe@16
|
2278 'issue_voting_started',
|
jbe@16
|
2279 'issue_finished_after_voting',
|
jbe@16
|
2280 'initiative_created',
|
jbe@16
|
2281 'initiative_revoked',
|
jbe@16
|
2282 'draft_created',
|
jbe@16
|
2283 'suggestion_created');
|
jbe@16
|
2284
|
jbe@112
|
2285 COMMENT ON TYPE "timeline_event" IS 'Types of event in timeline tables (DEPRECATED)';
|
jbe@16
|
2286
|
jbe@16
|
2287
|
jbe@16
|
2288 CREATE VIEW "timeline_issue" AS
|
jbe@16
|
2289 SELECT
|
jbe@16
|
2290 "created" AS "occurrence",
|
jbe@16
|
2291 'issue_created'::"timeline_event" AS "event",
|
jbe@16
|
2292 "id" AS "issue_id"
|
jbe@16
|
2293 FROM "issue"
|
jbe@16
|
2294 UNION ALL
|
jbe@16
|
2295 SELECT
|
jbe@16
|
2296 "closed" AS "occurrence",
|
jbe@16
|
2297 'issue_canceled'::"timeline_event" AS "event",
|
jbe@16
|
2298 "id" AS "issue_id"
|
jbe@16
|
2299 FROM "issue" WHERE "closed" NOTNULL AND "fully_frozen" ISNULL
|
jbe@16
|
2300 UNION ALL
|
jbe@16
|
2301 SELECT
|
jbe@16
|
2302 "accepted" AS "occurrence",
|
jbe@16
|
2303 'issue_accepted'::"timeline_event" AS "event",
|
jbe@16
|
2304 "id" AS "issue_id"
|
jbe@16
|
2305 FROM "issue" WHERE "accepted" NOTNULL
|
jbe@16
|
2306 UNION ALL
|
jbe@16
|
2307 SELECT
|
jbe@16
|
2308 "half_frozen" AS "occurrence",
|
jbe@16
|
2309 'issue_half_frozen'::"timeline_event" AS "event",
|
jbe@16
|
2310 "id" AS "issue_id"
|
jbe@16
|
2311 FROM "issue" WHERE "half_frozen" NOTNULL
|
jbe@16
|
2312 UNION ALL
|
jbe@16
|
2313 SELECT
|
jbe@16
|
2314 "fully_frozen" AS "occurrence",
|
jbe@16
|
2315 'issue_voting_started'::"timeline_event" AS "event",
|
jbe@16
|
2316 "id" AS "issue_id"
|
jbe@16
|
2317 FROM "issue"
|
jbe@17
|
2318 WHERE "fully_frozen" NOTNULL
|
jbe@17
|
2319 AND ("closed" ISNULL OR "closed" != "fully_frozen")
|
jbe@16
|
2320 UNION ALL
|
jbe@16
|
2321 SELECT
|
jbe@16
|
2322 "closed" AS "occurrence",
|
jbe@16
|
2323 CASE WHEN "fully_frozen" = "closed" THEN
|
jbe@16
|
2324 'issue_finished_without_voting'::"timeline_event"
|
jbe@16
|
2325 ELSE
|
jbe@16
|
2326 'issue_finished_after_voting'::"timeline_event"
|
jbe@16
|
2327 END AS "event",
|
jbe@16
|
2328 "id" AS "issue_id"
|
jbe@16
|
2329 FROM "issue" WHERE "closed" NOTNULL AND "fully_frozen" NOTNULL;
|
jbe@16
|
2330
|
jbe@112
|
2331 COMMENT ON VIEW "timeline_issue" IS 'Helper view for "timeline" view (DEPRECATED)';
|
jbe@16
|
2332
|
jbe@16
|
2333
|
jbe@16
|
2334 CREATE VIEW "timeline_initiative" AS
|
jbe@16
|
2335 SELECT
|
jbe@16
|
2336 "created" AS "occurrence",
|
jbe@16
|
2337 'initiative_created'::"timeline_event" AS "event",
|
jbe@16
|
2338 "id" AS "initiative_id"
|
jbe@16
|
2339 FROM "initiative"
|
jbe@16
|
2340 UNION ALL
|
jbe@16
|
2341 SELECT
|
jbe@16
|
2342 "revoked" AS "occurrence",
|
jbe@16
|
2343 'initiative_revoked'::"timeline_event" AS "event",
|
jbe@16
|
2344 "id" AS "initiative_id"
|
jbe@16
|
2345 FROM "initiative" WHERE "revoked" NOTNULL;
|
jbe@16
|
2346
|
jbe@112
|
2347 COMMENT ON VIEW "timeline_initiative" IS 'Helper view for "timeline" view (DEPRECATED)';
|
jbe@16
|
2348
|
jbe@16
|
2349
|
jbe@16
|
2350 CREATE VIEW "timeline_draft" AS
|
jbe@16
|
2351 SELECT
|
jbe@16
|
2352 "created" AS "occurrence",
|
jbe@16
|
2353 'draft_created'::"timeline_event" AS "event",
|
jbe@16
|
2354 "id" AS "draft_id"
|
jbe@16
|
2355 FROM "draft";
|
jbe@16
|
2356
|
jbe@112
|
2357 COMMENT ON VIEW "timeline_draft" IS 'Helper view for "timeline" view (DEPRECATED)';
|
jbe@16
|
2358
|
jbe@16
|
2359
|
jbe@16
|
2360 CREATE VIEW "timeline_suggestion" AS
|
jbe@16
|
2361 SELECT
|
jbe@16
|
2362 "created" AS "occurrence",
|
jbe@16
|
2363 'suggestion_created'::"timeline_event" AS "event",
|
jbe@16
|
2364 "id" AS "suggestion_id"
|
jbe@16
|
2365 FROM "suggestion";
|
jbe@16
|
2366
|
jbe@112
|
2367 COMMENT ON VIEW "timeline_suggestion" IS 'Helper view for "timeline" view (DEPRECATED)';
|
jbe@16
|
2368
|
jbe@16
|
2369
|
jbe@16
|
2370 CREATE VIEW "timeline" AS
|
jbe@16
|
2371 SELECT
|
jbe@16
|
2372 "occurrence",
|
jbe@16
|
2373 "event",
|
jbe@16
|
2374 "issue_id",
|
jbe@16
|
2375 NULL AS "initiative_id",
|
jbe@16
|
2376 NULL::INT8 AS "draft_id", -- TODO: Why do we need a type-cast here? Is this due to 32 bit architecture?
|
jbe@16
|
2377 NULL::INT8 AS "suggestion_id"
|
jbe@16
|
2378 FROM "timeline_issue"
|
jbe@16
|
2379 UNION ALL
|
jbe@16
|
2380 SELECT
|
jbe@16
|
2381 "occurrence",
|
jbe@16
|
2382 "event",
|
jbe@16
|
2383 NULL AS "issue_id",
|
jbe@16
|
2384 "initiative_id",
|
jbe@16
|
2385 NULL AS "draft_id",
|
jbe@16
|
2386 NULL AS "suggestion_id"
|
jbe@16
|
2387 FROM "timeline_initiative"
|
jbe@16
|
2388 UNION ALL
|
jbe@16
|
2389 SELECT
|
jbe@16
|
2390 "occurrence",
|
jbe@16
|
2391 "event",
|
jbe@16
|
2392 NULL AS "issue_id",
|
jbe@16
|
2393 NULL AS "initiative_id",
|
jbe@16
|
2394 "draft_id",
|
jbe@16
|
2395 NULL AS "suggestion_id"
|
jbe@16
|
2396 FROM "timeline_draft"
|
jbe@16
|
2397 UNION ALL
|
jbe@16
|
2398 SELECT
|
jbe@16
|
2399 "occurrence",
|
jbe@16
|
2400 "event",
|
jbe@16
|
2401 NULL AS "issue_id",
|
jbe@16
|
2402 NULL AS "initiative_id",
|
jbe@16
|
2403 NULL AS "draft_id",
|
jbe@16
|
2404 "suggestion_id"
|
jbe@16
|
2405 FROM "timeline_suggestion";
|
jbe@16
|
2406
|
jbe@112
|
2407 COMMENT ON VIEW "timeline" IS 'Aggregation of different events in the system (DEPRECATED)';
|
jbe@16
|
2408
|
jbe@16
|
2409
|
jbe@0
|
2410
|
jbe@242
|
2411 ------------------------------------------------------
|
jbe@242
|
2412 -- Row set returning function for delegation chains --
|
jbe@242
|
2413 ------------------------------------------------------
|
jbe@5
|
2414
|
jbe@5
|
2415
|
jbe@5
|
2416 CREATE TYPE "delegation_chain_loop_tag" AS ENUM
|
jbe@5
|
2417 ('first', 'intermediate', 'last', 'repetition');
|
jbe@5
|
2418
|
jbe@5
|
2419 COMMENT ON TYPE "delegation_chain_loop_tag" IS 'Type for loop tags in "delegation_chain_row" type';
|
jbe@5
|
2420
|
jbe@5
|
2421
|
jbe@5
|
2422 CREATE TYPE "delegation_chain_row" AS (
|
jbe@5
|
2423 "index" INT4,
|
jbe@5
|
2424 "member_id" INT4,
|
jbe@97
|
2425 "member_valid" BOOLEAN,
|
jbe@5
|
2426 "participation" BOOLEAN,
|
jbe@5
|
2427 "overridden" BOOLEAN,
|
jbe@5
|
2428 "scope_in" "delegation_scope",
|
jbe@5
|
2429 "scope_out" "delegation_scope",
|
jbe@86
|
2430 "disabled_out" BOOLEAN,
|
jbe@5
|
2431 "loop" "delegation_chain_loop_tag" );
|
jbe@5
|
2432
|
jbe@243
|
2433 COMMENT ON TYPE "delegation_chain_row" IS 'Type of rows returned by "delegation_chain" function';
|
jbe@5
|
2434
|
jbe@5
|
2435 COMMENT ON COLUMN "delegation_chain_row"."index" IS 'Index starting with 0 and counting up';
|
jbe@5
|
2436 COMMENT ON COLUMN "delegation_chain_row"."participation" IS 'In case of delegation chains for issues: interest, for areas: membership, for global delegation chains: always null';
|
jbe@5
|
2437 COMMENT ON COLUMN "delegation_chain_row"."overridden" IS 'True, if an entry with lower index has "participation" set to true';
|
jbe@5
|
2438 COMMENT ON COLUMN "delegation_chain_row"."scope_in" IS 'Scope of used incoming delegation';
|
jbe@5
|
2439 COMMENT ON COLUMN "delegation_chain_row"."scope_out" IS 'Scope of used outgoing delegation';
|
jbe@86
|
2440 COMMENT ON COLUMN "delegation_chain_row"."disabled_out" IS 'Outgoing delegation is explicitly disabled by a delegation with trustee_id set to NULL';
|
jbe@5
|
2441 COMMENT ON COLUMN "delegation_chain_row"."loop" IS 'Not null, if member is part of a loop, see "delegation_chain_loop_tag" type';
|
jbe@5
|
2442
|
jbe@5
|
2443
|
jbe@242
|
2444 CREATE FUNCTION "delegation_chain_for_closed_issue"
|
jbe@242
|
2445 ( "member_id_p" "member"."id"%TYPE,
|
jbe@242
|
2446 "issue_id_p" "issue"."id"%TYPE )
|
jbe@242
|
2447 RETURNS SETOF "delegation_chain_row"
|
jbe@242
|
2448 LANGUAGE 'plpgsql' STABLE AS $$
|
jbe@242
|
2449 DECLARE
|
jbe@242
|
2450 "output_row" "delegation_chain_row";
|
jbe@242
|
2451 "direct_voter_row" "direct_voter"%ROWTYPE;
|
jbe@242
|
2452 "delegating_voter_row" "delegating_voter"%ROWTYPE;
|
jbe@242
|
2453 BEGIN
|
jbe@242
|
2454 "output_row"."index" := 0;
|
jbe@242
|
2455 "output_row"."member_id" := "member_id_p";
|
jbe@242
|
2456 "output_row"."member_valid" := TRUE;
|
jbe@242
|
2457 "output_row"."participation" := FALSE;
|
jbe@242
|
2458 "output_row"."overridden" := FALSE;
|
jbe@242
|
2459 "output_row"."disabled_out" := FALSE;
|
jbe@242
|
2460 LOOP
|
jbe@242
|
2461 SELECT INTO "direct_voter_row" * FROM "direct_voter"
|
jbe@242
|
2462 WHERE "issue_id" = "issue_id_p"
|
jbe@242
|
2463 AND "member_id" = "output_row"."member_id";
|
jbe@242
|
2464 IF "direct_voter_row"."member_id" NOTNULL THEN
|
jbe@242
|
2465 "output_row"."participation" := TRUE;
|
jbe@242
|
2466 "output_row"."scope_out" := NULL;
|
jbe@242
|
2467 "output_row"."disabled_out" := NULL;
|
jbe@242
|
2468 RETURN NEXT "output_row";
|
jbe@242
|
2469 RETURN;
|
jbe@242
|
2470 END IF;
|
jbe@242
|
2471 SELECT INTO "delegating_voter_row" * FROM "delegating_voter"
|
jbe@242
|
2472 WHERE "issue_id" = "issue_id_p"
|
jbe@242
|
2473 AND "member_id" = "output_row"."member_id";
|
jbe@242
|
2474 IF "delegating_voter_row"."member_id" ISNULL THEN
|
jbe@242
|
2475 RETURN;
|
jbe@242
|
2476 END IF;
|
jbe@242
|
2477 "output_row"."scope_out" := "delegating_voter_row"."scope";
|
jbe@242
|
2478 RETURN NEXT "output_row";
|
jbe@242
|
2479 "output_row"."member_id" := "delegating_voter_row"."delegate_member_ids"[1];
|
jbe@242
|
2480 "output_row"."scope_in" := "output_row"."scope_out";
|
jbe@242
|
2481 END LOOP;
|
jbe@242
|
2482 END;
|
jbe@242
|
2483 $$;
|
jbe@242
|
2484
|
jbe@242
|
2485 COMMENT ON FUNCTION "delegation_chain_for_closed_issue"
|
jbe@242
|
2486 ( "member"."id"%TYPE,
|
jbe@242
|
2487 "member"."id"%TYPE )
|
jbe@242
|
2488 IS 'Helper function for "delegation_chain" function, handling the special case of closed issues after voting';
|
jbe@242
|
2489
|
jbe@242
|
2490
|
jbe@5
|
2491 CREATE FUNCTION "delegation_chain"
|
jbe@5
|
2492 ( "member_id_p" "member"."id"%TYPE,
|
jbe@97
|
2493 "unit_id_p" "unit"."id"%TYPE,
|
jbe@5
|
2494 "area_id_p" "area"."id"%TYPE,
|
jbe@5
|
2495 "issue_id_p" "issue"."id"%TYPE,
|
jbe@255
|
2496 "simulate_trustee_id_p" "member"."id"%TYPE DEFAULT NULL,
|
jbe@255
|
2497 "simulate_default_p" BOOLEAN DEFAULT FALSE )
|
jbe@5
|
2498 RETURNS SETOF "delegation_chain_row"
|
jbe@5
|
2499 LANGUAGE 'plpgsql' STABLE AS $$
|
jbe@5
|
2500 DECLARE
|
jbe@97
|
2501 "scope_v" "delegation_scope";
|
jbe@97
|
2502 "unit_id_v" "unit"."id"%TYPE;
|
jbe@97
|
2503 "area_id_v" "area"."id"%TYPE;
|
jbe@241
|
2504 "issue_row" "issue"%ROWTYPE;
|
jbe@5
|
2505 "visited_member_ids" INT4[]; -- "member"."id"%TYPE[]
|
jbe@5
|
2506 "loop_member_id_v" "member"."id"%TYPE;
|
jbe@5
|
2507 "output_row" "delegation_chain_row";
|
jbe@5
|
2508 "output_rows" "delegation_chain_row"[];
|
jbe@255
|
2509 "simulate_v" BOOLEAN;
|
jbe@255
|
2510 "simulate_here_v" BOOLEAN;
|
jbe@5
|
2511 "delegation_row" "delegation"%ROWTYPE;
|
jbe@5
|
2512 "row_count" INT4;
|
jbe@5
|
2513 "i" INT4;
|
jbe@5
|
2514 "loop_v" BOOLEAN;
|
jbe@5
|
2515 BEGIN
|
jbe@255
|
2516 IF "simulate_trustee_id_p" NOTNULL AND "simulate_default_p" THEN
|
jbe@255
|
2517 RAISE EXCEPTION 'Both "simulate_trustee_id_p" is set, and "simulate_default_p" is true';
|
jbe@255
|
2518 END IF;
|
jbe@255
|
2519 IF "simulate_trustee_id_p" NOTNULL OR "simulate_default_p" THEN
|
jbe@255
|
2520 "simulate_v" := TRUE;
|
jbe@255
|
2521 ELSE
|
jbe@255
|
2522 "simulate_v" := FALSE;
|
jbe@255
|
2523 END IF;
|
jbe@97
|
2524 IF
|
jbe@97
|
2525 "unit_id_p" NOTNULL AND
|
jbe@97
|
2526 "area_id_p" ISNULL AND
|
jbe@97
|
2527 "issue_id_p" ISNULL
|
jbe@97
|
2528 THEN
|
jbe@97
|
2529 "scope_v" := 'unit';
|
jbe@97
|
2530 "unit_id_v" := "unit_id_p";
|
jbe@97
|
2531 ELSIF
|
jbe@97
|
2532 "unit_id_p" ISNULL AND
|
jbe@97
|
2533 "area_id_p" NOTNULL AND
|
jbe@97
|
2534 "issue_id_p" ISNULL
|
jbe@97
|
2535 THEN
|
jbe@97
|
2536 "scope_v" := 'area';
|
jbe@97
|
2537 "area_id_v" := "area_id_p";
|
jbe@97
|
2538 SELECT "unit_id" INTO "unit_id_v"
|
jbe@97
|
2539 FROM "area" WHERE "id" = "area_id_v";
|
jbe@97
|
2540 ELSIF
|
jbe@97
|
2541 "unit_id_p" ISNULL AND
|
jbe@97
|
2542 "area_id_p" ISNULL AND
|
jbe@97
|
2543 "issue_id_p" NOTNULL
|
jbe@97
|
2544 THEN
|
jbe@242
|
2545 SELECT INTO "issue_row" * FROM "issue" WHERE "id" = "issue_id_p";
|
jbe@242
|
2546 IF "issue_row"."id" ISNULL THEN
|
jbe@242
|
2547 RETURN;
|
jbe@242
|
2548 END IF;
|
jbe@242
|
2549 IF "issue_row"."closed" NOTNULL THEN
|
jbe@255
|
2550 IF "simulate_v" THEN
|
jbe@242
|
2551 RAISE EXCEPTION 'Tried to simulate delegation chain for closed issue.';
|
jbe@242
|
2552 END IF;
|
jbe@242
|
2553 FOR "output_row" IN
|
jbe@242
|
2554 SELECT * FROM
|
jbe@242
|
2555 "delegation_chain_for_closed_issue"("member_id_p", "issue_id_p")
|
jbe@242
|
2556 LOOP
|
jbe@242
|
2557 RETURN NEXT "output_row";
|
jbe@242
|
2558 END LOOP;
|
jbe@242
|
2559 RETURN;
|
jbe@242
|
2560 END IF;
|
jbe@97
|
2561 "scope_v" := 'issue';
|
jbe@97
|
2562 SELECT "area_id" INTO "area_id_v"
|
jbe@97
|
2563 FROM "issue" WHERE "id" = "issue_id_p";
|
jbe@97
|
2564 SELECT "unit_id" INTO "unit_id_v"
|
jbe@97
|
2565 FROM "area" WHERE "id" = "area_id_v";
|
jbe@97
|
2566 ELSE
|
jbe@97
|
2567 RAISE EXCEPTION 'Exactly one of unit_id_p, area_id_p, or issue_id_p must be NOTNULL.';
|
jbe@97
|
2568 END IF;
|
jbe@5
|
2569 "visited_member_ids" := '{}';
|
jbe@5
|
2570 "loop_member_id_v" := NULL;
|
jbe@5
|
2571 "output_rows" := '{}';
|
jbe@5
|
2572 "output_row"."index" := 0;
|
jbe@5
|
2573 "output_row"."member_id" := "member_id_p";
|
jbe@97
|
2574 "output_row"."member_valid" := TRUE;
|
jbe@5
|
2575 "output_row"."participation" := FALSE;
|
jbe@5
|
2576 "output_row"."overridden" := FALSE;
|
jbe@86
|
2577 "output_row"."disabled_out" := FALSE;
|
jbe@5
|
2578 "output_row"."scope_out" := NULL;
|
jbe@5
|
2579 LOOP
|
jbe@5
|
2580 IF "visited_member_ids" @> ARRAY["output_row"."member_id"] THEN
|
jbe@5
|
2581 "loop_member_id_v" := "output_row"."member_id";
|
jbe@5
|
2582 ELSE
|
jbe@5
|
2583 "visited_member_ids" :=
|
jbe@5
|
2584 "visited_member_ids" || "output_row"."member_id";
|
jbe@5
|
2585 END IF;
|
jbe@241
|
2586 IF "output_row"."participation" ISNULL THEN
|
jbe@241
|
2587 "output_row"."overridden" := NULL;
|
jbe@241
|
2588 ELSIF "output_row"."participation" THEN
|
jbe@5
|
2589 "output_row"."overridden" := TRUE;
|
jbe@5
|
2590 END IF;
|
jbe@5
|
2591 "output_row"."scope_in" := "output_row"."scope_out";
|
jbe@255
|
2592 "output_row"."member_valid" := EXISTS (
|
jbe@97
|
2593 SELECT NULL FROM "member" JOIN "privilege"
|
jbe@97
|
2594 ON "privilege"."member_id" = "member"."id"
|
jbe@97
|
2595 AND "privilege"."unit_id" = "unit_id_v"
|
jbe@97
|
2596 WHERE "id" = "output_row"."member_id"
|
jbe@97
|
2597 AND "member"."active" AND "privilege"."voting_right"
|
jbe@255
|
2598 );
|
jbe@255
|
2599 "simulate_here_v" := (
|
jbe@255
|
2600 "simulate_v" AND
|
jbe@255
|
2601 "output_row"."member_id" = "member_id_p"
|
jbe@255
|
2602 );
|
jbe@255
|
2603 "delegation_row" := ROW(NULL);
|
jbe@255
|
2604 IF "output_row"."member_valid" OR "simulate_here_v" THEN
|
jbe@97
|
2605 IF "scope_v" = 'unit' THEN
|
jbe@255
|
2606 IF NOT "simulate_here_v" THEN
|
jbe@255
|
2607 SELECT * INTO "delegation_row" FROM "delegation"
|
jbe@255
|
2608 WHERE "truster_id" = "output_row"."member_id"
|
jbe@255
|
2609 AND "unit_id" = "unit_id_v";
|
jbe@255
|
2610 END IF;
|
jbe@97
|
2611 ELSIF "scope_v" = 'area' THEN
|
jbe@5
|
2612 "output_row"."participation" := EXISTS (
|
jbe@5
|
2613 SELECT NULL FROM "membership"
|
jbe@5
|
2614 WHERE "area_id" = "area_id_p"
|
jbe@5
|
2615 AND "member_id" = "output_row"."member_id"
|
jbe@5
|
2616 );
|
jbe@255
|
2617 IF "simulate_here_v" THEN
|
jbe@255
|
2618 IF "simulate_trustee_id_p" ISNULL THEN
|
jbe@255
|
2619 SELECT * INTO "delegation_row" FROM "delegation"
|
jbe@255
|
2620 WHERE "truster_id" = "output_row"."member_id"
|
jbe@255
|
2621 AND "unit_id" = "unit_id_v";
|
jbe@255
|
2622 END IF;
|
jbe@255
|
2623 ELSE
|
jbe@255
|
2624 SELECT * INTO "delegation_row" FROM "delegation"
|
jbe@255
|
2625 WHERE "truster_id" = "output_row"."member_id"
|
jbe@255
|
2626 AND (
|
jbe@255
|
2627 "unit_id" = "unit_id_v" OR
|
jbe@255
|
2628 "area_id" = "area_id_v"
|
jbe@255
|
2629 )
|
jbe@255
|
2630 ORDER BY "scope" DESC;
|
jbe@255
|
2631 END IF;
|
jbe@97
|
2632 ELSIF "scope_v" = 'issue' THEN
|
jbe@241
|
2633 IF "issue_row"."fully_frozen" ISNULL THEN
|
jbe@241
|
2634 "output_row"."participation" := EXISTS (
|
jbe@241
|
2635 SELECT NULL FROM "interest"
|
jbe@241
|
2636 WHERE "issue_id" = "issue_id_p"
|
jbe@241
|
2637 AND "member_id" = "output_row"."member_id"
|
jbe@241
|
2638 );
|
jbe@241
|
2639 ELSE
|
jbe@241
|
2640 IF "output_row"."member_id" = "member_id_p" THEN
|
jbe@241
|
2641 "output_row"."participation" := EXISTS (
|
jbe@241
|
2642 SELECT NULL FROM "direct_voter"
|
jbe@241
|
2643 WHERE "issue_id" = "issue_id_p"
|
jbe@241
|
2644 AND "member_id" = "output_row"."member_id"
|
jbe@241
|
2645 );
|
jbe@241
|
2646 ELSE
|
jbe@241
|
2647 "output_row"."participation" := NULL;
|
jbe@241
|
2648 END IF;
|
jbe@241
|
2649 END IF;
|
jbe@255
|
2650 IF "simulate_here_v" THEN
|
jbe@255
|
2651 IF "simulate_trustee_id_p" ISNULL THEN
|
jbe@255
|
2652 SELECT * INTO "delegation_row" FROM "delegation"
|
jbe@255
|
2653 WHERE "truster_id" = "output_row"."member_id"
|
jbe@255
|
2654 AND (
|
jbe@255
|
2655 "unit_id" = "unit_id_v" OR
|
jbe@255
|
2656 "area_id" = "area_id_v"
|
jbe@255
|
2657 )
|
jbe@255
|
2658 ORDER BY "scope" DESC;
|
jbe@255
|
2659 END IF;
|
jbe@255
|
2660 ELSE
|
jbe@255
|
2661 SELECT * INTO "delegation_row" FROM "delegation"
|
jbe@255
|
2662 WHERE "truster_id" = "output_row"."member_id"
|
jbe@255
|
2663 AND (
|
jbe@255
|
2664 "unit_id" = "unit_id_v" OR
|
jbe@255
|
2665 "area_id" = "area_id_v" OR
|
jbe@255
|
2666 "issue_id" = "issue_id_p"
|
jbe@255
|
2667 )
|
jbe@255
|
2668 ORDER BY "scope" DESC;
|
jbe@255
|
2669 END IF;
|
jbe@5
|
2670 END IF;
|
jbe@5
|
2671 ELSE
|
jbe@5
|
2672 "output_row"."participation" := FALSE;
|
jbe@5
|
2673 END IF;
|
jbe@255
|
2674 IF "simulate_here_v" AND "simulate_trustee_id_p" NOTNULL THEN
|
jbe@97
|
2675 "output_row"."scope_out" := "scope_v";
|
jbe@5
|
2676 "output_rows" := "output_rows" || "output_row";
|
jbe@5
|
2677 "output_row"."member_id" := "simulate_trustee_id_p";
|
jbe@5
|
2678 ELSIF "delegation_row"."trustee_id" NOTNULL THEN
|
jbe@10
|
2679 "output_row"."scope_out" := "delegation_row"."scope";
|
jbe@5
|
2680 "output_rows" := "output_rows" || "output_row";
|
jbe@5
|
2681 "output_row"."member_id" := "delegation_row"."trustee_id";
|
jbe@86
|
2682 ELSIF "delegation_row"."scope" NOTNULL THEN
|
jbe@86
|
2683 "output_row"."scope_out" := "delegation_row"."scope";
|
jbe@86
|
2684 "output_row"."disabled_out" := TRUE;
|
jbe@86
|
2685 "output_rows" := "output_rows" || "output_row";
|
jbe@86
|
2686 EXIT;
|
jbe@5
|
2687 ELSE
|
jbe@5
|
2688 "output_row"."scope_out" := NULL;
|
jbe@5
|
2689 "output_rows" := "output_rows" || "output_row";
|
jbe@5
|
2690 EXIT;
|
jbe@5
|
2691 END IF;
|
jbe@5
|
2692 EXIT WHEN "loop_member_id_v" NOTNULL;
|
jbe@5
|
2693 "output_row"."index" := "output_row"."index" + 1;
|
jbe@5
|
2694 END LOOP;
|
jbe@5
|
2695 "row_count" := array_upper("output_rows", 1);
|
jbe@5
|
2696 "i" := 1;
|
jbe@5
|
2697 "loop_v" := FALSE;
|
jbe@5
|
2698 LOOP
|
jbe@5
|
2699 "output_row" := "output_rows"["i"];
|
jbe@98
|
2700 EXIT WHEN "output_row" ISNULL; -- NOTE: ISNULL and NOT ... NOTNULL produce different results!
|
jbe@5
|
2701 IF "loop_v" THEN
|
jbe@5
|
2702 IF "i" + 1 = "row_count" THEN
|
jbe@5
|
2703 "output_row"."loop" := 'last';
|
jbe@5
|
2704 ELSIF "i" = "row_count" THEN
|
jbe@5
|
2705 "output_row"."loop" := 'repetition';
|
jbe@5
|
2706 ELSE
|
jbe@5
|
2707 "output_row"."loop" := 'intermediate';
|
jbe@5
|
2708 END IF;
|
jbe@5
|
2709 ELSIF "output_row"."member_id" = "loop_member_id_v" THEN
|
jbe@5
|
2710 "output_row"."loop" := 'first';
|
jbe@5
|
2711 "loop_v" := TRUE;
|
jbe@5
|
2712 END IF;
|
jbe@97
|
2713 IF "scope_v" = 'unit' THEN
|
jbe@5
|
2714 "output_row"."participation" := NULL;
|
jbe@5
|
2715 END IF;
|
jbe@5
|
2716 RETURN NEXT "output_row";
|
jbe@5
|
2717 "i" := "i" + 1;
|
jbe@5
|
2718 END LOOP;
|
jbe@5
|
2719 RETURN;
|
jbe@5
|
2720 END;
|
jbe@5
|
2721 $$;
|
jbe@5
|
2722
|
jbe@5
|
2723 COMMENT ON FUNCTION "delegation_chain"
|
jbe@5
|
2724 ( "member"."id"%TYPE,
|
jbe@97
|
2725 "unit"."id"%TYPE,
|
jbe@5
|
2726 "area"."id"%TYPE,
|
jbe@5
|
2727 "issue"."id"%TYPE,
|
jbe@255
|
2728 "member"."id"%TYPE,
|
jbe@255
|
2729 BOOLEAN )
|
jbe@242
|
2730 IS 'Shows a delegation chain for unit, area, or issue; See "delegation_chain_row" type for more information';
|
jbe@242
|
2731
|
jbe@242
|
2732
|
jbe@242
|
2733
|
jbe@242
|
2734 ---------------------------------------------------------
|
jbe@242
|
2735 -- Single row returning function for delegation chains --
|
jbe@242
|
2736 ---------------------------------------------------------
|
jbe@242
|
2737
|
jbe@242
|
2738
|
jbe@242
|
2739 CREATE TYPE "delegation_info_loop_type" AS ENUM
|
jbe@242
|
2740 ('own', 'first', 'first_ellipsis', 'other', 'other_ellipsis');
|
jbe@240
|
2741
|
jbe@243
|
2742 COMMENT ON TYPE "delegation_info_loop_type" IS 'Type of "delegation_loop" in "delegation_info_type"; ''own'' means loop to self, ''first'' means loop to first trustee, ''first_ellipsis'' means loop to ellipsis after first trustee, ''other'' means loop to other trustee, ''other_ellipsis'' means loop to ellipsis after other trustee''';
|
jbe@243
|
2743
|
jbe@243
|
2744
|
jbe@240
|
2745 CREATE TYPE "delegation_info_type" AS (
|
jbe@242
|
2746 "own_participation" BOOLEAN,
|
jbe@242
|
2747 "own_delegation_scope" "delegation_scope",
|
jbe@242
|
2748 "first_trustee_id" INT4,
|
jbe@240
|
2749 "first_trustee_participation" BOOLEAN,
|
jbe@242
|
2750 "first_trustee_ellipsis" BOOLEAN,
|
jbe@242
|
2751 "other_trustee_id" INT4,
|
jbe@240
|
2752 "other_trustee_participation" BOOLEAN,
|
jbe@242
|
2753 "other_trustee_ellipsis" BOOLEAN,
|
jbe@253
|
2754 "delegation_loop" "delegation_info_loop_type",
|
jbe@253
|
2755 "participating_member_id" INT4 );
|
jbe@240
|
2756
|
jbe@243
|
2757 COMMENT ON TYPE "delegation_info_type" IS 'Type of result returned by "delegation_info" function; For meaning of "participation" check comment on "delegation_chain_row" type';
|
jbe@243
|
2758
|
jbe@243
|
2759 COMMENT ON COLUMN "delegation_info_type"."own_participation" IS 'Member is directly participating';
|
jbe@243
|
2760 COMMENT ON COLUMN "delegation_info_type"."own_delegation_scope" IS 'Delegation scope of member';
|
jbe@243
|
2761 COMMENT ON COLUMN "delegation_info_type"."first_trustee_id" IS 'Direct trustee of member';
|
jbe@243
|
2762 COMMENT ON COLUMN "delegation_info_type"."first_trustee_participation" IS 'Direct trustee of member is participating';
|
jbe@243
|
2763 COMMENT ON COLUMN "delegation_info_type"."first_trustee_ellipsis" IS 'Ellipsis in delegation chain after "first_trustee"';
|
jbe@243
|
2764 COMMENT ON COLUMN "delegation_info_type"."other_trustee_id" IS 'Another relevant trustee (due to participation)';
|
jbe@243
|
2765 COMMENT ON COLUMN "delegation_info_type"."other_trustee_participation" IS 'Another trustee is participating (redundant field: if "other_trustee_id" is set, then "other_trustee_participation" is always TRUE, else "other_trustee_participation" is NULL)';
|
jbe@243
|
2766 COMMENT ON COLUMN "delegation_info_type"."other_trustee_ellipsis" IS 'Ellipsis in delegation chain after "other_trustee"';
|
jbe@243
|
2767 COMMENT ON COLUMN "delegation_info_type"."delegation_loop" IS 'Non-NULL value, if delegation chain contains a circle; See comment on "delegation_info_loop_type" for details';
|
jbe@253
|
2768 COMMENT ON COLUMN "delegation_info_type"."participating_member_id" IS 'First participating member in delegation chain';
|
jbe@243
|
2769
|
jbe@243
|
2770
|
jbe@240
|
2771 CREATE FUNCTION "delegation_info"
|
jbe@242
|
2772 ( "member_id_p" "member"."id"%TYPE,
|
jbe@242
|
2773 "unit_id_p" "unit"."id"%TYPE,
|
jbe@242
|
2774 "area_id_p" "area"."id"%TYPE,
|
jbe@242
|
2775 "issue_id_p" "issue"."id"%TYPE,
|
jbe@255
|
2776 "simulate_trustee_id_p" "member"."id"%TYPE DEFAULT NULL,
|
jbe@255
|
2777 "simulate_default_p" BOOLEAN DEFAULT FALSE )
|
jbe@240
|
2778 RETURNS "delegation_info_type"
|
jbe@240
|
2779 LANGUAGE 'plpgsql' STABLE AS $$
|
jbe@240
|
2780 DECLARE
|
jbe@242
|
2781 "current_row" "delegation_chain_row";
|
jbe@242
|
2782 "result" "delegation_info_type";
|
jbe@240
|
2783 BEGIN
|
jbe@242
|
2784 "result"."own_participation" := FALSE;
|
jbe@242
|
2785 FOR "current_row" IN
|
jbe@242
|
2786 SELECT * FROM "delegation_chain"(
|
jbe@242
|
2787 "member_id_p",
|
jbe@242
|
2788 "unit_id_p", "area_id_p", "issue_id_p",
|
jbe@255
|
2789 "simulate_trustee_id_p", "simulate_default_p")
|
jbe@242
|
2790 LOOP
|
jbe@253
|
2791 IF
|
jbe@253
|
2792 "result"."participating_member_id" ISNULL AND
|
jbe@253
|
2793 "current_row"."participation"
|
jbe@253
|
2794 THEN
|
jbe@253
|
2795 "result"."participating_member_id" := "current_row"."member_id";
|
jbe@253
|
2796 END IF;
|
jbe@242
|
2797 IF "current_row"."member_id" = "member_id_p" THEN
|
jbe@242
|
2798 "result"."own_participation" := "current_row"."participation";
|
jbe@242
|
2799 "result"."own_delegation_scope" := "current_row"."scope_out";
|
jbe@242
|
2800 IF "current_row"."loop" = 'first' THEN
|
jbe@242
|
2801 "result"."delegation_loop" := 'own';
|
jbe@242
|
2802 END IF;
|
jbe@242
|
2803 ELSIF
|
jbe@242
|
2804 "current_row"."member_valid" AND
|
jbe@242
|
2805 ( "current_row"."loop" ISNULL OR
|
jbe@242
|
2806 "current_row"."loop" != 'repetition' )
|
jbe@242
|
2807 THEN
|
jbe@242
|
2808 IF "result"."first_trustee_id" ISNULL THEN
|
jbe@242
|
2809 "result"."first_trustee_id" := "current_row"."member_id";
|
jbe@242
|
2810 "result"."first_trustee_participation" := "current_row"."participation";
|
jbe@242
|
2811 "result"."first_trustee_ellipsis" := FALSE;
|
jbe@242
|
2812 IF "current_row"."loop" = 'first' THEN
|
jbe@242
|
2813 "result"."delegation_loop" := 'first';
|
jbe@242
|
2814 END IF;
|
jbe@242
|
2815 ELSIF "result"."other_trustee_id" ISNULL THEN
|
jbe@247
|
2816 IF "current_row"."participation" AND NOT "current_row"."overridden" THEN
|
jbe@242
|
2817 "result"."other_trustee_id" := "current_row"."member_id";
|
jbe@242
|
2818 "result"."other_trustee_participation" := TRUE;
|
jbe@242
|
2819 "result"."other_trustee_ellipsis" := FALSE;
|
jbe@242
|
2820 IF "current_row"."loop" = 'first' THEN
|
jbe@242
|
2821 "result"."delegation_loop" := 'other';
|
jbe@240
|
2822 END IF;
|
jbe@240
|
2823 ELSE
|
jbe@242
|
2824 "result"."first_trustee_ellipsis" := TRUE;
|
jbe@242
|
2825 IF "current_row"."loop" = 'first' THEN
|
jbe@242
|
2826 "result"."delegation_loop" := 'first_ellipsis';
|
jbe@242
|
2827 END IF;
|
jbe@242
|
2828 END IF;
|
jbe@242
|
2829 ELSE
|
jbe@242
|
2830 "result"."other_trustee_ellipsis" := TRUE;
|
jbe@242
|
2831 IF "current_row"."loop" = 'first' THEN
|
jbe@242
|
2832 "result"."delegation_loop" := 'other_ellipsis';
|
jbe@240
|
2833 END IF;
|
jbe@240
|
2834 END IF;
|
jbe@240
|
2835 END IF;
|
jbe@242
|
2836 END LOOP;
|
jbe@240
|
2837 RETURN "result";
|
jbe@240
|
2838 END;
|
jbe@240
|
2839 $$;
|
jbe@240
|
2840
|
jbe@243
|
2841 COMMENT ON FUNCTION "delegation_info"
|
jbe@243
|
2842 ( "member"."id"%TYPE,
|
jbe@243
|
2843 "unit"."id"%TYPE,
|
jbe@243
|
2844 "area"."id"%TYPE,
|
jbe@243
|
2845 "issue"."id"%TYPE,
|
jbe@255
|
2846 "member"."id"%TYPE,
|
jbe@255
|
2847 BOOLEAN )
|
jbe@243
|
2848 IS 'Notable information about a delegation chain for unit, area, or issue; See "delegation_info_type" for more information';
|
jbe@243
|
2849
|
jbe@240
|
2850
|
jbe@240
|
2851
|
jbe@240
|
2852 ------------------------------
|
jbe@0
|
2853 -- Comparison by vote count --
|
jbe@0
|
2854 ------------------------------
|
jbe@0
|
2855
|
jbe@0
|
2856 CREATE FUNCTION "vote_ratio"
|
jbe@0
|
2857 ( "positive_votes_p" "initiative"."positive_votes"%TYPE,
|
jbe@0
|
2858 "negative_votes_p" "initiative"."negative_votes"%TYPE )
|
jbe@0
|
2859 RETURNS FLOAT8
|
jbe@0
|
2860 LANGUAGE 'plpgsql' STABLE AS $$
|
jbe@0
|
2861 BEGIN
|
jbe@30
|
2862 IF "positive_votes_p" > 0 AND "negative_votes_p" > 0 THEN
|
jbe@30
|
2863 RETURN
|
jbe@30
|
2864 "positive_votes_p"::FLOAT8 /
|
jbe@30
|
2865 ("positive_votes_p" + "negative_votes_p")::FLOAT8;
|
jbe@30
|
2866 ELSIF "positive_votes_p" > 0 THEN
|
jbe@30
|
2867 RETURN "positive_votes_p";
|
jbe@30
|
2868 ELSIF "negative_votes_p" > 0 THEN
|
jbe@30
|
2869 RETURN 1 - "negative_votes_p";
|
jbe@0
|
2870 ELSE
|
jbe@0
|
2871 RETURN 0.5;
|
jbe@0
|
2872 END IF;
|
jbe@0
|
2873 END;
|
jbe@0
|
2874 $$;
|
jbe@0
|
2875
|
jbe@0
|
2876 COMMENT ON FUNCTION "vote_ratio"
|
jbe@0
|
2877 ( "initiative"."positive_votes"%TYPE,
|
jbe@0
|
2878 "initiative"."negative_votes"%TYPE )
|
jbe@30
|
2879 IS 'Returns a number, which can be used for comparison of initiatives based on count of approvals and disapprovals. Greater numbers indicate a better result. This function is NOT injective.';
|
jbe@0
|
2880
|
jbe@0
|
2881
|
jbe@0
|
2882
|
jbe@0
|
2883 ------------------------------------------------
|
jbe@0
|
2884 -- Locking for snapshots and voting procedure --
|
jbe@0
|
2885 ------------------------------------------------
|
jbe@0
|
2886
|
jbe@67
|
2887
|
jbe@67
|
2888 CREATE FUNCTION "share_row_lock_issue_trigger"()
|
jbe@67
|
2889 RETURNS TRIGGER
|
jbe@67
|
2890 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@67
|
2891 BEGIN
|
jbe@67
|
2892 IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN
|
jbe@67
|
2893 PERFORM NULL FROM "issue" WHERE "id" = OLD."issue_id" FOR SHARE;
|
jbe@67
|
2894 END IF;
|
jbe@67
|
2895 IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
|
jbe@67
|
2896 PERFORM NULL FROM "issue" WHERE "id" = NEW."issue_id" FOR SHARE;
|
jbe@67
|
2897 RETURN NEW;
|
jbe@67
|
2898 ELSE
|
jbe@67
|
2899 RETURN OLD;
|
jbe@67
|
2900 END IF;
|
jbe@67
|
2901 END;
|
jbe@67
|
2902 $$;
|
jbe@67
|
2903
|
jbe@67
|
2904 COMMENT ON FUNCTION "share_row_lock_issue_trigger"() IS 'Implementation of triggers "share_row_lock_issue" on multiple tables';
|
jbe@67
|
2905
|
jbe@67
|
2906
|
jbe@67
|
2907 CREATE FUNCTION "share_row_lock_issue_via_initiative_trigger"()
|
jbe@67
|
2908 RETURNS TRIGGER
|
jbe@0
|
2909 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
2910 BEGIN
|
jbe@67
|
2911 IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN
|
jbe@67
|
2912 PERFORM NULL FROM "issue"
|
jbe@67
|
2913 JOIN "initiative" ON "issue"."id" = "initiative"."issue_id"
|
jbe@67
|
2914 WHERE "initiative"."id" = OLD."initiative_id"
|
jbe@67
|
2915 FOR SHARE OF "issue";
|
jbe@67
|
2916 END IF;
|
jbe@67
|
2917 IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
|
jbe@67
|
2918 PERFORM NULL FROM "issue"
|
jbe@67
|
2919 JOIN "initiative" ON "issue"."id" = "initiative"."issue_id"
|
jbe@67
|
2920 WHERE "initiative"."id" = NEW."initiative_id"
|
jbe@67
|
2921 FOR SHARE OF "issue";
|
jbe@67
|
2922 RETURN NEW;
|
jbe@67
|
2923 ELSE
|
jbe@67
|
2924 RETURN OLD;
|
jbe@67
|
2925 END IF;
|
jbe@67
|
2926 END;
|
jbe@67
|
2927 $$;
|
jbe@67
|
2928
|
jbe@67
|
2929 COMMENT ON FUNCTION "share_row_lock_issue_trigger"() IS 'Implementation of trigger "share_row_lock_issue_via_initiative" on table "opinion"';
|
jbe@67
|
2930
|
jbe@67
|
2931
|
jbe@67
|
2932 CREATE TRIGGER "share_row_lock_issue"
|
jbe@67
|
2933 BEFORE INSERT OR UPDATE OR DELETE ON "initiative"
|
jbe@67
|
2934 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@67
|
2935 "share_row_lock_issue_trigger"();
|
jbe@67
|
2936
|
jbe@67
|
2937 CREATE TRIGGER "share_row_lock_issue"
|
jbe@67
|
2938 BEFORE INSERT OR UPDATE OR DELETE ON "interest"
|
jbe@67
|
2939 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@67
|
2940 "share_row_lock_issue_trigger"();
|
jbe@67
|
2941
|
jbe@67
|
2942 CREATE TRIGGER "share_row_lock_issue"
|
jbe@67
|
2943 BEFORE INSERT OR UPDATE OR DELETE ON "supporter"
|
jbe@67
|
2944 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@67
|
2945 "share_row_lock_issue_trigger"();
|
jbe@67
|
2946
|
jbe@67
|
2947 CREATE TRIGGER "share_row_lock_issue_via_initiative"
|
jbe@67
|
2948 BEFORE INSERT OR UPDATE OR DELETE ON "opinion"
|
jbe@67
|
2949 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@67
|
2950 "share_row_lock_issue_via_initiative_trigger"();
|
jbe@67
|
2951
|
jbe@67
|
2952 CREATE TRIGGER "share_row_lock_issue"
|
jbe@67
|
2953 BEFORE INSERT OR UPDATE OR DELETE ON "direct_voter"
|
jbe@67
|
2954 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@67
|
2955 "share_row_lock_issue_trigger"();
|
jbe@67
|
2956
|
jbe@67
|
2957 CREATE TRIGGER "share_row_lock_issue"
|
jbe@67
|
2958 BEFORE INSERT OR UPDATE OR DELETE ON "delegating_voter"
|
jbe@67
|
2959 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@67
|
2960 "share_row_lock_issue_trigger"();
|
jbe@67
|
2961
|
jbe@67
|
2962 CREATE TRIGGER "share_row_lock_issue"
|
jbe@67
|
2963 BEFORE INSERT OR UPDATE OR DELETE ON "vote"
|
jbe@67
|
2964 FOR EACH ROW EXECUTE PROCEDURE
|
jbe@67
|
2965 "share_row_lock_issue_trigger"();
|
jbe@67
|
2966
|
jbe@67
|
2967 COMMENT ON TRIGGER "share_row_lock_issue" ON "initiative" IS 'See "lock_issue" function';
|
jbe@67
|
2968 COMMENT ON TRIGGER "share_row_lock_issue" ON "interest" IS 'See "lock_issue" function';
|
jbe@67
|
2969 COMMENT ON TRIGGER "share_row_lock_issue" ON "supporter" IS 'See "lock_issue" function';
|
jbe@67
|
2970 COMMENT ON TRIGGER "share_row_lock_issue_via_initiative" ON "opinion" IS 'See "lock_issue" function';
|
jbe@67
|
2971 COMMENT ON TRIGGER "share_row_lock_issue" ON "direct_voter" IS 'See "lock_issue" function';
|
jbe@67
|
2972 COMMENT ON TRIGGER "share_row_lock_issue" ON "delegating_voter" IS 'See "lock_issue" function';
|
jbe@67
|
2973 COMMENT ON TRIGGER "share_row_lock_issue" ON "vote" IS 'See "lock_issue" function';
|
jbe@67
|
2974
|
jbe@67
|
2975
|
jbe@67
|
2976 CREATE FUNCTION "lock_issue"
|
jbe@67
|
2977 ( "issue_id_p" "issue"."id"%TYPE )
|
jbe@67
|
2978 RETURNS VOID
|
jbe@67
|
2979 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@67
|
2980 BEGIN
|
jbe@67
|
2981 LOCK TABLE "member" IN SHARE MODE;
|
jbe@97
|
2982 LOCK TABLE "privilege" IN SHARE MODE;
|
jbe@67
|
2983 LOCK TABLE "membership" IN SHARE MODE;
|
jbe@67
|
2984 LOCK TABLE "policy" IN SHARE MODE;
|
jbe@67
|
2985 PERFORM NULL FROM "issue" WHERE "id" = "issue_id_p" FOR UPDATE;
|
jbe@67
|
2986 -- NOTE: The row-level exclusive lock in combination with the
|
jbe@67
|
2987 -- share_row_lock_issue(_via_initiative)_trigger functions (which
|
jbe@67
|
2988 -- acquire a row-level share lock on the issue) ensure that no data
|
jbe@67
|
2989 -- is changed, which could affect calculation of snapshots or
|
jbe@67
|
2990 -- counting of votes. Table "delegation" must be table-level-locked,
|
jbe@67
|
2991 -- as it also contains issue- and global-scope delegations.
|
jbe@67
|
2992 LOCK TABLE "delegation" IN SHARE MODE;
|
jbe@0
|
2993 LOCK TABLE "direct_population_snapshot" IN EXCLUSIVE MODE;
|
jbe@0
|
2994 LOCK TABLE "delegating_population_snapshot" IN EXCLUSIVE MODE;
|
jbe@0
|
2995 LOCK TABLE "direct_interest_snapshot" IN EXCLUSIVE MODE;
|
jbe@0
|
2996 LOCK TABLE "delegating_interest_snapshot" IN EXCLUSIVE MODE;
|
jbe@0
|
2997 LOCK TABLE "direct_supporter_snapshot" IN EXCLUSIVE MODE;
|
jbe@0
|
2998 RETURN;
|
jbe@0
|
2999 END;
|
jbe@0
|
3000 $$;
|
jbe@0
|
3001
|
jbe@67
|
3002 COMMENT ON FUNCTION "lock_issue"
|
jbe@67
|
3003 ( "issue"."id"%TYPE )
|
jbe@67
|
3004 IS 'Locks the issue and all other data which is used for calculating snapshots or counting votes.';
|
jbe@0
|
3005
|
jbe@0
|
3006
|
jbe@0
|
3007
|
jbe@103
|
3008 ------------------------------------------------------------------------
|
jbe@103
|
3009 -- Regular tasks, except calculcation of snapshots and voting results --
|
jbe@103
|
3010 ------------------------------------------------------------------------
|
jbe@103
|
3011
|
jbe@184
|
3012 CREATE FUNCTION "check_activity"()
|
jbe@103
|
3013 RETURNS VOID
|
jbe@103
|
3014 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@104
|
3015 DECLARE
|
jbe@104
|
3016 "system_setting_row" "system_setting"%ROWTYPE;
|
jbe@103
|
3017 BEGIN
|
jbe@104
|
3018 SELECT * INTO "system_setting_row" FROM "system_setting";
|
jbe@103
|
3019 LOCK TABLE "member" IN SHARE ROW EXCLUSIVE MODE;
|
jbe@104
|
3020 IF "system_setting_row"."member_ttl" NOTNULL THEN
|
jbe@104
|
3021 UPDATE "member" SET "active" = FALSE
|
jbe@104
|
3022 WHERE "active" = TRUE
|
jbe@184
|
3023 AND "last_activity" < (now() - "system_setting_row"."member_ttl")::DATE;
|
jbe@104
|
3024 END IF;
|
jbe@103
|
3025 RETURN;
|
jbe@103
|
3026 END;
|
jbe@103
|
3027 $$;
|
jbe@103
|
3028
|
jbe@184
|
3029 COMMENT ON FUNCTION "check_activity"() IS 'Deactivates members when "last_activity" is older than "system_setting"."member_ttl".';
|
jbe@103
|
3030
|
jbe@4
|
3031
|
jbe@4
|
3032 CREATE FUNCTION "calculate_member_counts"()
|
jbe@4
|
3033 RETURNS VOID
|
jbe@4
|
3034 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@4
|
3035 BEGIN
|
jbe@67
|
3036 LOCK TABLE "member" IN SHARE MODE;
|
jbe@67
|
3037 LOCK TABLE "member_count" IN EXCLUSIVE MODE;
|
jbe@97
|
3038 LOCK TABLE "unit" IN EXCLUSIVE MODE;
|
jbe@67
|
3039 LOCK TABLE "area" IN EXCLUSIVE MODE;
|
jbe@97
|
3040 LOCK TABLE "privilege" IN SHARE MODE;
|
jbe@67
|
3041 LOCK TABLE "membership" IN SHARE MODE;
|
jbe@4
|
3042 DELETE FROM "member_count";
|
jbe@5
|
3043 INSERT INTO "member_count" ("total_count")
|
jbe@5
|
3044 SELECT "total_count" FROM "member_count_view";
|
jbe@97
|
3045 UPDATE "unit" SET "member_count" = "view"."member_count"
|
jbe@97
|
3046 FROM "unit_member_count" AS "view"
|
jbe@97
|
3047 WHERE "view"."unit_id" = "unit"."id";
|
jbe@5
|
3048 UPDATE "area" SET
|
jbe@5
|
3049 "direct_member_count" = "view"."direct_member_count",
|
jbe@169
|
3050 "member_weight" = "view"."member_weight"
|
jbe@5
|
3051 FROM "area_member_count" AS "view"
|
jbe@5
|
3052 WHERE "view"."area_id" = "area"."id";
|
jbe@4
|
3053 RETURN;
|
jbe@4
|
3054 END;
|
jbe@4
|
3055 $$;
|
jbe@4
|
3056
|
jbe@4
|
3057 COMMENT ON FUNCTION "calculate_member_counts"() IS 'Updates "member_count" table and "member_count" column of table "area" by materializing data from views "member_count_view" and "area_member_count"';
|
jbe@4
|
3058
|
jbe@4
|
3059
|
jbe@4
|
3060
|
jbe@0
|
3061 ------------------------------
|
jbe@0
|
3062 -- Calculation of snapshots --
|
jbe@0
|
3063 ------------------------------
|
jbe@0
|
3064
|
jbe@0
|
3065 CREATE FUNCTION "weight_of_added_delegations_for_population_snapshot"
|
jbe@0
|
3066 ( "issue_id_p" "issue"."id"%TYPE,
|
jbe@0
|
3067 "member_id_p" "member"."id"%TYPE,
|
jbe@0
|
3068 "delegate_member_ids_p" "delegating_population_snapshot"."delegate_member_ids"%TYPE )
|
jbe@0
|
3069 RETURNS "direct_population_snapshot"."weight"%TYPE
|
jbe@0
|
3070 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3071 DECLARE
|
jbe@0
|
3072 "issue_delegation_row" "issue_delegation"%ROWTYPE;
|
jbe@0
|
3073 "delegate_member_ids_v" "delegating_population_snapshot"."delegate_member_ids"%TYPE;
|
jbe@0
|
3074 "weight_v" INT4;
|
jbe@8
|
3075 "sub_weight_v" INT4;
|
jbe@0
|
3076 BEGIN
|
jbe@0
|
3077 "weight_v" := 0;
|
jbe@0
|
3078 FOR "issue_delegation_row" IN
|
jbe@0
|
3079 SELECT * FROM "issue_delegation"
|
jbe@0
|
3080 WHERE "trustee_id" = "member_id_p"
|
jbe@0
|
3081 AND "issue_id" = "issue_id_p"
|
jbe@0
|
3082 LOOP
|
jbe@0
|
3083 IF NOT EXISTS (
|
jbe@0
|
3084 SELECT NULL FROM "direct_population_snapshot"
|
jbe@0
|
3085 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3086 AND "event" = 'periodic'
|
jbe@0
|
3087 AND "member_id" = "issue_delegation_row"."truster_id"
|
jbe@0
|
3088 ) AND NOT EXISTS (
|
jbe@0
|
3089 SELECT NULL FROM "delegating_population_snapshot"
|
jbe@0
|
3090 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3091 AND "event" = 'periodic'
|
jbe@0
|
3092 AND "member_id" = "issue_delegation_row"."truster_id"
|
jbe@0
|
3093 ) THEN
|
jbe@0
|
3094 "delegate_member_ids_v" :=
|
jbe@0
|
3095 "member_id_p" || "delegate_member_ids_p";
|
jbe@10
|
3096 INSERT INTO "delegating_population_snapshot" (
|
jbe@10
|
3097 "issue_id",
|
jbe@10
|
3098 "event",
|
jbe@10
|
3099 "member_id",
|
jbe@10
|
3100 "scope",
|
jbe@10
|
3101 "delegate_member_ids"
|
jbe@10
|
3102 ) VALUES (
|
jbe@0
|
3103 "issue_id_p",
|
jbe@0
|
3104 'periodic',
|
jbe@0
|
3105 "issue_delegation_row"."truster_id",
|
jbe@10
|
3106 "issue_delegation_row"."scope",
|
jbe@0
|
3107 "delegate_member_ids_v"
|
jbe@0
|
3108 );
|
jbe@8
|
3109 "sub_weight_v" := 1 +
|
jbe@0
|
3110 "weight_of_added_delegations_for_population_snapshot"(
|
jbe@0
|
3111 "issue_id_p",
|
jbe@0
|
3112 "issue_delegation_row"."truster_id",
|
jbe@0
|
3113 "delegate_member_ids_v"
|
jbe@0
|
3114 );
|
jbe@8
|
3115 UPDATE "delegating_population_snapshot"
|
jbe@8
|
3116 SET "weight" = "sub_weight_v"
|
jbe@8
|
3117 WHERE "issue_id" = "issue_id_p"
|
jbe@8
|
3118 AND "event" = 'periodic'
|
jbe@8
|
3119 AND "member_id" = "issue_delegation_row"."truster_id";
|
jbe@8
|
3120 "weight_v" := "weight_v" + "sub_weight_v";
|
jbe@0
|
3121 END IF;
|
jbe@0
|
3122 END LOOP;
|
jbe@0
|
3123 RETURN "weight_v";
|
jbe@0
|
3124 END;
|
jbe@0
|
3125 $$;
|
jbe@0
|
3126
|
jbe@0
|
3127 COMMENT ON FUNCTION "weight_of_added_delegations_for_population_snapshot"
|
jbe@0
|
3128 ( "issue"."id"%TYPE,
|
jbe@0
|
3129 "member"."id"%TYPE,
|
jbe@0
|
3130 "delegating_population_snapshot"."delegate_member_ids"%TYPE )
|
jbe@0
|
3131 IS 'Helper function for "create_population_snapshot" function';
|
jbe@0
|
3132
|
jbe@0
|
3133
|
jbe@0
|
3134 CREATE FUNCTION "create_population_snapshot"
|
jbe@0
|
3135 ( "issue_id_p" "issue"."id"%TYPE )
|
jbe@0
|
3136 RETURNS VOID
|
jbe@0
|
3137 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3138 DECLARE
|
jbe@0
|
3139 "member_id_v" "member"."id"%TYPE;
|
jbe@0
|
3140 BEGIN
|
jbe@0
|
3141 DELETE FROM "direct_population_snapshot"
|
jbe@0
|
3142 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3143 AND "event" = 'periodic';
|
jbe@0
|
3144 DELETE FROM "delegating_population_snapshot"
|
jbe@0
|
3145 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3146 AND "event" = 'periodic';
|
jbe@0
|
3147 INSERT INTO "direct_population_snapshot"
|
jbe@54
|
3148 ("issue_id", "event", "member_id")
|
jbe@54
|
3149 SELECT
|
jbe@54
|
3150 "issue_id_p" AS "issue_id",
|
jbe@54
|
3151 'periodic'::"snapshot_event" AS "event",
|
jbe@54
|
3152 "member"."id" AS "member_id"
|
jbe@54
|
3153 FROM "issue"
|
jbe@54
|
3154 JOIN "area" ON "issue"."area_id" = "area"."id"
|
jbe@54
|
3155 JOIN "membership" ON "area"."id" = "membership"."area_id"
|
jbe@54
|
3156 JOIN "member" ON "membership"."member_id" = "member"."id"
|
jbe@97
|
3157 JOIN "privilege"
|
jbe@97
|
3158 ON "privilege"."unit_id" = "area"."unit_id"
|
jbe@97
|
3159 AND "privilege"."member_id" = "member"."id"
|
jbe@54
|
3160 WHERE "issue"."id" = "issue_id_p"
|
jbe@97
|
3161 AND "member"."active" AND "privilege"."voting_right"
|
jbe@54
|
3162 UNION
|
jbe@54
|
3163 SELECT
|
jbe@54
|
3164 "issue_id_p" AS "issue_id",
|
jbe@54
|
3165 'periodic'::"snapshot_event" AS "event",
|
jbe@54
|
3166 "member"."id" AS "member_id"
|
jbe@97
|
3167 FROM "issue"
|
jbe@97
|
3168 JOIN "area" ON "issue"."area_id" = "area"."id"
|
jbe@97
|
3169 JOIN "interest" ON "issue"."id" = "interest"."issue_id"
|
jbe@97
|
3170 JOIN "member" ON "interest"."member_id" = "member"."id"
|
jbe@97
|
3171 JOIN "privilege"
|
jbe@97
|
3172 ON "privilege"."unit_id" = "area"."unit_id"
|
jbe@97
|
3173 AND "privilege"."member_id" = "member"."id"
|
jbe@97
|
3174 WHERE "issue"."id" = "issue_id_p"
|
jbe@97
|
3175 AND "member"."active" AND "privilege"."voting_right";
|
jbe@0
|
3176 FOR "member_id_v" IN
|
jbe@0
|
3177 SELECT "member_id" FROM "direct_population_snapshot"
|
jbe@0
|
3178 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3179 AND "event" = 'periodic'
|
jbe@0
|
3180 LOOP
|
jbe@0
|
3181 UPDATE "direct_population_snapshot" SET
|
jbe@0
|
3182 "weight" = 1 +
|
jbe@0
|
3183 "weight_of_added_delegations_for_population_snapshot"(
|
jbe@0
|
3184 "issue_id_p",
|
jbe@0
|
3185 "member_id_v",
|
jbe@0
|
3186 '{}'
|
jbe@0
|
3187 )
|
jbe@0
|
3188 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3189 AND "event" = 'periodic'
|
jbe@0
|
3190 AND "member_id" = "member_id_v";
|
jbe@0
|
3191 END LOOP;
|
jbe@0
|
3192 RETURN;
|
jbe@0
|
3193 END;
|
jbe@0
|
3194 $$;
|
jbe@0
|
3195
|
jbe@0
|
3196 COMMENT ON FUNCTION "create_population_snapshot"
|
jbe@67
|
3197 ( "issue"."id"%TYPE )
|
jbe@0
|
3198 IS 'This function creates a new ''periodic'' population snapshot for the given issue. It does neither lock any tables, nor updates precalculated values in other tables.';
|
jbe@0
|
3199
|
jbe@0
|
3200
|
jbe@0
|
3201 CREATE FUNCTION "weight_of_added_delegations_for_interest_snapshot"
|
jbe@0
|
3202 ( "issue_id_p" "issue"."id"%TYPE,
|
jbe@0
|
3203 "member_id_p" "member"."id"%TYPE,
|
jbe@0
|
3204 "delegate_member_ids_p" "delegating_interest_snapshot"."delegate_member_ids"%TYPE )
|
jbe@0
|
3205 RETURNS "direct_interest_snapshot"."weight"%TYPE
|
jbe@0
|
3206 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3207 DECLARE
|
jbe@0
|
3208 "issue_delegation_row" "issue_delegation"%ROWTYPE;
|
jbe@0
|
3209 "delegate_member_ids_v" "delegating_interest_snapshot"."delegate_member_ids"%TYPE;
|
jbe@0
|
3210 "weight_v" INT4;
|
jbe@8
|
3211 "sub_weight_v" INT4;
|
jbe@0
|
3212 BEGIN
|
jbe@0
|
3213 "weight_v" := 0;
|
jbe@0
|
3214 FOR "issue_delegation_row" IN
|
jbe@0
|
3215 SELECT * FROM "issue_delegation"
|
jbe@0
|
3216 WHERE "trustee_id" = "member_id_p"
|
jbe@0
|
3217 AND "issue_id" = "issue_id_p"
|
jbe@0
|
3218 LOOP
|
jbe@0
|
3219 IF NOT EXISTS (
|
jbe@0
|
3220 SELECT NULL FROM "direct_interest_snapshot"
|
jbe@0
|
3221 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3222 AND "event" = 'periodic'
|
jbe@0
|
3223 AND "member_id" = "issue_delegation_row"."truster_id"
|
jbe@0
|
3224 ) AND NOT EXISTS (
|
jbe@0
|
3225 SELECT NULL FROM "delegating_interest_snapshot"
|
jbe@0
|
3226 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3227 AND "event" = 'periodic'
|
jbe@0
|
3228 AND "member_id" = "issue_delegation_row"."truster_id"
|
jbe@0
|
3229 ) THEN
|
jbe@0
|
3230 "delegate_member_ids_v" :=
|
jbe@0
|
3231 "member_id_p" || "delegate_member_ids_p";
|
jbe@10
|
3232 INSERT INTO "delegating_interest_snapshot" (
|
jbe@10
|
3233 "issue_id",
|
jbe@10
|
3234 "event",
|
jbe@10
|
3235 "member_id",
|
jbe@10
|
3236 "scope",
|
jbe@10
|
3237 "delegate_member_ids"
|
jbe@10
|
3238 ) VALUES (
|
jbe@0
|
3239 "issue_id_p",
|
jbe@0
|
3240 'periodic',
|
jbe@0
|
3241 "issue_delegation_row"."truster_id",
|
jbe@10
|
3242 "issue_delegation_row"."scope",
|
jbe@0
|
3243 "delegate_member_ids_v"
|
jbe@0
|
3244 );
|
jbe@8
|
3245 "sub_weight_v" := 1 +
|
jbe@0
|
3246 "weight_of_added_delegations_for_interest_snapshot"(
|
jbe@0
|
3247 "issue_id_p",
|
jbe@0
|
3248 "issue_delegation_row"."truster_id",
|
jbe@0
|
3249 "delegate_member_ids_v"
|
jbe@0
|
3250 );
|
jbe@8
|
3251 UPDATE "delegating_interest_snapshot"
|
jbe@8
|
3252 SET "weight" = "sub_weight_v"
|
jbe@8
|
3253 WHERE "issue_id" = "issue_id_p"
|
jbe@8
|
3254 AND "event" = 'periodic'
|
jbe@8
|
3255 AND "member_id" = "issue_delegation_row"."truster_id";
|
jbe@8
|
3256 "weight_v" := "weight_v" + "sub_weight_v";
|
jbe@0
|
3257 END IF;
|
jbe@0
|
3258 END LOOP;
|
jbe@0
|
3259 RETURN "weight_v";
|
jbe@0
|
3260 END;
|
jbe@0
|
3261 $$;
|
jbe@0
|
3262
|
jbe@0
|
3263 COMMENT ON FUNCTION "weight_of_added_delegations_for_interest_snapshot"
|
jbe@0
|
3264 ( "issue"."id"%TYPE,
|
jbe@0
|
3265 "member"."id"%TYPE,
|
jbe@0
|
3266 "delegating_interest_snapshot"."delegate_member_ids"%TYPE )
|
jbe@0
|
3267 IS 'Helper function for "create_interest_snapshot" function';
|
jbe@0
|
3268
|
jbe@0
|
3269
|
jbe@0
|
3270 CREATE FUNCTION "create_interest_snapshot"
|
jbe@0
|
3271 ( "issue_id_p" "issue"."id"%TYPE )
|
jbe@0
|
3272 RETURNS VOID
|
jbe@0
|
3273 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3274 DECLARE
|
jbe@0
|
3275 "member_id_v" "member"."id"%TYPE;
|
jbe@0
|
3276 BEGIN
|
jbe@0
|
3277 DELETE FROM "direct_interest_snapshot"
|
jbe@0
|
3278 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3279 AND "event" = 'periodic';
|
jbe@0
|
3280 DELETE FROM "delegating_interest_snapshot"
|
jbe@0
|
3281 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3282 AND "event" = 'periodic';
|
jbe@0
|
3283 DELETE FROM "direct_supporter_snapshot"
|
jbe@0
|
3284 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3285 AND "event" = 'periodic';
|
jbe@0
|
3286 INSERT INTO "direct_interest_snapshot"
|
jbe@144
|
3287 ("issue_id", "event", "member_id")
|
jbe@0
|
3288 SELECT
|
jbe@0
|
3289 "issue_id_p" AS "issue_id",
|
jbe@0
|
3290 'periodic' AS "event",
|
jbe@144
|
3291 "member"."id" AS "member_id"
|
jbe@97
|
3292 FROM "issue"
|
jbe@97
|
3293 JOIN "area" ON "issue"."area_id" = "area"."id"
|
jbe@97
|
3294 JOIN "interest" ON "issue"."id" = "interest"."issue_id"
|
jbe@97
|
3295 JOIN "member" ON "interest"."member_id" = "member"."id"
|
jbe@97
|
3296 JOIN "privilege"
|
jbe@97
|
3297 ON "privilege"."unit_id" = "area"."unit_id"
|
jbe@97
|
3298 AND "privilege"."member_id" = "member"."id"
|
jbe@97
|
3299 WHERE "issue"."id" = "issue_id_p"
|
jbe@97
|
3300 AND "member"."active" AND "privilege"."voting_right";
|
jbe@0
|
3301 FOR "member_id_v" IN
|
jbe@0
|
3302 SELECT "member_id" FROM "direct_interest_snapshot"
|
jbe@0
|
3303 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3304 AND "event" = 'periodic'
|
jbe@0
|
3305 LOOP
|
jbe@0
|
3306 UPDATE "direct_interest_snapshot" SET
|
jbe@0
|
3307 "weight" = 1 +
|
jbe@0
|
3308 "weight_of_added_delegations_for_interest_snapshot"(
|
jbe@0
|
3309 "issue_id_p",
|
jbe@0
|
3310 "member_id_v",
|
jbe@0
|
3311 '{}'
|
jbe@0
|
3312 )
|
jbe@0
|
3313 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3314 AND "event" = 'periodic'
|
jbe@0
|
3315 AND "member_id" = "member_id_v";
|
jbe@0
|
3316 END LOOP;
|
jbe@0
|
3317 INSERT INTO "direct_supporter_snapshot"
|
jbe@0
|
3318 ( "issue_id", "initiative_id", "event", "member_id",
|
jbe@204
|
3319 "draft_id", "informed", "satisfied" )
|
jbe@0
|
3320 SELECT
|
jbe@96
|
3321 "issue_id_p" AS "issue_id",
|
jbe@96
|
3322 "initiative"."id" AS "initiative_id",
|
jbe@96
|
3323 'periodic' AS "event",
|
jbe@96
|
3324 "supporter"."member_id" AS "member_id",
|
jbe@204
|
3325 "supporter"."draft_id" AS "draft_id",
|
jbe@0
|
3326 "supporter"."draft_id" = "current_draft"."id" AS "informed",
|
jbe@0
|
3327 NOT EXISTS (
|
jbe@0
|
3328 SELECT NULL FROM "critical_opinion"
|
jbe@0
|
3329 WHERE "initiative_id" = "initiative"."id"
|
jbe@96
|
3330 AND "member_id" = "supporter"."member_id"
|
jbe@0
|
3331 ) AS "satisfied"
|
jbe@96
|
3332 FROM "initiative"
|
jbe@96
|
3333 JOIN "supporter"
|
jbe@0
|
3334 ON "supporter"."initiative_id" = "initiative"."id"
|
jbe@0
|
3335 JOIN "current_draft"
|
jbe@0
|
3336 ON "initiative"."id" = "current_draft"."initiative_id"
|
jbe@0
|
3337 JOIN "direct_interest_snapshot"
|
jbe@96
|
3338 ON "supporter"."member_id" = "direct_interest_snapshot"."member_id"
|
jbe@0
|
3339 AND "initiative"."issue_id" = "direct_interest_snapshot"."issue_id"
|
jbe@3
|
3340 AND "event" = 'periodic'
|
jbe@96
|
3341 WHERE "initiative"."issue_id" = "issue_id_p";
|
jbe@0
|
3342 RETURN;
|
jbe@0
|
3343 END;
|
jbe@0
|
3344 $$;
|
jbe@0
|
3345
|
jbe@0
|
3346 COMMENT ON FUNCTION "create_interest_snapshot"
|
jbe@0
|
3347 ( "issue"."id"%TYPE )
|
jbe@0
|
3348 IS 'This function creates a new ''periodic'' interest/supporter snapshot for the given issue. It does neither lock any tables, nor updates precalculated values in other tables.';
|
jbe@0
|
3349
|
jbe@0
|
3350
|
jbe@0
|
3351 CREATE FUNCTION "create_snapshot"
|
jbe@0
|
3352 ( "issue_id_p" "issue"."id"%TYPE )
|
jbe@0
|
3353 RETURNS VOID
|
jbe@0
|
3354 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3355 DECLARE
|
jbe@0
|
3356 "initiative_id_v" "initiative"."id"%TYPE;
|
jbe@0
|
3357 "suggestion_id_v" "suggestion"."id"%TYPE;
|
jbe@0
|
3358 BEGIN
|
jbe@67
|
3359 PERFORM "lock_issue"("issue_id_p");
|
jbe@0
|
3360 PERFORM "create_population_snapshot"("issue_id_p");
|
jbe@0
|
3361 PERFORM "create_interest_snapshot"("issue_id_p");
|
jbe@0
|
3362 UPDATE "issue" SET
|
jbe@8
|
3363 "snapshot" = now(),
|
jbe@8
|
3364 "latest_snapshot_event" = 'periodic',
|
jbe@0
|
3365 "population" = (
|
jbe@0
|
3366 SELECT coalesce(sum("weight"), 0)
|
jbe@0
|
3367 FROM "direct_population_snapshot"
|
jbe@0
|
3368 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3369 AND "event" = 'periodic'
|
jbe@0
|
3370 )
|
jbe@0
|
3371 WHERE "id" = "issue_id_p";
|
jbe@0
|
3372 FOR "initiative_id_v" IN
|
jbe@0
|
3373 SELECT "id" FROM "initiative" WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3374 LOOP
|
jbe@0
|
3375 UPDATE "initiative" SET
|
jbe@0
|
3376 "supporter_count" = (
|
jbe@0
|
3377 SELECT coalesce(sum("di"."weight"), 0)
|
jbe@0
|
3378 FROM "direct_interest_snapshot" AS "di"
|
jbe@0
|
3379 JOIN "direct_supporter_snapshot" AS "ds"
|
jbe@0
|
3380 ON "di"."member_id" = "ds"."member_id"
|
jbe@0
|
3381 WHERE "di"."issue_id" = "issue_id_p"
|
jbe@0
|
3382 AND "di"."event" = 'periodic'
|
jbe@0
|
3383 AND "ds"."initiative_id" = "initiative_id_v"
|
jbe@0
|
3384 AND "ds"."event" = 'periodic'
|
jbe@0
|
3385 ),
|
jbe@0
|
3386 "informed_supporter_count" = (
|
jbe@0
|
3387 SELECT coalesce(sum("di"."weight"), 0)
|
jbe@0
|
3388 FROM "direct_interest_snapshot" AS "di"
|
jbe@0
|
3389 JOIN "direct_supporter_snapshot" AS "ds"
|
jbe@0
|
3390 ON "di"."member_id" = "ds"."member_id"
|
jbe@0
|
3391 WHERE "di"."issue_id" = "issue_id_p"
|
jbe@0
|
3392 AND "di"."event" = 'periodic'
|
jbe@0
|
3393 AND "ds"."initiative_id" = "initiative_id_v"
|
jbe@0
|
3394 AND "ds"."event" = 'periodic'
|
jbe@0
|
3395 AND "ds"."informed"
|
jbe@0
|
3396 ),
|
jbe@0
|
3397 "satisfied_supporter_count" = (
|
jbe@0
|
3398 SELECT coalesce(sum("di"."weight"), 0)
|
jbe@0
|
3399 FROM "direct_interest_snapshot" AS "di"
|
jbe@0
|
3400 JOIN "direct_supporter_snapshot" AS "ds"
|
jbe@0
|
3401 ON "di"."member_id" = "ds"."member_id"
|
jbe@0
|
3402 WHERE "di"."issue_id" = "issue_id_p"
|
jbe@0
|
3403 AND "di"."event" = 'periodic'
|
jbe@0
|
3404 AND "ds"."initiative_id" = "initiative_id_v"
|
jbe@0
|
3405 AND "ds"."event" = 'periodic'
|
jbe@0
|
3406 AND "ds"."satisfied"
|
jbe@0
|
3407 ),
|
jbe@0
|
3408 "satisfied_informed_supporter_count" = (
|
jbe@0
|
3409 SELECT coalesce(sum("di"."weight"), 0)
|
jbe@0
|
3410 FROM "direct_interest_snapshot" AS "di"
|
jbe@0
|
3411 JOIN "direct_supporter_snapshot" AS "ds"
|
jbe@0
|
3412 ON "di"."member_id" = "ds"."member_id"
|
jbe@0
|
3413 WHERE "di"."issue_id" = "issue_id_p"
|
jbe@0
|
3414 AND "di"."event" = 'periodic'
|
jbe@0
|
3415 AND "ds"."initiative_id" = "initiative_id_v"
|
jbe@0
|
3416 AND "ds"."event" = 'periodic'
|
jbe@0
|
3417 AND "ds"."informed"
|
jbe@0
|
3418 AND "ds"."satisfied"
|
jbe@0
|
3419 )
|
jbe@0
|
3420 WHERE "id" = "initiative_id_v";
|
jbe@0
|
3421 FOR "suggestion_id_v" IN
|
jbe@0
|
3422 SELECT "id" FROM "suggestion"
|
jbe@0
|
3423 WHERE "initiative_id" = "initiative_id_v"
|
jbe@0
|
3424 LOOP
|
jbe@0
|
3425 UPDATE "suggestion" SET
|
jbe@0
|
3426 "minus2_unfulfilled_count" = (
|
jbe@0
|
3427 SELECT coalesce(sum("snapshot"."weight"), 0)
|
jbe@36
|
3428 FROM "issue" CROSS JOIN "opinion"
|
jbe@36
|
3429 JOIN "direct_interest_snapshot" AS "snapshot"
|
jbe@36
|
3430 ON "snapshot"."issue_id" = "issue"."id"
|
jbe@36
|
3431 AND "snapshot"."event" = "issue"."latest_snapshot_event"
|
jbe@36
|
3432 AND "snapshot"."member_id" = "opinion"."member_id"
|
jbe@36
|
3433 WHERE "issue"."id" = "issue_id_p"
|
jbe@36
|
3434 AND "opinion"."suggestion_id" = "suggestion_id_v"
|
jbe@0
|
3435 AND "opinion"."degree" = -2
|
jbe@0
|
3436 AND "opinion"."fulfilled" = FALSE
|
jbe@0
|
3437 ),
|
jbe@0
|
3438 "minus2_fulfilled_count" = (
|
jbe@0
|
3439 SELECT coalesce(sum("snapshot"."weight"), 0)
|
jbe@36
|
3440 FROM "issue" CROSS JOIN "opinion"
|
jbe@36
|
3441 JOIN "direct_interest_snapshot" AS "snapshot"
|
jbe@36
|
3442 ON "snapshot"."issue_id" = "issue"."id"
|
jbe@36
|
3443 AND "snapshot"."event" = "issue"."latest_snapshot_event"
|
jbe@36
|
3444 AND "snapshot"."member_id" = "opinion"."member_id"
|
jbe@36
|
3445 WHERE "issue"."id" = "issue_id_p"
|
jbe@36
|
3446 AND "opinion"."suggestion_id" = "suggestion_id_v"
|
jbe@0
|
3447 AND "opinion"."degree" = -2
|
jbe@0
|
3448 AND "opinion"."fulfilled" = TRUE
|
jbe@0
|
3449 ),
|
jbe@0
|
3450 "minus1_unfulfilled_count" = (
|
jbe@0
|
3451 SELECT coalesce(sum("snapshot"."weight"), 0)
|
jbe@36
|
3452 FROM "issue" CROSS JOIN "opinion"
|
jbe@36
|
3453 JOIN "direct_interest_snapshot" AS "snapshot"
|
jbe@36
|
3454 ON "snapshot"."issue_id" = "issue"."id"
|
jbe@36
|
3455 AND "snapshot"."event" = "issue"."latest_snapshot_event"
|
jbe@36
|
3456 AND "snapshot"."member_id" = "opinion"."member_id"
|
jbe@36
|
3457 WHERE "issue"."id" = "issue_id_p"
|
jbe@36
|
3458 AND "opinion"."suggestion_id" = "suggestion_id_v"
|
jbe@0
|
3459 AND "opinion"."degree" = -1
|
jbe@0
|
3460 AND "opinion"."fulfilled" = FALSE
|
jbe@0
|
3461 ),
|
jbe@0
|
3462 "minus1_fulfilled_count" = (
|
jbe@0
|
3463 SELECT coalesce(sum("snapshot"."weight"), 0)
|
jbe@36
|
3464 FROM "issue" CROSS JOIN "opinion"
|
jbe@36
|
3465 JOIN "direct_interest_snapshot" AS "snapshot"
|
jbe@36
|
3466 ON "snapshot"."issue_id" = "issue"."id"
|
jbe@36
|
3467 AND "snapshot"."event" = "issue"."latest_snapshot_event"
|
jbe@36
|
3468 AND "snapshot"."member_id" = "opinion"."member_id"
|
jbe@36
|
3469 WHERE "issue"."id" = "issue_id_p"
|
jbe@36
|
3470 AND "opinion"."suggestion_id" = "suggestion_id_v"
|
jbe@0
|
3471 AND "opinion"."degree" = -1
|
jbe@0
|
3472 AND "opinion"."fulfilled" = TRUE
|
jbe@0
|
3473 ),
|
jbe@0
|
3474 "plus1_unfulfilled_count" = (
|
jbe@0
|
3475 SELECT coalesce(sum("snapshot"."weight"), 0)
|
jbe@36
|
3476 FROM "issue" CROSS JOIN "opinion"
|
jbe@36
|
3477 JOIN "direct_interest_snapshot" AS "snapshot"
|
jbe@36
|
3478 ON "snapshot"."issue_id" = "issue"."id"
|
jbe@36
|
3479 AND "snapshot"."event" = "issue"."latest_snapshot_event"
|
jbe@36
|
3480 AND "snapshot"."member_id" = "opinion"."member_id"
|
jbe@36
|
3481 WHERE "issue"."id" = "issue_id_p"
|
jbe@36
|
3482 AND "opinion"."suggestion_id" = "suggestion_id_v"
|
jbe@0
|
3483 AND "opinion"."degree" = 1
|
jbe@0
|
3484 AND "opinion"."fulfilled" = FALSE
|
jbe@0
|
3485 ),
|
jbe@0
|
3486 "plus1_fulfilled_count" = (
|
jbe@0
|
3487 SELECT coalesce(sum("snapshot"."weight"), 0)
|
jbe@36
|
3488 FROM "issue" CROSS JOIN "opinion"
|
jbe@36
|
3489 JOIN "direct_interest_snapshot" AS "snapshot"
|
jbe@36
|
3490 ON "snapshot"."issue_id" = "issue"."id"
|
jbe@36
|
3491 AND "snapshot"."event" = "issue"."latest_snapshot_event"
|
jbe@36
|
3492 AND "snapshot"."member_id" = "opinion"."member_id"
|
jbe@36
|
3493 WHERE "issue"."id" = "issue_id_p"
|
jbe@36
|
3494 AND "opinion"."suggestion_id" = "suggestion_id_v"
|
jbe@0
|
3495 AND "opinion"."degree" = 1
|
jbe@0
|
3496 AND "opinion"."fulfilled" = TRUE
|
jbe@0
|
3497 ),
|
jbe@0
|
3498 "plus2_unfulfilled_count" = (
|
jbe@0
|
3499 SELECT coalesce(sum("snapshot"."weight"), 0)
|
jbe@36
|
3500 FROM "issue" CROSS JOIN "opinion"
|
jbe@36
|
3501 JOIN "direct_interest_snapshot" AS "snapshot"
|
jbe@36
|
3502 ON "snapshot"."issue_id" = "issue"."id"
|
jbe@36
|
3503 AND "snapshot"."event" = "issue"."latest_snapshot_event"
|
jbe@36
|
3504 AND "snapshot"."member_id" = "opinion"."member_id"
|
jbe@36
|
3505 WHERE "issue"."id" = "issue_id_p"
|
jbe@36
|
3506 AND "opinion"."suggestion_id" = "suggestion_id_v"
|
jbe@0
|
3507 AND "opinion"."degree" = 2
|
jbe@0
|
3508 AND "opinion"."fulfilled" = FALSE
|
jbe@0
|
3509 ),
|
jbe@0
|
3510 "plus2_fulfilled_count" = (
|
jbe@0
|
3511 SELECT coalesce(sum("snapshot"."weight"), 0)
|
jbe@36
|
3512 FROM "issue" CROSS JOIN "opinion"
|
jbe@36
|
3513 JOIN "direct_interest_snapshot" AS "snapshot"
|
jbe@36
|
3514 ON "snapshot"."issue_id" = "issue"."id"
|
jbe@36
|
3515 AND "snapshot"."event" = "issue"."latest_snapshot_event"
|
jbe@36
|
3516 AND "snapshot"."member_id" = "opinion"."member_id"
|
jbe@36
|
3517 WHERE "issue"."id" = "issue_id_p"
|
jbe@36
|
3518 AND "opinion"."suggestion_id" = "suggestion_id_v"
|
jbe@0
|
3519 AND "opinion"."degree" = 2
|
jbe@0
|
3520 AND "opinion"."fulfilled" = TRUE
|
jbe@0
|
3521 )
|
jbe@0
|
3522 WHERE "suggestion"."id" = "suggestion_id_v";
|
jbe@0
|
3523 END LOOP;
|
jbe@0
|
3524 END LOOP;
|
jbe@0
|
3525 RETURN;
|
jbe@0
|
3526 END;
|
jbe@0
|
3527 $$;
|
jbe@0
|
3528
|
jbe@0
|
3529 COMMENT ON FUNCTION "create_snapshot"
|
jbe@0
|
3530 ( "issue"."id"%TYPE )
|
jbe@0
|
3531 IS 'This function creates a complete new ''periodic'' snapshot of population, interest and support for the given issue. All involved tables are locked, and after completion precalculated values in the source tables are updated.';
|
jbe@0
|
3532
|
jbe@0
|
3533
|
jbe@0
|
3534 CREATE FUNCTION "set_snapshot_event"
|
jbe@0
|
3535 ( "issue_id_p" "issue"."id"%TYPE,
|
jbe@0
|
3536 "event_p" "snapshot_event" )
|
jbe@0
|
3537 RETURNS VOID
|
jbe@0
|
3538 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@21
|
3539 DECLARE
|
jbe@21
|
3540 "event_v" "issue"."latest_snapshot_event"%TYPE;
|
jbe@0
|
3541 BEGIN
|
jbe@21
|
3542 SELECT "latest_snapshot_event" INTO "event_v" FROM "issue"
|
jbe@21
|
3543 WHERE "id" = "issue_id_p" FOR UPDATE;
|
jbe@8
|
3544 UPDATE "issue" SET "latest_snapshot_event" = "event_p"
|
jbe@8
|
3545 WHERE "id" = "issue_id_p";
|
jbe@3
|
3546 UPDATE "direct_population_snapshot" SET "event" = "event_p"
|
jbe@21
|
3547 WHERE "issue_id" = "issue_id_p" AND "event" = "event_v";
|
jbe@3
|
3548 UPDATE "delegating_population_snapshot" SET "event" = "event_p"
|
jbe@21
|
3549 WHERE "issue_id" = "issue_id_p" AND "event" = "event_v";
|
jbe@3
|
3550 UPDATE "direct_interest_snapshot" SET "event" = "event_p"
|
jbe@21
|
3551 WHERE "issue_id" = "issue_id_p" AND "event" = "event_v";
|
jbe@3
|
3552 UPDATE "delegating_interest_snapshot" SET "event" = "event_p"
|
jbe@21
|
3553 WHERE "issue_id" = "issue_id_p" AND "event" = "event_v";
|
jbe@3
|
3554 UPDATE "direct_supporter_snapshot" SET "event" = "event_p"
|
jbe@21
|
3555 WHERE "issue_id" = "issue_id_p" AND "event" = "event_v";
|
jbe@0
|
3556 RETURN;
|
jbe@0
|
3557 END;
|
jbe@0
|
3558 $$;
|
jbe@0
|
3559
|
jbe@0
|
3560 COMMENT ON FUNCTION "set_snapshot_event"
|
jbe@0
|
3561 ( "issue"."id"%TYPE,
|
jbe@0
|
3562 "snapshot_event" )
|
jbe@0
|
3563 IS 'Change "event" attribute of the previous ''periodic'' snapshot';
|
jbe@0
|
3564
|
jbe@0
|
3565
|
jbe@0
|
3566
|
jbe@0
|
3567 ---------------------
|
jbe@0
|
3568 -- Freezing issues --
|
jbe@0
|
3569 ---------------------
|
jbe@0
|
3570
|
jbe@0
|
3571 CREATE FUNCTION "freeze_after_snapshot"
|
jbe@0
|
3572 ( "issue_id_p" "issue"."id"%TYPE )
|
jbe@0
|
3573 RETURNS VOID
|
jbe@0
|
3574 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3575 DECLARE
|
jbe@0
|
3576 "issue_row" "issue"%ROWTYPE;
|
jbe@0
|
3577 "policy_row" "policy"%ROWTYPE;
|
jbe@0
|
3578 "initiative_row" "initiative"%ROWTYPE;
|
jbe@0
|
3579 BEGIN
|
jbe@0
|
3580 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p";
|
jbe@0
|
3581 SELECT * INTO "policy_row"
|
jbe@0
|
3582 FROM "policy" WHERE "id" = "issue_row"."policy_id";
|
jbe@21
|
3583 PERFORM "set_snapshot_event"("issue_id_p", 'full_freeze');
|
jbe@0
|
3584 FOR "initiative_row" IN
|
jbe@15
|
3585 SELECT * FROM "initiative"
|
jbe@15
|
3586 WHERE "issue_id" = "issue_id_p" AND "revoked" ISNULL
|
jbe@0
|
3587 LOOP
|
jbe@0
|
3588 IF
|
jbe@261
|
3589 "initiative_row"."polling" OR (
|
jbe@261
|
3590 "initiative_row"."satisfied_supporter_count" > 0 AND
|
jbe@261
|
3591 "initiative_row"."satisfied_supporter_count" *
|
jbe@261
|
3592 "policy_row"."initiative_quorum_den" >=
|
jbe@261
|
3593 "issue_row"."population" * "policy_row"."initiative_quorum_num"
|
jbe@261
|
3594 )
|
jbe@0
|
3595 THEN
|
jbe@0
|
3596 UPDATE "initiative" SET "admitted" = TRUE
|
jbe@0
|
3597 WHERE "id" = "initiative_row"."id";
|
jbe@0
|
3598 ELSE
|
jbe@0
|
3599 UPDATE "initiative" SET "admitted" = FALSE
|
jbe@0
|
3600 WHERE "id" = "initiative_row"."id";
|
jbe@0
|
3601 END IF;
|
jbe@0
|
3602 END LOOP;
|
jbe@113
|
3603 IF EXISTS (
|
jbe@9
|
3604 SELECT NULL FROM "initiative"
|
jbe@9
|
3605 WHERE "issue_id" = "issue_id_p" AND "admitted" = TRUE
|
jbe@9
|
3606 ) THEN
|
jbe@113
|
3607 UPDATE "issue" SET
|
jbe@113
|
3608 "state" = 'voting',
|
jbe@113
|
3609 "accepted" = coalesce("accepted", now()),
|
jbe@113
|
3610 "half_frozen" = coalesce("half_frozen", now()),
|
jbe@113
|
3611 "fully_frozen" = now()
|
jbe@113
|
3612 WHERE "id" = "issue_id_p";
|
jbe@113
|
3613 ELSE
|
jbe@113
|
3614 UPDATE "issue" SET
|
jbe@121
|
3615 "state" = 'canceled_no_initiative_admitted',
|
jbe@121
|
3616 "accepted" = coalesce("accepted", now()),
|
jbe@121
|
3617 "half_frozen" = coalesce("half_frozen", now()),
|
jbe@121
|
3618 "fully_frozen" = now(),
|
jbe@121
|
3619 "closed" = now(),
|
jbe@121
|
3620 "ranks_available" = TRUE
|
jbe@113
|
3621 WHERE "id" = "issue_id_p";
|
jbe@113
|
3622 -- NOTE: The following DELETE statements have effect only when
|
jbe@113
|
3623 -- issue state has been manipulated
|
jbe@113
|
3624 DELETE FROM "direct_voter" WHERE "issue_id" = "issue_id_p";
|
jbe@113
|
3625 DELETE FROM "delegating_voter" WHERE "issue_id" = "issue_id_p";
|
jbe@113
|
3626 DELETE FROM "battle" WHERE "issue_id" = "issue_id_p";
|
jbe@9
|
3627 END IF;
|
jbe@0
|
3628 RETURN;
|
jbe@0
|
3629 END;
|
jbe@0
|
3630 $$;
|
jbe@0
|
3631
|
jbe@0
|
3632 COMMENT ON FUNCTION "freeze_after_snapshot"
|
jbe@0
|
3633 ( "issue"."id"%TYPE )
|
jbe@9
|
3634 IS 'This function freezes an issue (fully) and starts voting, but must only be called when "create_snapshot" was called in the same transaction.';
|
jbe@0
|
3635
|
jbe@0
|
3636
|
jbe@0
|
3637 CREATE FUNCTION "manual_freeze"("issue_id_p" "issue"."id"%TYPE)
|
jbe@0
|
3638 RETURNS VOID
|
jbe@0
|
3639 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3640 DECLARE
|
jbe@0
|
3641 "issue_row" "issue"%ROWTYPE;
|
jbe@0
|
3642 BEGIN
|
jbe@0
|
3643 PERFORM "create_snapshot"("issue_id_p");
|
jbe@0
|
3644 PERFORM "freeze_after_snapshot"("issue_id_p");
|
jbe@0
|
3645 RETURN;
|
jbe@0
|
3646 END;
|
jbe@0
|
3647 $$;
|
jbe@0
|
3648
|
jbe@55
|
3649 COMMENT ON FUNCTION "manual_freeze"
|
jbe@0
|
3650 ( "issue"."id"%TYPE )
|
jbe@3
|
3651 IS 'Freeze an issue manually (fully) and start voting';
|
jbe@0
|
3652
|
jbe@0
|
3653
|
jbe@0
|
3654
|
jbe@0
|
3655 -----------------------
|
jbe@0
|
3656 -- Counting of votes --
|
jbe@0
|
3657 -----------------------
|
jbe@0
|
3658
|
jbe@0
|
3659
|
jbe@5
|
3660 CREATE FUNCTION "weight_of_added_vote_delegations"
|
jbe@0
|
3661 ( "issue_id_p" "issue"."id"%TYPE,
|
jbe@0
|
3662 "member_id_p" "member"."id"%TYPE,
|
jbe@0
|
3663 "delegate_member_ids_p" "delegating_voter"."delegate_member_ids"%TYPE )
|
jbe@0
|
3664 RETURNS "direct_voter"."weight"%TYPE
|
jbe@0
|
3665 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3666 DECLARE
|
jbe@0
|
3667 "issue_delegation_row" "issue_delegation"%ROWTYPE;
|
jbe@0
|
3668 "delegate_member_ids_v" "delegating_voter"."delegate_member_ids"%TYPE;
|
jbe@0
|
3669 "weight_v" INT4;
|
jbe@8
|
3670 "sub_weight_v" INT4;
|
jbe@0
|
3671 BEGIN
|
jbe@0
|
3672 "weight_v" := 0;
|
jbe@0
|
3673 FOR "issue_delegation_row" IN
|
jbe@0
|
3674 SELECT * FROM "issue_delegation"
|
jbe@0
|
3675 WHERE "trustee_id" = "member_id_p"
|
jbe@0
|
3676 AND "issue_id" = "issue_id_p"
|
jbe@0
|
3677 LOOP
|
jbe@0
|
3678 IF NOT EXISTS (
|
jbe@0
|
3679 SELECT NULL FROM "direct_voter"
|
jbe@0
|
3680 WHERE "member_id" = "issue_delegation_row"."truster_id"
|
jbe@0
|
3681 AND "issue_id" = "issue_id_p"
|
jbe@0
|
3682 ) AND NOT EXISTS (
|
jbe@0
|
3683 SELECT NULL FROM "delegating_voter"
|
jbe@0
|
3684 WHERE "member_id" = "issue_delegation_row"."truster_id"
|
jbe@0
|
3685 AND "issue_id" = "issue_id_p"
|
jbe@0
|
3686 ) THEN
|
jbe@0
|
3687 "delegate_member_ids_v" :=
|
jbe@0
|
3688 "member_id_p" || "delegate_member_ids_p";
|
jbe@10
|
3689 INSERT INTO "delegating_voter" (
|
jbe@10
|
3690 "issue_id",
|
jbe@10
|
3691 "member_id",
|
jbe@10
|
3692 "scope",
|
jbe@10
|
3693 "delegate_member_ids"
|
jbe@10
|
3694 ) VALUES (
|
jbe@5
|
3695 "issue_id_p",
|
jbe@5
|
3696 "issue_delegation_row"."truster_id",
|
jbe@10
|
3697 "issue_delegation_row"."scope",
|
jbe@5
|
3698 "delegate_member_ids_v"
|
jbe@5
|
3699 );
|
jbe@8
|
3700 "sub_weight_v" := 1 +
|
jbe@8
|
3701 "weight_of_added_vote_delegations"(
|
jbe@8
|
3702 "issue_id_p",
|
jbe@8
|
3703 "issue_delegation_row"."truster_id",
|
jbe@8
|
3704 "delegate_member_ids_v"
|
jbe@8
|
3705 );
|
jbe@8
|
3706 UPDATE "delegating_voter"
|
jbe@8
|
3707 SET "weight" = "sub_weight_v"
|
jbe@8
|
3708 WHERE "issue_id" = "issue_id_p"
|
jbe@8
|
3709 AND "member_id" = "issue_delegation_row"."truster_id";
|
jbe@8
|
3710 "weight_v" := "weight_v" + "sub_weight_v";
|
jbe@0
|
3711 END IF;
|
jbe@0
|
3712 END LOOP;
|
jbe@0
|
3713 RETURN "weight_v";
|
jbe@0
|
3714 END;
|
jbe@0
|
3715 $$;
|
jbe@0
|
3716
|
jbe@5
|
3717 COMMENT ON FUNCTION "weight_of_added_vote_delegations"
|
jbe@0
|
3718 ( "issue"."id"%TYPE,
|
jbe@0
|
3719 "member"."id"%TYPE,
|
jbe@0
|
3720 "delegating_voter"."delegate_member_ids"%TYPE )
|
jbe@0
|
3721 IS 'Helper function for "add_vote_delegations" function';
|
jbe@0
|
3722
|
jbe@0
|
3723
|
jbe@0
|
3724 CREATE FUNCTION "add_vote_delegations"
|
jbe@0
|
3725 ( "issue_id_p" "issue"."id"%TYPE )
|
jbe@0
|
3726 RETURNS VOID
|
jbe@0
|
3727 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3728 DECLARE
|
jbe@0
|
3729 "member_id_v" "member"."id"%TYPE;
|
jbe@0
|
3730 BEGIN
|
jbe@0
|
3731 FOR "member_id_v" IN
|
jbe@0
|
3732 SELECT "member_id" FROM "direct_voter"
|
jbe@0
|
3733 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
3734 LOOP
|
jbe@0
|
3735 UPDATE "direct_voter" SET
|
jbe@5
|
3736 "weight" = "weight" + "weight_of_added_vote_delegations"(
|
jbe@0
|
3737 "issue_id_p",
|
jbe@0
|
3738 "member_id_v",
|
jbe@0
|
3739 '{}'
|
jbe@0
|
3740 )
|
jbe@0
|
3741 WHERE "member_id" = "member_id_v"
|
jbe@0
|
3742 AND "issue_id" = "issue_id_p";
|
jbe@0
|
3743 END LOOP;
|
jbe@0
|
3744 RETURN;
|
jbe@0
|
3745 END;
|
jbe@0
|
3746 $$;
|
jbe@0
|
3747
|
jbe@0
|
3748 COMMENT ON FUNCTION "add_vote_delegations"
|
jbe@0
|
3749 ( "issue_id_p" "issue"."id"%TYPE )
|
jbe@0
|
3750 IS 'Helper function for "close_voting" function';
|
jbe@0
|
3751
|
jbe@0
|
3752
|
jbe@0
|
3753 CREATE FUNCTION "close_voting"("issue_id_p" "issue"."id"%TYPE)
|
jbe@0
|
3754 RETURNS VOID
|
jbe@0
|
3755 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3756 DECLARE
|
jbe@97
|
3757 "area_id_v" "area"."id"%TYPE;
|
jbe@97
|
3758 "unit_id_v" "unit"."id"%TYPE;
|
jbe@0
|
3759 "member_id_v" "member"."id"%TYPE;
|
jbe@0
|
3760 BEGIN
|
jbe@67
|
3761 PERFORM "lock_issue"("issue_id_p");
|
jbe@129
|
3762 SELECT "area_id" INTO "area_id_v" FROM "issue" WHERE "id" = "issue_id_p";
|
jbe@129
|
3763 SELECT "unit_id" INTO "unit_id_v" FROM "area" WHERE "id" = "area_id_v";
|
jbe@285
|
3764 -- delete timestamp of voting comment:
|
jbe@285
|
3765 UPDATE "direct_voter" SET "comment_changed" = NULL
|
jbe@285
|
3766 WHERE "issue_id" = "issue_id_p";
|
jbe@169
|
3767 -- delete delegating votes (in cases of manual reset of issue state):
|
jbe@0
|
3768 DELETE FROM "delegating_voter"
|
jbe@0
|
3769 WHERE "issue_id" = "issue_id_p";
|
jbe@169
|
3770 -- delete votes from non-privileged voters:
|
jbe@97
|
3771 DELETE FROM "direct_voter"
|
jbe@97
|
3772 USING (
|
jbe@97
|
3773 SELECT
|
jbe@97
|
3774 "direct_voter"."member_id"
|
jbe@97
|
3775 FROM "direct_voter"
|
jbe@97
|
3776 JOIN "member" ON "direct_voter"."member_id" = "member"."id"
|
jbe@97
|
3777 LEFT JOIN "privilege"
|
jbe@97
|
3778 ON "privilege"."unit_id" = "unit_id_v"
|
jbe@97
|
3779 AND "privilege"."member_id" = "direct_voter"."member_id"
|
jbe@97
|
3780 WHERE "direct_voter"."issue_id" = "issue_id_p" AND (
|
jbe@97
|
3781 "member"."active" = FALSE OR
|
jbe@97
|
3782 "privilege"."voting_right" ISNULL OR
|
jbe@97
|
3783 "privilege"."voting_right" = FALSE
|
jbe@97
|
3784 )
|
jbe@97
|
3785 ) AS "subquery"
|
jbe@97
|
3786 WHERE "direct_voter"."issue_id" = "issue_id_p"
|
jbe@97
|
3787 AND "direct_voter"."member_id" = "subquery"."member_id";
|
jbe@169
|
3788 -- consider delegations:
|
jbe@0
|
3789 UPDATE "direct_voter" SET "weight" = 1
|
jbe@0
|
3790 WHERE "issue_id" = "issue_id_p";
|
jbe@0
|
3791 PERFORM "add_vote_delegations"("issue_id_p");
|
jbe@137
|
3792 -- set voter count and mark issue as being calculated:
|
jbe@4
|
3793 UPDATE "issue" SET
|
jbe@111
|
3794 "state" = 'calculation',
|
jbe@61
|
3795 "closed" = now(),
|
jbe@4
|
3796 "voter_count" = (
|
jbe@4
|
3797 SELECT coalesce(sum("weight"), 0)
|
jbe@4
|
3798 FROM "direct_voter" WHERE "issue_id" = "issue_id_p"
|
jbe@6
|
3799 )
|
jbe@6
|
3800 WHERE "id" = "issue_id_p";
|
jbe@137
|
3801 -- materialize battle_view:
|
jbe@61
|
3802 -- NOTE: "closed" column of issue must be set at this point
|
jbe@61
|
3803 DELETE FROM "battle" WHERE "issue_id" = "issue_id_p";
|
jbe@61
|
3804 INSERT INTO "battle" (
|
jbe@61
|
3805 "issue_id",
|
jbe@61
|
3806 "winning_initiative_id", "losing_initiative_id",
|
jbe@61
|
3807 "count"
|
jbe@61
|
3808 ) SELECT
|
jbe@61
|
3809 "issue_id",
|
jbe@61
|
3810 "winning_initiative_id", "losing_initiative_id",
|
jbe@61
|
3811 "count"
|
jbe@61
|
3812 FROM "battle_view" WHERE "issue_id" = "issue_id_p";
|
jbe@155
|
3813 -- copy "positive_votes" and "negative_votes" from "battle" table:
|
jbe@155
|
3814 UPDATE "initiative" SET
|
jbe@155
|
3815 "positive_votes" = "battle_win"."count",
|
jbe@155
|
3816 "negative_votes" = "battle_lose"."count"
|
jbe@155
|
3817 FROM "battle" AS "battle_win", "battle" AS "battle_lose"
|
jbe@155
|
3818 WHERE
|
jbe@155
|
3819 "battle_win"."issue_id" = "issue_id_p" AND
|
jbe@155
|
3820 "battle_win"."winning_initiative_id" = "initiative"."id" AND
|
jbe@155
|
3821 "battle_win"."losing_initiative_id" ISNULL AND
|
jbe@155
|
3822 "battle_lose"."issue_id" = "issue_id_p" AND
|
jbe@155
|
3823 "battle_lose"."losing_initiative_id" = "initiative"."id" AND
|
jbe@155
|
3824 "battle_lose"."winning_initiative_id" ISNULL;
|
jbe@0
|
3825 END;
|
jbe@0
|
3826 $$;
|
jbe@0
|
3827
|
jbe@0
|
3828 COMMENT ON FUNCTION "close_voting"
|
jbe@0
|
3829 ( "issue"."id"%TYPE )
|
jbe@0
|
3830 IS 'Closes the voting on an issue, and calculates positive and negative votes for each initiative; The ranking is not calculated yet, to keep the (locking) transaction short.';
|
jbe@0
|
3831
|
jbe@0
|
3832
|
jbe@30
|
3833 CREATE FUNCTION "defeat_strength"
|
jbe@30
|
3834 ( "positive_votes_p" INT4, "negative_votes_p" INT4 )
|
jbe@30
|
3835 RETURNS INT8
|
jbe@30
|
3836 LANGUAGE 'plpgsql' IMMUTABLE AS $$
|
jbe@30
|
3837 BEGIN
|
jbe@30
|
3838 IF "positive_votes_p" > "negative_votes_p" THEN
|
jbe@30
|
3839 RETURN ("positive_votes_p"::INT8 << 31) - "negative_votes_p"::INT8;
|
jbe@30
|
3840 ELSIF "positive_votes_p" = "negative_votes_p" THEN
|
jbe@30
|
3841 RETURN 0;
|
jbe@30
|
3842 ELSE
|
jbe@30
|
3843 RETURN -1;
|
jbe@30
|
3844 END IF;
|
jbe@30
|
3845 END;
|
jbe@30
|
3846 $$;
|
jbe@30
|
3847
|
jbe@30
|
3848 COMMENT ON FUNCTION "defeat_strength"(INT4, INT4) IS 'Calculates defeat strength (INT8!) of a pairwise defeat primarily by the absolute number of votes for the winner and secondarily by the absolute number of votes for the loser';
|
jbe@30
|
3849
|
jbe@30
|
3850
|
jbe@0
|
3851 CREATE FUNCTION "calculate_ranks"("issue_id_p" "issue"."id"%TYPE)
|
jbe@0
|
3852 RETURNS VOID
|
jbe@0
|
3853 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
3854 DECLARE
|
jbe@155
|
3855 "issue_row" "issue"%ROWTYPE;
|
jbe@155
|
3856 "policy_row" "policy"%ROWTYPE;
|
jbe@134
|
3857 "dimension_v" INTEGER;
|
jbe@134
|
3858 "vote_matrix" INT4[][]; -- absolute votes
|
jbe@134
|
3859 "matrix" INT8[][]; -- defeat strength / best paths
|
jbe@134
|
3860 "i" INTEGER;
|
jbe@134
|
3861 "j" INTEGER;
|
jbe@134
|
3862 "k" INTEGER;
|
jbe@134
|
3863 "battle_row" "battle"%ROWTYPE;
|
jbe@134
|
3864 "rank_ary" INT4[];
|
jbe@134
|
3865 "rank_v" INT4;
|
jbe@134
|
3866 "done_v" INTEGER;
|
jbe@134
|
3867 "winners_ary" INTEGER[];
|
jbe@134
|
3868 "initiative_id_v" "initiative"."id"%TYPE;
|
jbe@0
|
3869 BEGIN
|
jbe@155
|
3870 SELECT * INTO "issue_row"
|
jbe@155
|
3871 FROM "issue" WHERE "id" = "issue_id_p"
|
jbe@155
|
3872 FOR UPDATE;
|
jbe@155
|
3873 SELECT * INTO "policy_row"
|
jbe@155
|
3874 FROM "policy" WHERE "id" = "issue_row"."policy_id";
|
jbe@126
|
3875 SELECT count(1) INTO "dimension_v"
|
jbe@126
|
3876 FROM "battle_participant" WHERE "issue_id" = "issue_id_p";
|
jbe@170
|
3877 -- Create "vote_matrix" with absolute number of votes in pairwise
|
jbe@170
|
3878 -- comparison:
|
jbe@170
|
3879 "vote_matrix" := array_fill(NULL::INT4, ARRAY["dimension_v", "dimension_v"]);
|
jbe@170
|
3880 "i" := 1;
|
jbe@170
|
3881 "j" := 2;
|
jbe@170
|
3882 FOR "battle_row" IN
|
jbe@170
|
3883 SELECT * FROM "battle" WHERE "issue_id" = "issue_id_p"
|
jbe@170
|
3884 ORDER BY
|
jbe@170
|
3885 "winning_initiative_id" NULLS LAST,
|
jbe@170
|
3886 "losing_initiative_id" NULLS LAST
|
jbe@170
|
3887 LOOP
|
jbe@170
|
3888 "vote_matrix"["i"]["j"] := "battle_row"."count";
|
jbe@170
|
3889 IF "j" = "dimension_v" THEN
|
jbe@170
|
3890 "i" := "i" + 1;
|
jbe@170
|
3891 "j" := 1;
|
jbe@170
|
3892 ELSE
|
jbe@170
|
3893 "j" := "j" + 1;
|
jbe@170
|
3894 IF "j" = "i" THEN
|
jbe@170
|
3895 "j" := "j" + 1;
|
jbe@170
|
3896 END IF;
|
jbe@170
|
3897 END IF;
|
jbe@170
|
3898 END LOOP;
|
jbe@170
|
3899 IF "i" != "dimension_v" OR "j" != "dimension_v" + 1 THEN
|
jbe@170
|
3900 RAISE EXCEPTION 'Wrong battle count (should not happen)';
|
jbe@170
|
3901 END IF;
|
jbe@170
|
3902 -- Store defeat strengths in "matrix" using "defeat_strength"
|
jbe@170
|
3903 -- function:
|
jbe@170
|
3904 "matrix" := array_fill(NULL::INT8, ARRAY["dimension_v", "dimension_v"]);
|
jbe@170
|
3905 "i" := 1;
|
jbe@170
|
3906 LOOP
|
jbe@170
|
3907 "j" := 1;
|
jbe@0
|
3908 LOOP
|
jbe@170
|
3909 IF "i" != "j" THEN
|
jbe@170
|
3910 "matrix"["i"]["j"] := "defeat_strength"(
|
jbe@170
|
3911 "vote_matrix"["i"]["j"],
|
jbe@170
|
3912 "vote_matrix"["j"]["i"]
|
jbe@170
|
3913 );
|
jbe@0
|
3914 END IF;
|
jbe@170
|
3915 EXIT WHEN "j" = "dimension_v";
|
jbe@170
|
3916 "j" := "j" + 1;
|
jbe@0
|
3917 END LOOP;
|
jbe@170
|
3918 EXIT WHEN "i" = "dimension_v";
|
jbe@170
|
3919 "i" := "i" + 1;
|
jbe@170
|
3920 END LOOP;
|
jbe@170
|
3921 -- Find best paths:
|
jbe@170
|
3922 "i" := 1;
|
jbe@170
|
3923 LOOP
|
jbe@170
|
3924 "j" := 1;
|
jbe@170
|
3925 LOOP
|
jbe@170
|
3926 IF "i" != "j" THEN
|
jbe@170
|
3927 "k" := 1;
|
jbe@170
|
3928 LOOP
|
jbe@170
|
3929 IF "i" != "k" AND "j" != "k" THEN
|
jbe@170
|
3930 IF "matrix"["j"]["i"] < "matrix"["i"]["k"] THEN
|
jbe@170
|
3931 IF "matrix"["j"]["i"] > "matrix"["j"]["k"] THEN
|
jbe@170
|
3932 "matrix"["j"]["k"] := "matrix"["j"]["i"];
|
jbe@170
|
3933 END IF;
|
jbe@170
|
3934 ELSE
|
jbe@170
|
3935 IF "matrix"["i"]["k"] > "matrix"["j"]["k"] THEN
|
jbe@170
|
3936 "matrix"["j"]["k"] := "matrix"["i"]["k"];
|
jbe@170
|
3937 END IF;
|
jbe@170
|
3938 END IF;
|
jbe@170
|
3939 END IF;
|
jbe@170
|
3940 EXIT WHEN "k" = "dimension_v";
|
jbe@170
|
3941 "k" := "k" + 1;
|
jbe@170
|
3942 END LOOP;
|
jbe@170
|
3943 END IF;
|
jbe@170
|
3944 EXIT WHEN "j" = "dimension_v";
|
jbe@170
|
3945 "j" := "j" + 1;
|
jbe@170
|
3946 END LOOP;
|
jbe@170
|
3947 EXIT WHEN "i" = "dimension_v";
|
jbe@170
|
3948 "i" := "i" + 1;
|
jbe@170
|
3949 END LOOP;
|
jbe@170
|
3950 -- Determine order of winners:
|
jbe@170
|
3951 "rank_ary" := array_fill(NULL::INT4, ARRAY["dimension_v"]);
|
jbe@170
|
3952 "rank_v" := 1;
|
jbe@170
|
3953 "done_v" := 0;
|
jbe@170
|
3954 LOOP
|
jbe@170
|
3955 "winners_ary" := '{}';
|
jbe@0
|
3956 "i" := 1;
|
jbe@0
|
3957 LOOP
|
jbe@170
|
3958 IF "rank_ary"["i"] ISNULL THEN
|
jbe@170
|
3959 "j" := 1;
|
jbe@170
|
3960 LOOP
|
jbe@170
|
3961 IF
|
jbe@170
|
3962 "i" != "j" AND
|
jbe@170
|
3963 "rank_ary"["j"] ISNULL AND
|
jbe@170
|
3964 "matrix"["j"]["i"] > "matrix"["i"]["j"]
|
jbe@170
|
3965 THEN
|
jbe@170
|
3966 -- someone else is better
|
jbe@170
|
3967 EXIT;
|
jbe@170
|
3968 END IF;
|
jbe@170
|
3969 IF "j" = "dimension_v" THEN
|
jbe@170
|
3970 -- noone is better
|
jbe@170
|
3971 "winners_ary" := "winners_ary" || "i";
|
jbe@170
|
3972 EXIT;
|
jbe@170
|
3973 END IF;
|
jbe@170
|
3974 "j" := "j" + 1;
|
jbe@170
|
3975 END LOOP;
|
jbe@170
|
3976 END IF;
|
jbe@30
|
3977 EXIT WHEN "i" = "dimension_v";
|
jbe@0
|
3978 "i" := "i" + 1;
|
jbe@0
|
3979 END LOOP;
|
jbe@0
|
3980 "i" := 1;
|
jbe@0
|
3981 LOOP
|
jbe@170
|
3982 "rank_ary"["winners_ary"["i"]] := "rank_v";
|
jbe@170
|
3983 "done_v" := "done_v" + 1;
|
jbe@170
|
3984 EXIT WHEN "i" = array_upper("winners_ary", 1);
|
jbe@0
|
3985 "i" := "i" + 1;
|
jbe@0
|
3986 END LOOP;
|
jbe@170
|
3987 EXIT WHEN "done_v" = "dimension_v";
|
jbe@170
|
3988 "rank_v" := "rank_v" + 1;
|
jbe@170
|
3989 END LOOP;
|
jbe@170
|
3990 -- write preliminary results:
|
jbe@170
|
3991 "i" := 1;
|
jbe@170
|
3992 FOR "initiative_id_v" IN
|
jbe@170
|
3993 SELECT "id" FROM "initiative"
|
jbe@170
|
3994 WHERE "issue_id" = "issue_id_p" AND "admitted"
|
jbe@170
|
3995 ORDER BY "id"
|
jbe@170
|
3996 LOOP
|
jbe@170
|
3997 UPDATE "initiative" SET
|
jbe@170
|
3998 "direct_majority" =
|
jbe@170
|
3999 CASE WHEN "policy_row"."direct_majority_strict" THEN
|
jbe@170
|
4000 "positive_votes" * "policy_row"."direct_majority_den" >
|
jbe@170
|
4001 "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
|
jbe@170
|
4002 ELSE
|
jbe@170
|
4003 "positive_votes" * "policy_row"."direct_majority_den" >=
|
jbe@170
|
4004 "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
|
jbe@170
|
4005 END
|
jbe@170
|
4006 AND "positive_votes" >= "policy_row"."direct_majority_positive"
|
jbe@170
|
4007 AND "issue_row"."voter_count"-"negative_votes" >=
|
jbe@170
|
4008 "policy_row"."direct_majority_non_negative",
|
jbe@170
|
4009 "indirect_majority" =
|
jbe@170
|
4010 CASE WHEN "policy_row"."indirect_majority_strict" THEN
|
jbe@170
|
4011 "positive_votes" * "policy_row"."indirect_majority_den" >
|
jbe@170
|
4012 "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
|
jbe@170
|
4013 ELSE
|
jbe@170
|
4014 "positive_votes" * "policy_row"."indirect_majority_den" >=
|
jbe@170
|
4015 "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
|
jbe@170
|
4016 END
|
jbe@170
|
4017 AND "positive_votes" >= "policy_row"."indirect_majority_positive"
|
jbe@170
|
4018 AND "issue_row"."voter_count"-"negative_votes" >=
|
jbe@170
|
4019 "policy_row"."indirect_majority_non_negative",
|
jbe@171
|
4020 "schulze_rank" = "rank_ary"["i"],
|
jbe@170
|
4021 "better_than_status_quo" = "rank_ary"["i"] < "rank_ary"["dimension_v"],
|
jbe@170
|
4022 "worse_than_status_quo" = "rank_ary"["i"] > "rank_ary"["dimension_v"],
|
jbe@170
|
4023 "multistage_majority" = "rank_ary"["i"] >= "rank_ary"["dimension_v"],
|
jbe@172
|
4024 "reverse_beat_path" = "matrix"["dimension_v"]["i"] >= 0,
|
jbe@216
|
4025 "eligible" = FALSE,
|
jbe@250
|
4026 "winner" = FALSE,
|
jbe@250
|
4027 "rank" = NULL -- NOTE: in cases of manual reset of issue state
|
jbe@170
|
4028 WHERE "id" = "initiative_id_v";
|
jbe@170
|
4029 "i" := "i" + 1;
|
jbe@170
|
4030 END LOOP;
|
jbe@170
|
4031 IF "i" != "dimension_v" THEN
|
jbe@170
|
4032 RAISE EXCEPTION 'Wrong winner count (should not happen)';
|
jbe@0
|
4033 END IF;
|
jbe@170
|
4034 -- take indirect majorities into account:
|
jbe@170
|
4035 LOOP
|
jbe@170
|
4036 UPDATE "initiative" SET "indirect_majority" = TRUE
|
jbe@139
|
4037 FROM (
|
jbe@170
|
4038 SELECT "new_initiative"."id" AS "initiative_id"
|
jbe@170
|
4039 FROM "initiative" "old_initiative"
|
jbe@170
|
4040 JOIN "initiative" "new_initiative"
|
jbe@170
|
4041 ON "new_initiative"."issue_id" = "issue_id_p"
|
jbe@170
|
4042 AND "new_initiative"."indirect_majority" = FALSE
|
jbe@139
|
4043 JOIN "battle" "battle_win"
|
jbe@139
|
4044 ON "battle_win"."issue_id" = "issue_id_p"
|
jbe@170
|
4045 AND "battle_win"."winning_initiative_id" = "new_initiative"."id"
|
jbe@170
|
4046 AND "battle_win"."losing_initiative_id" = "old_initiative"."id"
|
jbe@139
|
4047 JOIN "battle" "battle_lose"
|
jbe@139
|
4048 ON "battle_lose"."issue_id" = "issue_id_p"
|
jbe@170
|
4049 AND "battle_lose"."losing_initiative_id" = "new_initiative"."id"
|
jbe@170
|
4050 AND "battle_lose"."winning_initiative_id" = "old_initiative"."id"
|
jbe@170
|
4051 WHERE "old_initiative"."issue_id" = "issue_id_p"
|
jbe@170
|
4052 AND "old_initiative"."indirect_majority" = TRUE
|
jbe@170
|
4053 AND CASE WHEN "policy_row"."indirect_majority_strict" THEN
|
jbe@170
|
4054 "battle_win"."count" * "policy_row"."indirect_majority_den" >
|
jbe@170
|
4055 "policy_row"."indirect_majority_num" *
|
jbe@170
|
4056 ("battle_win"."count"+"battle_lose"."count")
|
jbe@170
|
4057 ELSE
|
jbe@170
|
4058 "battle_win"."count" * "policy_row"."indirect_majority_den" >=
|
jbe@170
|
4059 "policy_row"."indirect_majority_num" *
|
jbe@170
|
4060 ("battle_win"."count"+"battle_lose"."count")
|
jbe@170
|
4061 END
|
jbe@170
|
4062 AND "battle_win"."count" >= "policy_row"."indirect_majority_positive"
|
jbe@170
|
4063 AND "issue_row"."voter_count"-"battle_lose"."count" >=
|
jbe@170
|
4064 "policy_row"."indirect_majority_non_negative"
|
jbe@139
|
4065 ) AS "subquery"
|
jbe@139
|
4066 WHERE "id" = "subquery"."initiative_id";
|
jbe@170
|
4067 EXIT WHEN NOT FOUND;
|
jbe@170
|
4068 END LOOP;
|
jbe@170
|
4069 -- set "multistage_majority" for remaining matching initiatives:
|
jbe@216
|
4070 UPDATE "initiative" SET "multistage_majority" = TRUE
|
jbe@170
|
4071 FROM (
|
jbe@170
|
4072 SELECT "losing_initiative"."id" AS "initiative_id"
|
jbe@170
|
4073 FROM "initiative" "losing_initiative"
|
jbe@170
|
4074 JOIN "initiative" "winning_initiative"
|
jbe@170
|
4075 ON "winning_initiative"."issue_id" = "issue_id_p"
|
jbe@170
|
4076 AND "winning_initiative"."admitted"
|
jbe@170
|
4077 JOIN "battle" "battle_win"
|
jbe@170
|
4078 ON "battle_win"."issue_id" = "issue_id_p"
|
jbe@170
|
4079 AND "battle_win"."winning_initiative_id" = "winning_initiative"."id"
|
jbe@170
|
4080 AND "battle_win"."losing_initiative_id" = "losing_initiative"."id"
|
jbe@170
|
4081 JOIN "battle" "battle_lose"
|
jbe@170
|
4082 ON "battle_lose"."issue_id" = "issue_id_p"
|
jbe@170
|
4083 AND "battle_lose"."losing_initiative_id" = "winning_initiative"."id"
|
jbe@170
|
4084 AND "battle_lose"."winning_initiative_id" = "losing_initiative"."id"
|
jbe@170
|
4085 WHERE "losing_initiative"."issue_id" = "issue_id_p"
|
jbe@170
|
4086 AND "losing_initiative"."admitted"
|
jbe@170
|
4087 AND "winning_initiative"."schulze_rank" <
|
jbe@170
|
4088 "losing_initiative"."schulze_rank"
|
jbe@170
|
4089 AND "battle_win"."count" > "battle_lose"."count"
|
jbe@170
|
4090 AND (
|
jbe@170
|
4091 "battle_win"."count" > "winning_initiative"."positive_votes" OR
|
jbe@170
|
4092 "battle_lose"."count" < "losing_initiative"."negative_votes" )
|
jbe@170
|
4093 ) AS "subquery"
|
jbe@170
|
4094 WHERE "id" = "subquery"."initiative_id";
|
jbe@170
|
4095 -- mark eligible initiatives:
|
jbe@170
|
4096 UPDATE "initiative" SET "eligible" = TRUE
|
jbe@171
|
4097 WHERE "issue_id" = "issue_id_p"
|
jbe@171
|
4098 AND "initiative"."direct_majority"
|
jbe@171
|
4099 AND "initiative"."indirect_majority"
|
jbe@171
|
4100 AND "initiative"."better_than_status_quo"
|
jbe@171
|
4101 AND (
|
jbe@171
|
4102 "policy_row"."no_multistage_majority" = FALSE OR
|
jbe@171
|
4103 "initiative"."multistage_majority" = FALSE )
|
jbe@171
|
4104 AND (
|
jbe@171
|
4105 "policy_row"."no_reverse_beat_path" = FALSE OR
|
jbe@171
|
4106 "initiative"."reverse_beat_path" = FALSE );
|
jbe@170
|
4107 -- mark final winner:
|
jbe@170
|
4108 UPDATE "initiative" SET "winner" = TRUE
|
jbe@170
|
4109 FROM (
|
jbe@170
|
4110 SELECT "id" AS "initiative_id"
|
jbe@170
|
4111 FROM "initiative"
|
jbe@170
|
4112 WHERE "issue_id" = "issue_id_p" AND "eligible"
|
jbe@217
|
4113 ORDER BY
|
jbe@217
|
4114 "schulze_rank",
|
jbe@217
|
4115 "vote_ratio"("positive_votes", "negative_votes"),
|
jbe@217
|
4116 "id"
|
jbe@170
|
4117 LIMIT 1
|
jbe@170
|
4118 ) AS "subquery"
|
jbe@170
|
4119 WHERE "id" = "subquery"."initiative_id";
|
jbe@173
|
4120 -- write (final) ranks:
|
jbe@173
|
4121 "rank_v" := 1;
|
jbe@173
|
4122 FOR "initiative_id_v" IN
|
jbe@173
|
4123 SELECT "id"
|
jbe@173
|
4124 FROM "initiative"
|
jbe@173
|
4125 WHERE "issue_id" = "issue_id_p" AND "admitted"
|
jbe@174
|
4126 ORDER BY
|
jbe@174
|
4127 "winner" DESC,
|
jbe@217
|
4128 "eligible" DESC,
|
jbe@174
|
4129 "schulze_rank",
|
jbe@217
|
4130 "vote_ratio"("positive_votes", "negative_votes"),
|
jbe@174
|
4131 "id"
|
jbe@173
|
4132 LOOP
|
jbe@173
|
4133 UPDATE "initiative" SET "rank" = "rank_v"
|
jbe@173
|
4134 WHERE "id" = "initiative_id_v";
|
jbe@173
|
4135 "rank_v" := "rank_v" + 1;
|
jbe@173
|
4136 END LOOP;
|
jbe@170
|
4137 -- set schulze rank of status quo and mark issue as finished:
|
jbe@111
|
4138 UPDATE "issue" SET
|
jbe@170
|
4139 "status_quo_schulze_rank" = "rank_ary"["dimension_v"],
|
jbe@111
|
4140 "state" =
|
jbe@139
|
4141 CASE WHEN EXISTS (
|
jbe@139
|
4142 SELECT NULL FROM "initiative"
|
jbe@139
|
4143 WHERE "issue_id" = "issue_id_p" AND "winner"
|
jbe@139
|
4144 ) THEN
|
jbe@139
|
4145 'finished_with_winner'::"issue_state"
|
jbe@139
|
4146 ELSE
|
jbe@121
|
4147 'finished_without_winner'::"issue_state"
|
jbe@111
|
4148 END,
|
jbe@111
|
4149 "ranks_available" = TRUE
|
jbe@0
|
4150 WHERE "id" = "issue_id_p";
|
jbe@0
|
4151 RETURN;
|
jbe@0
|
4152 END;
|
jbe@0
|
4153 $$;
|
jbe@0
|
4154
|
jbe@0
|
4155 COMMENT ON FUNCTION "calculate_ranks"
|
jbe@0
|
4156 ( "issue"."id"%TYPE )
|
jbe@0
|
4157 IS 'Determine ranking (Votes have to be counted first)';
|
jbe@0
|
4158
|
jbe@0
|
4159
|
jbe@0
|
4160
|
jbe@0
|
4161 -----------------------------
|
jbe@0
|
4162 -- Automatic state changes --
|
jbe@0
|
4163 -----------------------------
|
jbe@0
|
4164
|
jbe@0
|
4165
|
jbe@0
|
4166 CREATE FUNCTION "check_issue"
|
jbe@0
|
4167 ( "issue_id_p" "issue"."id"%TYPE )
|
jbe@0
|
4168 RETURNS VOID
|
jbe@0
|
4169 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
4170 DECLARE
|
jbe@0
|
4171 "issue_row" "issue"%ROWTYPE;
|
jbe@0
|
4172 "policy_row" "policy"%ROWTYPE;
|
jbe@0
|
4173 BEGIN
|
jbe@67
|
4174 PERFORM "lock_issue"("issue_id_p");
|
jbe@0
|
4175 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p";
|
jbe@24
|
4176 -- only process open issues:
|
jbe@0
|
4177 IF "issue_row"."closed" ISNULL THEN
|
jbe@0
|
4178 SELECT * INTO "policy_row" FROM "policy"
|
jbe@0
|
4179 WHERE "id" = "issue_row"."policy_id";
|
jbe@24
|
4180 -- create a snapshot, unless issue is already fully frozen:
|
jbe@3
|
4181 IF "issue_row"."fully_frozen" ISNULL THEN
|
jbe@0
|
4182 PERFORM "create_snapshot"("issue_id_p");
|
jbe@0
|
4183 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p";
|
jbe@0
|
4184 END IF;
|
jbe@24
|
4185 -- eventually close or accept issues, which have not been accepted:
|
jbe@0
|
4186 IF "issue_row"."accepted" ISNULL THEN
|
jbe@0
|
4187 IF EXISTS (
|
jbe@0
|
4188 SELECT NULL FROM "initiative"
|
jbe@0
|
4189 WHERE "issue_id" = "issue_id_p"
|
jbe@0
|
4190 AND "supporter_count" > 0
|
jbe@0
|
4191 AND "supporter_count" * "policy_row"."issue_quorum_den"
|
jbe@0
|
4192 >= "issue_row"."population" * "policy_row"."issue_quorum_num"
|
jbe@0
|
4193 ) THEN
|
jbe@24
|
4194 -- accept issues, if supporter count is high enough
|
jbe@3
|
4195 PERFORM "set_snapshot_event"("issue_id_p", 'end_of_admission');
|
jbe@111
|
4196 -- NOTE: "issue_row" used later
|
jbe@111
|
4197 "issue_row"."state" := 'discussion';
|
jbe@111
|
4198 "issue_row"."accepted" := now();
|
jbe@111
|
4199 UPDATE "issue" SET
|
jbe@111
|
4200 "state" = "issue_row"."state",
|
jbe@111
|
4201 "accepted" = "issue_row"."accepted"
|
jbe@0
|
4202 WHERE "id" = "issue_row"."id";
|
jbe@0
|
4203 ELSIF
|
jbe@22
|
4204 now() >= "issue_row"."created" + "issue_row"."admission_time"
|
jbe@0
|
4205 THEN
|
jbe@24
|
4206 -- close issues, if admission time has expired
|
jbe@0
|
4207 PERFORM "set_snapshot_event"("issue_id_p", 'end_of_admission');
|
jbe@111
|
4208 UPDATE "issue" SET
|
jbe@111
|
4209 "state" = 'canceled_issue_not_accepted',
|
jbe@111
|
4210 "closed" = now()
|
jbe@0
|
4211 WHERE "id" = "issue_row"."id";
|
jbe@0
|
4212 END IF;
|
jbe@0
|
4213 END IF;
|
jbe@24
|
4214 -- eventually half freeze issues:
|
jbe@0
|
4215 IF
|
jbe@24
|
4216 -- NOTE: issue can't be closed at this point, if it has been accepted
|
jbe@0
|
4217 "issue_row"."accepted" NOTNULL AND
|
jbe@3
|
4218 "issue_row"."half_frozen" ISNULL
|
jbe@0
|
4219 THEN
|
jbe@0
|
4220 IF
|
jbe@144
|
4221 now() >= "issue_row"."accepted" + "issue_row"."discussion_time"
|
jbe@0
|
4222 THEN
|
jbe@21
|
4223 PERFORM "set_snapshot_event"("issue_id_p", 'half_freeze');
|
jbe@111
|
4224 -- NOTE: "issue_row" used later
|
jbe@111
|
4225 "issue_row"."state" := 'verification';
|
jbe@111
|
4226 "issue_row"."half_frozen" := now();
|
jbe@111
|
4227 UPDATE "issue" SET
|
jbe@111
|
4228 "state" = "issue_row"."state",
|
jbe@111
|
4229 "half_frozen" = "issue_row"."half_frozen"
|
jbe@3
|
4230 WHERE "id" = "issue_row"."id";
|
jbe@0
|
4231 END IF;
|
jbe@0
|
4232 END IF;
|
jbe@24
|
4233 -- close issues after some time, if all initiatives have been revoked:
|
jbe@24
|
4234 IF
|
jbe@24
|
4235 "issue_row"."closed" ISNULL AND
|
jbe@24
|
4236 NOT EXISTS (
|
jbe@24
|
4237 -- all initiatives are revoked
|
jbe@24
|
4238 SELECT NULL FROM "initiative"
|
jbe@24
|
4239 WHERE "issue_id" = "issue_id_p" AND "revoked" ISNULL
|
jbe@24
|
4240 ) AND (
|
jbe@111
|
4241 -- and issue has not been accepted yet
|
jbe@111
|
4242 "issue_row"."accepted" ISNULL OR
|
jbe@24
|
4243 NOT EXISTS (
|
jbe@111
|
4244 -- or no initiatives have been revoked lately
|
jbe@24
|
4245 SELECT NULL FROM "initiative"
|
jbe@24
|
4246 WHERE "issue_id" = "issue_id_p"
|
jbe@24
|
4247 AND now() < "revoked" + "issue_row"."verification_time"
|
jbe@24
|
4248 ) OR (
|
jbe@24
|
4249 -- or verification time has elapsed
|
jbe@24
|
4250 "issue_row"."half_frozen" NOTNULL AND
|
jbe@24
|
4251 "issue_row"."fully_frozen" ISNULL AND
|
jbe@24
|
4252 now() >= "issue_row"."half_frozen" + "issue_row"."verification_time"
|
jbe@24
|
4253 )
|
jbe@24
|
4254 )
|
jbe@24
|
4255 THEN
|
jbe@111
|
4256 -- NOTE: "issue_row" used later
|
jbe@113
|
4257 IF "issue_row"."accepted" ISNULL THEN
|
jbe@113
|
4258 "issue_row"."state" := 'canceled_revoked_before_accepted';
|
jbe@113
|
4259 ELSIF "issue_row"."half_frozen" ISNULL THEN
|
jbe@113
|
4260 "issue_row"."state" := 'canceled_after_revocation_during_discussion';
|
jbe@113
|
4261 ELSE
|
jbe@113
|
4262 "issue_row"."state" := 'canceled_after_revocation_during_verification';
|
jbe@113
|
4263 END IF;
|
jbe@111
|
4264 "issue_row"."closed" := now();
|
jbe@111
|
4265 UPDATE "issue" SET
|
jbe@111
|
4266 "state" = "issue_row"."state",
|
jbe@111
|
4267 "closed" = "issue_row"."closed"
|
jbe@24
|
4268 WHERE "id" = "issue_row"."id";
|
jbe@24
|
4269 END IF;
|
jbe@24
|
4270 -- fully freeze issue after verification time:
|
jbe@0
|
4271 IF
|
jbe@3
|
4272 "issue_row"."half_frozen" NOTNULL AND
|
jbe@3
|
4273 "issue_row"."fully_frozen" ISNULL AND
|
jbe@24
|
4274 "issue_row"."closed" ISNULL AND
|
jbe@22
|
4275 now() >= "issue_row"."half_frozen" + "issue_row"."verification_time"
|
jbe@3
|
4276 THEN
|
jbe@3
|
4277 PERFORM "freeze_after_snapshot"("issue_id_p");
|
jbe@24
|
4278 -- NOTE: "issue" might change, thus "issue_row" has to be updated below
|
jbe@3
|
4279 END IF;
|
jbe@9
|
4280 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p";
|
jbe@24
|
4281 -- close issue by calling close_voting(...) after voting time:
|
jbe@3
|
4282 IF
|
jbe@9
|
4283 "issue_row"."closed" ISNULL AND
|
jbe@3
|
4284 "issue_row"."fully_frozen" NOTNULL AND
|
jbe@22
|
4285 now() >= "issue_row"."fully_frozen" + "issue_row"."voting_time"
|
jbe@0
|
4286 THEN
|
jbe@0
|
4287 PERFORM "close_voting"("issue_id_p");
|
jbe@111
|
4288 -- calculate ranks will not consume much time and can be done now
|
jbe@111
|
4289 PERFORM "calculate_ranks"("issue_id_p");
|
jbe@0
|
4290 END IF;
|
jbe@0
|
4291 END IF;
|
jbe@0
|
4292 RETURN;
|
jbe@0
|
4293 END;
|
jbe@0
|
4294 $$;
|
jbe@0
|
4295
|
jbe@0
|
4296 COMMENT ON FUNCTION "check_issue"
|
jbe@0
|
4297 ( "issue"."id"%TYPE )
|
jbe@0
|
4298 IS 'Precalculate supporter counts etc. for a given issue, and check, if status change is required; At end of voting the ranking is not calculated by this function, but must be calculated in a seperate transaction using the "calculate_ranks" function.';
|
jbe@0
|
4299
|
jbe@0
|
4300
|
jbe@0
|
4301 CREATE FUNCTION "check_everything"()
|
jbe@0
|
4302 RETURNS VOID
|
jbe@0
|
4303 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@0
|
4304 DECLARE
|
jbe@0
|
4305 "issue_id_v" "issue"."id"%TYPE;
|
jbe@0
|
4306 BEGIN
|
jbe@235
|
4307 DELETE FROM "expired_session";
|
jbe@184
|
4308 PERFORM "check_activity"();
|
jbe@4
|
4309 PERFORM "calculate_member_counts"();
|
jbe@4
|
4310 FOR "issue_id_v" IN SELECT "id" FROM "open_issue" LOOP
|
jbe@0
|
4311 PERFORM "check_issue"("issue_id_v");
|
jbe@0
|
4312 END LOOP;
|
jbe@4
|
4313 FOR "issue_id_v" IN SELECT "id" FROM "issue_with_ranks_missing" LOOP
|
jbe@0
|
4314 PERFORM "calculate_ranks"("issue_id_v");
|
jbe@0
|
4315 END LOOP;
|
jbe@0
|
4316 RETURN;
|
jbe@0
|
4317 END;
|
jbe@0
|
4318 $$;
|
jbe@0
|
4319
|
jbe@103
|
4320 COMMENT ON FUNCTION "check_everything"() IS 'Amongst other regular tasks this function performs "check_issue" for every open issue, and if possible, automatically calculates ranks. Use this function only for development and debugging purposes, as long transactions with exclusive locking may result. In productive environments you should call the lf_update program instead.';
|
jbe@0
|
4321
|
jbe@0
|
4322
|
jbe@0
|
4323
|
jbe@59
|
4324 ----------------------
|
jbe@59
|
4325 -- Deletion of data --
|
jbe@59
|
4326 ----------------------
|
jbe@59
|
4327
|
jbe@59
|
4328
|
jbe@59
|
4329 CREATE FUNCTION "clean_issue"("issue_id_p" "issue"."id"%TYPE)
|
jbe@59
|
4330 RETURNS VOID
|
jbe@59
|
4331 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@59
|
4332 DECLARE
|
jbe@59
|
4333 "issue_row" "issue"%ROWTYPE;
|
jbe@59
|
4334 BEGIN
|
jbe@59
|
4335 SELECT * INTO "issue_row"
|
jbe@59
|
4336 FROM "issue" WHERE "id" = "issue_id_p"
|
jbe@59
|
4337 FOR UPDATE;
|
jbe@59
|
4338 IF "issue_row"."cleaned" ISNULL THEN
|
jbe@59
|
4339 UPDATE "issue" SET
|
jbe@152
|
4340 "state" = 'voting',
|
jbe@152
|
4341 "closed" = NULL,
|
jbe@59
|
4342 "ranks_available" = FALSE
|
jbe@59
|
4343 WHERE "id" = "issue_id_p";
|
jbe@59
|
4344 DELETE FROM "delegating_voter"
|
jbe@59
|
4345 WHERE "issue_id" = "issue_id_p";
|
jbe@59
|
4346 DELETE FROM "direct_voter"
|
jbe@59
|
4347 WHERE "issue_id" = "issue_id_p";
|
jbe@59
|
4348 DELETE FROM "delegating_interest_snapshot"
|
jbe@59
|
4349 WHERE "issue_id" = "issue_id_p";
|
jbe@59
|
4350 DELETE FROM "direct_interest_snapshot"
|
jbe@59
|
4351 WHERE "issue_id" = "issue_id_p";
|
jbe@59
|
4352 DELETE FROM "delegating_population_snapshot"
|
jbe@59
|
4353 WHERE "issue_id" = "issue_id_p";
|
jbe@59
|
4354 DELETE FROM "direct_population_snapshot"
|
jbe@59
|
4355 WHERE "issue_id" = "issue_id_p";
|
jbe@113
|
4356 DELETE FROM "non_voter"
|
jbe@94
|
4357 WHERE "issue_id" = "issue_id_p";
|
jbe@59
|
4358 DELETE FROM "delegation"
|
jbe@59
|
4359 WHERE "issue_id" = "issue_id_p";
|
jbe@59
|
4360 DELETE FROM "supporter"
|
jbe@59
|
4361 WHERE "issue_id" = "issue_id_p";
|
jbe@59
|
4362 UPDATE "issue" SET
|
jbe@152
|
4363 "state" = "issue_row"."state",
|
jbe@59
|
4364 "closed" = "issue_row"."closed",
|
jbe@59
|
4365 "ranks_available" = "issue_row"."ranks_available",
|
jbe@59
|
4366 "cleaned" = now()
|
jbe@59
|
4367 WHERE "id" = "issue_id_p";
|
jbe@59
|
4368 END IF;
|
jbe@59
|
4369 RETURN;
|
jbe@59
|
4370 END;
|
jbe@59
|
4371 $$;
|
jbe@59
|
4372
|
jbe@59
|
4373 COMMENT ON FUNCTION "clean_issue"("issue"."id"%TYPE) IS 'Delete discussion data and votes belonging to an issue';
|
jbe@8
|
4374
|
jbe@8
|
4375
|
jbe@54
|
4376 CREATE FUNCTION "delete_member"("member_id_p" "member"."id"%TYPE)
|
jbe@8
|
4377 RETURNS VOID
|
jbe@8
|
4378 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@8
|
4379 BEGIN
|
jbe@9
|
4380 UPDATE "member" SET
|
jbe@57
|
4381 "last_login" = NULL,
|
jbe@45
|
4382 "login" = NULL,
|
jbe@11
|
4383 "password" = NULL,
|
jbe@101
|
4384 "locked" = TRUE,
|
jbe@54
|
4385 "active" = FALSE,
|
jbe@11
|
4386 "notify_email" = NULL,
|
jbe@11
|
4387 "notify_email_unconfirmed" = NULL,
|
jbe@11
|
4388 "notify_email_secret" = NULL,
|
jbe@11
|
4389 "notify_email_secret_expiry" = NULL,
|
jbe@57
|
4390 "notify_email_lock_expiry" = NULL,
|
jbe@11
|
4391 "password_reset_secret" = NULL,
|
jbe@11
|
4392 "password_reset_secret_expiry" = NULL,
|
jbe@11
|
4393 "organizational_unit" = NULL,
|
jbe@11
|
4394 "internal_posts" = NULL,
|
jbe@11
|
4395 "realname" = NULL,
|
jbe@11
|
4396 "birthday" = NULL,
|
jbe@11
|
4397 "address" = NULL,
|
jbe@11
|
4398 "email" = NULL,
|
jbe@11
|
4399 "xmpp_address" = NULL,
|
jbe@11
|
4400 "website" = NULL,
|
jbe@11
|
4401 "phone" = NULL,
|
jbe@11
|
4402 "mobile_phone" = NULL,
|
jbe@11
|
4403 "profession" = NULL,
|
jbe@11
|
4404 "external_memberships" = NULL,
|
jbe@11
|
4405 "external_posts" = NULL,
|
jbe@45
|
4406 "statement" = NULL
|
jbe@45
|
4407 WHERE "id" = "member_id_p";
|
jbe@11
|
4408 -- "text_search_data" is updated by triggers
|
jbe@45
|
4409 DELETE FROM "setting" WHERE "member_id" = "member_id_p";
|
jbe@45
|
4410 DELETE FROM "setting_map" WHERE "member_id" = "member_id_p";
|
jbe@45
|
4411 DELETE FROM "member_relation_setting" WHERE "member_id" = "member_id_p";
|
jbe@45
|
4412 DELETE FROM "member_image" WHERE "member_id" = "member_id_p";
|
jbe@45
|
4413 DELETE FROM "contact" WHERE "member_id" = "member_id_p";
|
jbe@113
|
4414 DELETE FROM "ignored_member" WHERE "member_id" = "member_id_p";
|
jbe@235
|
4415 DELETE FROM "session" WHERE "member_id" = "member_id_p";
|
jbe@45
|
4416 DELETE FROM "area_setting" WHERE "member_id" = "member_id_p";
|
jbe@45
|
4417 DELETE FROM "issue_setting" WHERE "member_id" = "member_id_p";
|
jbe@113
|
4418 DELETE FROM "ignored_initiative" WHERE "member_id" = "member_id_p";
|
jbe@45
|
4419 DELETE FROM "initiative_setting" WHERE "member_id" = "member_id_p";
|
jbe@45
|
4420 DELETE FROM "suggestion_setting" WHERE "member_id" = "member_id_p";
|
jbe@54
|
4421 DELETE FROM "membership" WHERE "member_id" = "member_id_p";
|
jbe@54
|
4422 DELETE FROM "delegation" WHERE "truster_id" = "member_id_p";
|
jbe@113
|
4423 DELETE FROM "non_voter" WHERE "member_id" = "member_id_p";
|
jbe@57
|
4424 DELETE FROM "direct_voter" USING "issue"
|
jbe@57
|
4425 WHERE "direct_voter"."issue_id" = "issue"."id"
|
jbe@57
|
4426 AND "issue"."closed" ISNULL
|
jbe@57
|
4427 AND "member_id" = "member_id_p";
|
jbe@45
|
4428 RETURN;
|
jbe@45
|
4429 END;
|
jbe@45
|
4430 $$;
|
jbe@45
|
4431
|
jbe@57
|
4432 COMMENT ON FUNCTION "delete_member"("member_id_p" "member"."id"%TYPE) IS 'Deactivate member and clear certain settings and data of this member (data protection)';
|
jbe@45
|
4433
|
jbe@45
|
4434
|
jbe@45
|
4435 CREATE FUNCTION "delete_private_data"()
|
jbe@45
|
4436 RETURNS VOID
|
jbe@45
|
4437 LANGUAGE 'plpgsql' VOLATILE AS $$
|
jbe@45
|
4438 BEGIN
|
jbe@226
|
4439 DELETE FROM "member" WHERE "activated" ISNULL;
|
jbe@50
|
4440 UPDATE "member" SET
|
jbe@206
|
4441 "invite_code" = NULL,
|
jbe@232
|
4442 "invite_code_expiry" = NULL,
|
jbe@228
|
4443 "admin_comment" = NULL,
|
jbe@57
|
4444 "last_login" = NULL,
|
jbe@50
|
4445 "login" = NULL,
|
jbe@50
|
4446 "password" = NULL,
|
jbe@238
|
4447 "lang" = NULL,
|
jbe@50
|
4448 "notify_email" = NULL,
|
jbe@50
|
4449 "notify_email_unconfirmed" = NULL,
|
jbe@50
|
4450 "notify_email_secret" = NULL,
|
jbe@50
|
4451 "notify_email_secret_expiry" = NULL,
|
jbe@57
|
4452 "notify_email_lock_expiry" = NULL,
|
jbe@238
|
4453 "notify_level" = NULL,
|
jbe@50
|
4454 "password_reset_secret" = NULL,
|
jbe@50
|
4455 "password_reset_secret_expiry" = NULL,
|
jbe@50
|
4456 "organizational_unit" = NULL,
|
jbe@50
|
4457 "internal_posts" = NULL,
|
jbe@50
|
4458 "realname" = NULL,
|
jbe@50
|
4459 "birthday" = NULL,
|
jbe@50
|
4460 "address" = NULL,
|
jbe@50
|
4461 "email" = NULL,
|
jbe@50
|
4462 "xmpp_address" = NULL,
|
jbe@50
|
4463 "website" = NULL,
|
jbe@50
|
4464 "phone" = NULL,
|
jbe@50
|
4465 "mobile_phone" = NULL,
|
jbe@50
|
4466 "profession" = NULL,
|
jbe@50
|
4467 "external_memberships" = NULL,
|
jbe@50
|
4468 "external_posts" = NULL,
|
jbe@238
|
4469 "formatting_engine" = NULL,
|
jbe@50
|
4470 "statement" = NULL;
|
jbe@50
|
4471 -- "text_search_data" is updated by triggers
|
jbe@50
|
4472 DELETE FROM "setting";
|
jbe@50
|
4473 DELETE FROM "setting_map";
|
jbe@50
|
4474 DELETE FROM "member_relation_setting";
|
jbe@50
|
4475 DELETE FROM "member_image";
|
jbe@50
|
4476 DELETE FROM "contact";
|
jbe@113
|
4477 DELETE FROM "ignored_member";
|
jbe@235
|
4478 DELETE FROM "session";
|
jbe@50
|
4479 DELETE FROM "area_setting";
|
jbe@50
|
4480 DELETE FROM "issue_setting";
|
jbe@113
|
4481 DELETE FROM "ignored_initiative";
|
jbe@50
|
4482 DELETE FROM "initiative_setting";
|
jbe@50
|
4483 DELETE FROM "suggestion_setting";
|
jbe@113
|
4484 DELETE FROM "non_voter";
|
jbe@8
|
4485 DELETE FROM "direct_voter" USING "issue"
|
jbe@8
|
4486 WHERE "direct_voter"."issue_id" = "issue"."id"
|
jbe@8
|
4487 AND "issue"."closed" ISNULL;
|
jbe@8
|
4488 RETURN;
|
jbe@8
|
4489 END;
|
jbe@8
|
4490 $$;
|
jbe@8
|
4491
|
jbe@273
|
4492 COMMENT ON FUNCTION "delete_private_data"() IS 'Used by lf_export script. DO NOT USE on productive database, but only on a copy! This function deletes all data which should not be publicly available, and can be used to create a database dump for publication. See source code to see which data is deleted. If you need a different behaviour, copy this function and modify lf_export accordingly, to avoid data-leaks after updating.';
|
jbe@8
|
4493
|
jbe@8
|
4494
|
jbe@8
|
4495
|
jbe@0
|
4496 COMMIT;
|