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