liquid_feedback_core
view update/core-update.v2.0.12-v2.1.0.sql @ 378:e88d0606891f
Bugfix regarding "proportional_order" of suggestions:
Use NULL values explicitly to be sorted last
(includes new suggestions as well as suggestions without any individual rankings)
Use NULL values explicitly to be sorted last
(includes new suggestions as well as suggestions without any individual rankings)
author | jbe |
---|---|
date | Mon Mar 18 09:36:21 2013 +0100 (2013-03-18) |
parents | 5c98265b39a0 |
children |
line source
1 BEGIN;
4 -- update version number
6 CREATE OR REPLACE VIEW "liquid_feedback_version" AS
7 SELECT * FROM (VALUES ('2.1.0', 2, 1, 0))
8 AS "subquery"("string", "major", "minor", "revision");
11 -- old API tables are now deprecated
13 COMMENT ON TYPE "application_access_level" IS 'DEPRECATED, WILL BE REMOVED! Access privileges for applications using the API';
14 COMMENT ON TABLE "member_application" IS 'DEPRECATED, WILL BE REMOVED! Registered application being allowed to use the API';
17 -- new polling mode and changed privileges
19 ALTER TABLE "policy" ADD COLUMN "polling" BOOLEAN NOT NULL DEFAULT FALSE;
20 ALTER TABLE "policy" ALTER COLUMN "admission_time" DROP NOT NULL;
21 ALTER TABLE "policy" ALTER COLUMN "discussion_time" DROP NOT NULL;
22 ALTER TABLE "policy" ALTER COLUMN "verification_time" DROP NOT NULL;
23 ALTER TABLE "policy" ALTER COLUMN "voting_time" DROP NOT NULL;
24 ALTER TABLE "policy" ALTER COLUMN "issue_quorum_num" DROP NOT NULL;
25 ALTER TABLE "policy" ALTER COLUMN "issue_quorum_den" DROP NOT NULL;
26 ALTER TABLE "policy" ADD CONSTRAINT "timing" CHECK (
27 ( "polling" = FALSE AND
28 "admission_time" NOTNULL AND "discussion_time" NOTNULL AND
29 "verification_time" NOTNULL AND "voting_time" NOTNULL ) OR
30 ( "polling" = TRUE AND
31 "admission_time" ISNULL AND "discussion_time" NOTNULL AND
32 "verification_time" NOTNULL AND "voting_time" NOTNULL ) OR
33 ( "polling" = TRUE AND
34 "admission_time" ISNULL AND "discussion_time" ISNULL AND
35 "verification_time" ISNULL AND "voting_time" ISNULL ) );
36 ALTER TABLE "policy" ADD CONSTRAINT "issue_quorum_if_and_only_if_not_polling" CHECK (
37 "polling" = "issue_quorum_num" ISNULL AND
38 "polling" = "issue_quorum_den" ISNULL );
39 COMMENT ON COLUMN "policy"."polling" IS 'TRUE = special policy for non-user-generated issues without issue quorum, where certain initiatives (those having the "polling" flag set) do not need to pass the initiative quorum; "admission_time" MUST be set to NULL, the other timings may be set to NULL altogether, allowing individual timing for those issues';
41 ALTER TABLE "issue" ALTER COLUMN "admission_time" DROP NOT NULL;
42 ALTER TABLE "issue" ADD CONSTRAINT "admission_time_not_null_unless_instantly_accepted" CHECK (
43 "admission_time" NOTNULL OR ("accepted" NOTNULL AND "accepted" = "created") );
45 ALTER TABLE "initiative" ADD COLUMN "polling" BOOLEAN NOT NULL DEFAULT FALSE;
46 COMMENT ON COLUMN "initiative"."polling" IS 'Initiative does not need to pass the initiative quorum (see "policy"."polling")';
48 ALTER TABLE "privilege" RENAME COLUMN "voting_right_manager" TO "member_manager";
49 ALTER TABLE "privilege" ADD COLUMN "initiative_right" BOOLEAN NOT NULL DEFAULT TRUE;
50 ALTER TABLE "privilege" ADD COLUMN "polling_right" BOOLEAN NOT NULL DEFAULT FALSE;
51 UPDATE "privilege" SET "initiative_right" = "voting_right";
52 COMMENT ON COLUMN "privilege"."admin_manager" IS 'Grant/revoke any privileges to/from other members';
53 COMMENT ON COLUMN "privilege"."member_manager" IS 'Adding/removing members from the unit, granting or revoking "initiative_right" and "voting_right"';
54 COMMENT ON COLUMN "privilege"."initiative_right" IS 'Right to create an initiative';
55 COMMENT ON COLUMN "privilege"."voting_right" IS 'Right to support initiatives, create and rate suggestions, and to vote';
56 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';
58 DROP VIEW "member_contingent_left";
59 DROP VIEW "member_contingent";
60 ALTER TABLE "contingent" DROP CONSTRAINT "contingent_pkey";
61 ALTER TABLE "contingent" ALTER COLUMN "time_frame" DROP NOT NULL;
62 ALTER TABLE "contingent" ADD COLUMN "polling" BOOLEAN DEFAULT FALSE;
63 ALTER TABLE "contingent" ADD PRIMARY KEY ("polling", "time_frame");
64 ALTER TABLE "contingent" ALTER COLUMN "polling" DROP DEFAULT;
65 COMMENT ON COLUMN "contingent"."polling" IS 'Determines if settings are for creating initiatives and new drafts of initiatives with "polling" flag set';
67 CREATE VIEW "member_contingent" AS
68 SELECT
69 "member"."id" AS "member_id",
70 "contingent"."polling",
71 "contingent"."time_frame",
72 CASE WHEN "contingent"."text_entry_limit" NOTNULL THEN
73 (
74 SELECT count(1) FROM "draft"
75 JOIN "initiative" ON "initiative"."id" = "draft"."initiative_id"
76 WHERE "draft"."author_id" = "member"."id"
77 AND "initiative"."polling" = "contingent"."polling"
78 AND "draft"."created" > now() - "contingent"."time_frame"
79 ) + (
80 SELECT count(1) FROM "suggestion"
81 JOIN "initiative" ON "initiative"."id" = "suggestion"."initiative_id"
82 WHERE "suggestion"."author_id" = "member"."id"
83 AND "contingent"."polling" = FALSE
84 AND "suggestion"."created" > now() - "contingent"."time_frame"
85 )
86 ELSE NULL END AS "text_entry_count",
87 "contingent"."text_entry_limit",
88 CASE WHEN "contingent"."initiative_limit" NOTNULL THEN (
89 SELECT count(1) FROM "opening_draft" AS "draft"
90 JOIN "initiative" ON "initiative"."id" = "draft"."initiative_id"
91 WHERE "draft"."author_id" = "member"."id"
92 AND "initiative"."polling" = "contingent"."polling"
93 AND "draft"."created" > now() - "contingent"."time_frame"
94 ) ELSE NULL END AS "initiative_count",
95 "contingent"."initiative_limit"
96 FROM "member" CROSS JOIN "contingent";
98 COMMENT ON VIEW "member_contingent" IS 'Actual counts of text entries and initiatives are calculated per member for each limit in the "contingent" table.';
100 COMMENT ON COLUMN "member_contingent"."text_entry_count" IS 'Only calculated when "text_entry_limit" is not null in the same row';
101 COMMENT ON COLUMN "member_contingent"."initiative_count" IS 'Only calculated when "initiative_limit" is not null in the same row';
103 CREATE VIEW "member_contingent_left" AS
104 SELECT
105 "member_id",
106 "polling",
107 max("text_entry_limit" - "text_entry_count") AS "text_entries_left",
108 max("initiative_limit" - "initiative_count") AS "initiatives_left"
109 FROM "member_contingent" GROUP BY "member_id", "polling";
111 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.';
113 CREATE OR REPLACE FUNCTION "freeze_after_snapshot"
114 ( "issue_id_p" "issue"."id"%TYPE )
115 RETURNS VOID
116 LANGUAGE 'plpgsql' VOLATILE AS $$
117 DECLARE
118 "issue_row" "issue"%ROWTYPE;
119 "policy_row" "policy"%ROWTYPE;
120 "initiative_row" "initiative"%ROWTYPE;
121 BEGIN
122 SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p";
123 SELECT * INTO "policy_row"
124 FROM "policy" WHERE "id" = "issue_row"."policy_id";
125 PERFORM "set_snapshot_event"("issue_id_p", 'full_freeze');
126 FOR "initiative_row" IN
127 SELECT * FROM "initiative"
128 WHERE "issue_id" = "issue_id_p" AND "revoked" ISNULL
129 LOOP
130 IF
131 "initiative_row"."polling" OR (
132 "initiative_row"."satisfied_supporter_count" > 0 AND
133 "initiative_row"."satisfied_supporter_count" *
134 "policy_row"."initiative_quorum_den" >=
135 "issue_row"."population" * "policy_row"."initiative_quorum_num"
136 )
137 THEN
138 UPDATE "initiative" SET "admitted" = TRUE
139 WHERE "id" = "initiative_row"."id";
140 ELSE
141 UPDATE "initiative" SET "admitted" = FALSE
142 WHERE "id" = "initiative_row"."id";
143 END IF;
144 END LOOP;
145 IF EXISTS (
146 SELECT NULL FROM "initiative"
147 WHERE "issue_id" = "issue_id_p" AND "admitted" = TRUE
148 ) THEN
149 UPDATE "issue" SET
150 "state" = 'voting',
151 "accepted" = coalesce("accepted", now()),
152 "half_frozen" = coalesce("half_frozen", now()),
153 "fully_frozen" = now()
154 WHERE "id" = "issue_id_p";
155 ELSE
156 UPDATE "issue" SET
157 "state" = 'canceled_no_initiative_admitted',
158 "accepted" = coalesce("accepted", now()),
159 "half_frozen" = coalesce("half_frozen", now()),
160 "fully_frozen" = now(),
161 "closed" = now(),
162 "ranks_available" = TRUE
163 WHERE "id" = "issue_id_p";
164 -- NOTE: The following DELETE statements have effect only when
165 -- issue state has been manipulated
166 DELETE FROM "direct_voter" WHERE "issue_id" = "issue_id_p";
167 DELETE FROM "delegating_voter" WHERE "issue_id" = "issue_id_p";
168 DELETE FROM "battle" WHERE "issue_id" = "issue_id_p";
169 END IF;
170 RETURN;
171 END;
172 $$;
175 -- issue comments removed, voting comments integrated in "direct_voter" table
177 ALTER TABLE "direct_voter" ADD COLUMN "comment_changed" TIMESTAMPTZ;
178 ALTER TABLE "direct_voter" ADD COLUMN "formatting_engine" TEXT;
179 ALTER TABLE "direct_voter" ADD COLUMN "comment" TEXT;
180 ALTER TABLE "direct_voter" ADD COLUMN "text_search_data" TSVECTOR;
181 CREATE INDEX "direct_voter_text_search_data_idx" ON "direct_voter" USING gin ("text_search_data");
182 CREATE TRIGGER "update_text_search_data"
183 BEFORE INSERT OR UPDATE ON "direct_voter"
184 FOR EACH ROW EXECUTE PROCEDURE
185 tsvector_update_trigger('text_search_data', 'pg_catalog.simple', "comment");
187 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';
188 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';
189 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.';
191 CREATE TABLE "rendered_voter_comment" (
192 PRIMARY KEY ("issue_id", "member_id", "format"),
193 FOREIGN KEY ("issue_id", "member_id")
194 REFERENCES "direct_voter" ("issue_id", "member_id")
195 ON DELETE CASCADE ON UPDATE CASCADE,
196 "issue_id" INT4,
197 "member_id" INT4,
198 "format" TEXT,
199 "content" TEXT NOT NULL );
201 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)';
203 DROP TABLE "rendered_issue_comment";
204 DROP TABLE "issue_comment";
205 DROP TABLE "rendered_voting_comment";
206 DROP TABLE "voting_comment";
208 CREATE FUNCTION "voter_comment_fields_only_set_when_voter_comment_is_set_trigger"()
209 RETURNS TRIGGER
210 LANGUAGE 'plpgsql' VOLATILE AS $$
211 BEGIN
212 IF NEW."comment" ISNULL THEN
213 NEW."comment_changed" := NULL;
214 NEW."formatting_engine" := NULL;
215 END IF;
216 RETURN NEW;
217 END;
218 $$;
220 CREATE TRIGGER "voter_comment_fields_only_set_when_voter_comment_is_set"
221 BEFORE INSERT OR UPDATE ON "direct_voter"
222 FOR EACH ROW EXECUTE PROCEDURE
223 "voter_comment_fields_only_set_when_voter_comment_is_set_trigger"();
225 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"';
226 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.';
228 CREATE OR REPLACE FUNCTION "forbid_changes_on_closed_issue_trigger"()
229 RETURNS TRIGGER
230 LANGUAGE 'plpgsql' VOLATILE AS $$
231 DECLARE
232 "issue_id_v" "issue"."id"%TYPE;
233 "issue_row" "issue"%ROWTYPE;
234 BEGIN
235 IF TG_RELID = 'direct_voter'::regclass AND TG_OP = 'UPDATE' THEN
236 IF
237 OLD."issue_id" = NEW."issue_id" AND
238 OLD."member_id" = NEW."member_id" AND
239 OLD."weight" = NEW."weight"
240 THEN
241 RETURN NULL; -- allows changing of voter comment
242 END IF;
243 END IF;
244 IF TG_OP = 'DELETE' THEN
245 "issue_id_v" := OLD."issue_id";
246 ELSE
247 "issue_id_v" := NEW."issue_id";
248 END IF;
249 SELECT INTO "issue_row" * FROM "issue"
250 WHERE "id" = "issue_id_v" FOR SHARE;
251 IF "issue_row"."closed" NOTNULL THEN
252 RAISE EXCEPTION 'Tried to modify data belonging to a closed issue.';
253 END IF;
254 RETURN NULL;
255 END;
256 $$;
258 CREATE OR REPLACE FUNCTION "close_voting"("issue_id_p" "issue"."id"%TYPE)
259 RETURNS VOID
260 LANGUAGE 'plpgsql' VOLATILE AS $$
261 DECLARE
262 "area_id_v" "area"."id"%TYPE;
263 "unit_id_v" "unit"."id"%TYPE;
264 "member_id_v" "member"."id"%TYPE;
265 BEGIN
266 PERFORM "lock_issue"("issue_id_p");
267 SELECT "area_id" INTO "area_id_v" FROM "issue" WHERE "id" = "issue_id_p";
268 SELECT "unit_id" INTO "unit_id_v" FROM "area" WHERE "id" = "area_id_v";
269 -- delete timestamp of voting comment:
270 UPDATE "direct_voter" SET "comment_changed" = NULL
271 WHERE "issue_id" = "issue_id_p";
272 -- delete delegating votes (in cases of manual reset of issue state):
273 DELETE FROM "delegating_voter"
274 WHERE "issue_id" = "issue_id_p";
275 -- delete votes from non-privileged voters:
276 DELETE FROM "direct_voter"
277 USING (
278 SELECT
279 "direct_voter"."member_id"
280 FROM "direct_voter"
281 JOIN "member" ON "direct_voter"."member_id" = "member"."id"
282 LEFT JOIN "privilege"
283 ON "privilege"."unit_id" = "unit_id_v"
284 AND "privilege"."member_id" = "direct_voter"."member_id"
285 WHERE "direct_voter"."issue_id" = "issue_id_p" AND (
286 "member"."active" = FALSE OR
287 "privilege"."voting_right" ISNULL OR
288 "privilege"."voting_right" = FALSE
289 )
290 ) AS "subquery"
291 WHERE "direct_voter"."issue_id" = "issue_id_p"
292 AND "direct_voter"."member_id" = "subquery"."member_id";
293 -- consider delegations:
294 UPDATE "direct_voter" SET "weight" = 1
295 WHERE "issue_id" = "issue_id_p";
296 PERFORM "add_vote_delegations"("issue_id_p");
297 -- set voter count and mark issue as being calculated:
298 UPDATE "issue" SET
299 "state" = 'calculation',
300 "closed" = now(),
301 "voter_count" = (
302 SELECT coalesce(sum("weight"), 0)
303 FROM "direct_voter" WHERE "issue_id" = "issue_id_p"
304 )
305 WHERE "id" = "issue_id_p";
306 -- materialize battle_view:
307 -- NOTE: "closed" column of issue must be set at this point
308 DELETE FROM "battle" WHERE "issue_id" = "issue_id_p";
309 INSERT INTO "battle" (
310 "issue_id",
311 "winning_initiative_id", "losing_initiative_id",
312 "count"
313 ) SELECT
314 "issue_id",
315 "winning_initiative_id", "losing_initiative_id",
316 "count"
317 FROM "battle_view" WHERE "issue_id" = "issue_id_p";
318 -- copy "positive_votes" and "negative_votes" from "battle" table:
319 UPDATE "initiative" SET
320 "positive_votes" = "battle_win"."count",
321 "negative_votes" = "battle_lose"."count"
322 FROM "battle" AS "battle_win", "battle" AS "battle_lose"
323 WHERE
324 "battle_win"."issue_id" = "issue_id_p" AND
325 "battle_win"."winning_initiative_id" = "initiative"."id" AND
326 "battle_win"."losing_initiative_id" ISNULL AND
327 "battle_lose"."issue_id" = "issue_id_p" AND
328 "battle_lose"."losing_initiative_id" = "initiative"."id" AND
329 "battle_lose"."winning_initiative_id" ISNULL;
330 END;
331 $$;
333 CREATE OR REPLACE FUNCTION "clean_issue"("issue_id_p" "issue"."id"%TYPE)
334 RETURNS VOID
335 LANGUAGE 'plpgsql' VOLATILE AS $$
336 DECLARE
337 "issue_row" "issue"%ROWTYPE;
338 BEGIN
339 SELECT * INTO "issue_row"
340 FROM "issue" WHERE "id" = "issue_id_p"
341 FOR UPDATE;
342 IF "issue_row"."cleaned" ISNULL THEN
343 UPDATE "issue" SET
344 "state" = 'voting',
345 "closed" = NULL,
346 "ranks_available" = FALSE
347 WHERE "id" = "issue_id_p";
348 DELETE FROM "delegating_voter"
349 WHERE "issue_id" = "issue_id_p";
350 DELETE FROM "direct_voter"
351 WHERE "issue_id" = "issue_id_p";
352 DELETE FROM "delegating_interest_snapshot"
353 WHERE "issue_id" = "issue_id_p";
354 DELETE FROM "direct_interest_snapshot"
355 WHERE "issue_id" = "issue_id_p";
356 DELETE FROM "delegating_population_snapshot"
357 WHERE "issue_id" = "issue_id_p";
358 DELETE FROM "direct_population_snapshot"
359 WHERE "issue_id" = "issue_id_p";
360 DELETE FROM "non_voter"
361 WHERE "issue_id" = "issue_id_p";
362 DELETE FROM "delegation"
363 WHERE "issue_id" = "issue_id_p";
364 DELETE FROM "supporter"
365 WHERE "issue_id" = "issue_id_p";
366 UPDATE "issue" SET
367 "state" = "issue_row"."state",
368 "closed" = "issue_row"."closed",
369 "ranks_available" = "issue_row"."ranks_available",
370 "cleaned" = now()
371 WHERE "id" = "issue_id_p";
372 END IF;
373 RETURN;
374 END;
375 $$;
378 -- "non_voter" deletes "direct_voter" and vice versa
380 CREATE FUNCTION "non_voter_deletes_direct_voter_trigger"()
381 RETURNS TRIGGER
382 LANGUAGE 'plpgsql' VOLATILE AS $$
383 BEGIN
384 DELETE FROM "direct_voter"
385 WHERE "issue_id" = NEW."issue_id" AND "member_id" = NEW."member_id";
386 RETURN NULL;
387 END;
388 $$;
390 CREATE TRIGGER "non_voter_deletes_direct_voter"
391 AFTER INSERT OR UPDATE ON "non_voter"
392 FOR EACH ROW EXECUTE PROCEDURE
393 "non_voter_deletes_direct_voter_trigger"();
395 COMMENT ON FUNCTION "non_voter_deletes_direct_voter_trigger"() IS 'Implementation of trigger "non_voter_deletes_direct_voter" on table "non_voter"';
396 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")';
398 CREATE FUNCTION "direct_voter_deletes_non_voter_trigger"()
399 RETURNS TRIGGER
400 LANGUAGE 'plpgsql' VOLATILE AS $$
401 BEGIN
402 DELETE FROM "non_voter"
403 WHERE "issue_id" = NEW."issue_id" AND "member_id" = NEW."member_id";
404 RETURN NULL;
405 END;
406 $$;
408 CREATE TRIGGER "direct_voter_deletes_non_voter"
409 AFTER INSERT OR UPDATE ON "direct_voter"
410 FOR EACH ROW EXECUTE PROCEDURE
411 "direct_voter_deletes_non_voter_trigger"();
413 COMMENT ON FUNCTION "direct_voter_deletes_non_voter_trigger"() IS 'Implementation of trigger "direct_voter_deletes_non_voter" on table "direct_voter"';
414 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")';
417 -- different locking levels and different locking order to avoid deadlocks
419 CREATE OR REPLACE FUNCTION "lock_issue"
420 ( "issue_id_p" "issue"."id"%TYPE )
421 RETURNS VOID
422 LANGUAGE 'plpgsql' VOLATILE AS $$
423 BEGIN
424 -- The following locking order is used:
425 -- 1st) row-level lock on the issue
426 -- 2nd) table-level locks in order of occurrence in the core.sql file
427 PERFORM NULL FROM "issue" WHERE "id" = "issue_id_p" FOR UPDATE;
428 -- NOTE: The row-level exclusive lock in combination with the
429 -- share_row_lock_issue(_via_initiative)_trigger functions (which
430 -- acquire a row-level share lock on the issue) ensure that no data
431 -- is changed, which could affect calculation of snapshots or
432 -- counting of votes. Table "delegation" must be table-level-locked,
433 -- as it also contains issue- and global-scope delegations.
434 PERFORM NULL FROM "member" WHERE "active" FOR SHARE;
435 -- NOTE: As we later cause implicit row-level share locks on many
436 -- active members, we lock them before locking any other table
437 -- to avoid deadlocks
438 LOCK TABLE "member" IN SHARE MODE;
439 LOCK TABLE "privilege" IN SHARE MODE;
440 LOCK TABLE "membership" IN SHARE MODE;
441 LOCK TABLE "policy" IN SHARE MODE;
442 LOCK TABLE "delegation" IN SHARE MODE;
443 LOCK TABLE "direct_population_snapshot" IN EXCLUSIVE MODE;
444 LOCK TABLE "delegating_population_snapshot" IN EXCLUSIVE MODE;
445 LOCK TABLE "direct_interest_snapshot" IN EXCLUSIVE MODE;
446 LOCK TABLE "delegating_interest_snapshot" IN EXCLUSIVE MODE;
447 LOCK TABLE "direct_supporter_snapshot" IN EXCLUSIVE MODE;
448 RETURN;
449 END;
450 $$;
453 -- new comment on function "delete_private_data"()
455 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.';
458 -- NOTE: The first version of the previous update script didn't
459 -- remove the "vote_ratio" function.
460 -- The function is therefore removed here as well, if existent.
462 DROP FUNCTION IF EXISTS "vote_ratio"
463 ( "initiative"."positive_votes"%TYPE,
464 "initiative"."negative_votes"%TYPE );
467 COMMIT;