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