jbe@67: BEGIN; jbe@67: jbe@67: jbe@67: CREATE OR REPLACE VIEW "liquid_feedback_version" AS jbe@67: SELECT * FROM (VALUES ('1.2.5', 1, 2, 5)) jbe@67: AS "subquery"("string", "major", "minor", "revision"); jbe@67: jbe@67: jbe@67: CREATE FUNCTION "share_row_lock_issue_trigger"() jbe@67: RETURNS TRIGGER jbe@67: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@67: BEGIN jbe@67: IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN jbe@67: PERFORM NULL FROM "issue" WHERE "id" = OLD."issue_id" FOR SHARE; jbe@67: END IF; jbe@67: IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN jbe@67: PERFORM NULL FROM "issue" WHERE "id" = NEW."issue_id" FOR SHARE; jbe@67: RETURN NEW; jbe@67: ELSE jbe@67: RETURN OLD; jbe@67: END IF; jbe@67: END; jbe@67: $$; jbe@67: jbe@67: COMMENT ON FUNCTION "share_row_lock_issue_trigger"() IS 'Implementation of triggers "share_row_lock_issue" on multiple tables'; jbe@67: jbe@67: jbe@67: CREATE FUNCTION "share_row_lock_issue_via_initiative_trigger"() jbe@67: RETURNS TRIGGER jbe@67: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@67: BEGIN jbe@67: IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN jbe@67: PERFORM NULL FROM "issue" jbe@67: JOIN "initiative" ON "issue"."id" = "initiative"."issue_id" jbe@67: WHERE "initiative"."id" = OLD."initiative_id" jbe@67: FOR SHARE OF "issue"; jbe@67: END IF; jbe@67: IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN jbe@67: PERFORM NULL FROM "issue" jbe@67: JOIN "initiative" ON "issue"."id" = "initiative"."issue_id" jbe@67: WHERE "initiative"."id" = NEW."initiative_id" jbe@67: FOR SHARE OF "issue"; jbe@67: RETURN NEW; jbe@67: ELSE jbe@67: RETURN OLD; jbe@67: END IF; jbe@67: END; jbe@67: $$; jbe@67: jbe@67: COMMENT ON FUNCTION "share_row_lock_issue_trigger"() IS 'Implementation of trigger "share_row_lock_issue_via_initiative" on table "opinion"'; jbe@67: jbe@67: jbe@67: CREATE TRIGGER "share_row_lock_issue" jbe@67: BEFORE INSERT OR UPDATE OR DELETE ON "initiative" jbe@67: FOR EACH ROW EXECUTE PROCEDURE jbe@67: "share_row_lock_issue_trigger"(); jbe@67: jbe@67: CREATE TRIGGER "share_row_lock_issue" jbe@67: BEFORE INSERT OR UPDATE OR DELETE ON "interest" jbe@67: FOR EACH ROW EXECUTE PROCEDURE jbe@67: "share_row_lock_issue_trigger"(); jbe@67: jbe@67: CREATE TRIGGER "share_row_lock_issue" jbe@67: BEFORE INSERT OR UPDATE OR DELETE ON "supporter" jbe@67: FOR EACH ROW EXECUTE PROCEDURE jbe@67: "share_row_lock_issue_trigger"(); jbe@67: jbe@67: CREATE TRIGGER "share_row_lock_issue_via_initiative" jbe@67: BEFORE INSERT OR UPDATE OR DELETE ON "opinion" jbe@67: FOR EACH ROW EXECUTE PROCEDURE jbe@67: "share_row_lock_issue_via_initiative_trigger"(); jbe@67: jbe@67: CREATE TRIGGER "share_row_lock_issue" jbe@67: BEFORE INSERT OR UPDATE OR DELETE ON "direct_voter" jbe@67: FOR EACH ROW EXECUTE PROCEDURE jbe@67: "share_row_lock_issue_trigger"(); jbe@67: jbe@67: CREATE TRIGGER "share_row_lock_issue" jbe@67: BEFORE INSERT OR UPDATE OR DELETE ON "delegating_voter" jbe@67: FOR EACH ROW EXECUTE PROCEDURE jbe@67: "share_row_lock_issue_trigger"(); jbe@67: jbe@67: CREATE TRIGGER "share_row_lock_issue" jbe@67: BEFORE INSERT OR UPDATE OR DELETE ON "vote" jbe@67: FOR EACH ROW EXECUTE PROCEDURE jbe@67: "share_row_lock_issue_trigger"(); jbe@67: jbe@67: COMMENT ON TRIGGER "share_row_lock_issue" ON "initiative" IS 'See "lock_issue" function'; jbe@67: COMMENT ON TRIGGER "share_row_lock_issue" ON "interest" IS 'See "lock_issue" function'; jbe@67: COMMENT ON TRIGGER "share_row_lock_issue" ON "supporter" IS 'See "lock_issue" function'; jbe@67: COMMENT ON TRIGGER "share_row_lock_issue_via_initiative" ON "opinion" IS 'See "lock_issue" function'; jbe@67: COMMENT ON TRIGGER "share_row_lock_issue" ON "direct_voter" IS 'See "lock_issue" function'; jbe@67: COMMENT ON TRIGGER "share_row_lock_issue" ON "delegating_voter" IS 'See "lock_issue" function'; jbe@67: COMMENT ON TRIGGER "share_row_lock_issue" ON "vote" IS 'See "lock_issue" function'; jbe@67: jbe@67: jbe@67: CREATE FUNCTION "lock_issue" jbe@67: ( "issue_id_p" "issue"."id"%TYPE ) jbe@67: RETURNS VOID jbe@67: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@67: BEGIN jbe@67: LOCK TABLE "member" IN SHARE MODE; jbe@67: LOCK TABLE "membership" IN SHARE MODE; jbe@67: LOCK TABLE "policy" IN SHARE MODE; jbe@67: PERFORM NULL FROM "issue" WHERE "id" = "issue_id_p" FOR UPDATE; jbe@67: -- NOTE: The row-level exclusive lock in combination with the jbe@67: -- share_row_lock_issue(_via_initiative)_trigger functions (which jbe@67: -- acquire a row-level share lock on the issue) ensure that no data jbe@67: -- is changed, which could affect calculation of snapshots or jbe@67: -- counting of votes. Table "delegation" must be table-level-locked, jbe@67: -- as it also contains issue- and global-scope delegations. jbe@67: LOCK TABLE "delegation" IN SHARE MODE; jbe@67: LOCK TABLE "direct_population_snapshot" IN EXCLUSIVE MODE; jbe@67: LOCK TABLE "delegating_population_snapshot" IN EXCLUSIVE MODE; jbe@67: LOCK TABLE "direct_interest_snapshot" IN EXCLUSIVE MODE; jbe@67: LOCK TABLE "delegating_interest_snapshot" IN EXCLUSIVE MODE; jbe@67: LOCK TABLE "direct_supporter_snapshot" IN EXCLUSIVE MODE; jbe@67: RETURN; jbe@67: END; jbe@67: $$; jbe@67: jbe@67: COMMENT ON FUNCTION "lock_issue" jbe@67: ( "issue"."id"%TYPE ) jbe@67: IS 'Locks the issue and all other data which is used for calculating snapshots or counting votes.'; jbe@67: jbe@67: jbe@67: CREATE OR REPLACE FUNCTION "calculate_member_counts"() jbe@67: RETURNS VOID jbe@67: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@67: BEGIN jbe@67: LOCK TABLE "member" IN SHARE MODE; jbe@67: LOCK TABLE "member_count" IN EXCLUSIVE MODE; jbe@67: LOCK TABLE "area" IN EXCLUSIVE MODE; jbe@67: LOCK TABLE "membership" IN SHARE MODE; jbe@67: DELETE FROM "member_count"; jbe@67: INSERT INTO "member_count" ("total_count") jbe@67: SELECT "total_count" FROM "member_count_view"; jbe@67: UPDATE "area" SET jbe@67: "direct_member_count" = "view"."direct_member_count", jbe@67: "member_weight" = "view"."member_weight", jbe@67: "autoreject_weight" = "view"."autoreject_weight" jbe@67: FROM "area_member_count" AS "view" jbe@67: WHERE "view"."area_id" = "area"."id"; jbe@67: RETURN; jbe@67: END; jbe@67: $$; jbe@67: jbe@67: CREATE OR REPLACE FUNCTION "create_snapshot" jbe@67: ( "issue_id_p" "issue"."id"%TYPE ) jbe@67: RETURNS VOID jbe@67: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@67: DECLARE jbe@67: "initiative_id_v" "initiative"."id"%TYPE; jbe@67: "suggestion_id_v" "suggestion"."id"%TYPE; jbe@67: BEGIN jbe@67: PERFORM "lock_issue"("issue_id_p"); jbe@67: PERFORM "create_population_snapshot"("issue_id_p"); jbe@67: PERFORM "create_interest_snapshot"("issue_id_p"); jbe@67: UPDATE "issue" SET jbe@67: "snapshot" = now(), jbe@67: "latest_snapshot_event" = 'periodic', jbe@67: "population" = ( jbe@67: SELECT coalesce(sum("weight"), 0) jbe@67: FROM "direct_population_snapshot" jbe@67: WHERE "issue_id" = "issue_id_p" jbe@67: AND "event" = 'periodic' jbe@67: ), jbe@67: "vote_now" = ( jbe@67: SELECT coalesce(sum("weight"), 0) jbe@67: FROM "direct_interest_snapshot" jbe@67: WHERE "issue_id" = "issue_id_p" jbe@67: AND "event" = 'periodic' jbe@67: AND "voting_requested" = TRUE jbe@67: ), jbe@67: "vote_later" = ( jbe@67: SELECT coalesce(sum("weight"), 0) jbe@67: FROM "direct_interest_snapshot" jbe@67: WHERE "issue_id" = "issue_id_p" jbe@67: AND "event" = 'periodic' jbe@67: AND "voting_requested" = FALSE jbe@67: ) jbe@67: WHERE "id" = "issue_id_p"; jbe@67: FOR "initiative_id_v" IN jbe@67: SELECT "id" FROM "initiative" WHERE "issue_id" = "issue_id_p" jbe@67: LOOP jbe@67: UPDATE "initiative" SET jbe@67: "supporter_count" = ( jbe@67: SELECT coalesce(sum("di"."weight"), 0) jbe@67: FROM "direct_interest_snapshot" AS "di" jbe@67: JOIN "direct_supporter_snapshot" AS "ds" jbe@67: ON "di"."member_id" = "ds"."member_id" jbe@67: WHERE "di"."issue_id" = "issue_id_p" jbe@67: AND "di"."event" = 'periodic' jbe@67: AND "ds"."initiative_id" = "initiative_id_v" jbe@67: AND "ds"."event" = 'periodic' jbe@67: ), jbe@67: "informed_supporter_count" = ( jbe@67: SELECT coalesce(sum("di"."weight"), 0) jbe@67: FROM "direct_interest_snapshot" AS "di" jbe@67: JOIN "direct_supporter_snapshot" AS "ds" jbe@67: ON "di"."member_id" = "ds"."member_id" jbe@67: WHERE "di"."issue_id" = "issue_id_p" jbe@67: AND "di"."event" = 'periodic' jbe@67: AND "ds"."initiative_id" = "initiative_id_v" jbe@67: AND "ds"."event" = 'periodic' jbe@67: AND "ds"."informed" jbe@67: ), jbe@67: "satisfied_supporter_count" = ( jbe@67: SELECT coalesce(sum("di"."weight"), 0) jbe@67: FROM "direct_interest_snapshot" AS "di" jbe@67: JOIN "direct_supporter_snapshot" AS "ds" jbe@67: ON "di"."member_id" = "ds"."member_id" jbe@67: WHERE "di"."issue_id" = "issue_id_p" jbe@67: AND "di"."event" = 'periodic' jbe@67: AND "ds"."initiative_id" = "initiative_id_v" jbe@67: AND "ds"."event" = 'periodic' jbe@67: AND "ds"."satisfied" jbe@67: ), jbe@67: "satisfied_informed_supporter_count" = ( jbe@67: SELECT coalesce(sum("di"."weight"), 0) jbe@67: FROM "direct_interest_snapshot" AS "di" jbe@67: JOIN "direct_supporter_snapshot" AS "ds" jbe@67: ON "di"."member_id" = "ds"."member_id" jbe@67: WHERE "di"."issue_id" = "issue_id_p" jbe@67: AND "di"."event" = 'periodic' jbe@67: AND "ds"."initiative_id" = "initiative_id_v" jbe@67: AND "ds"."event" = 'periodic' jbe@67: AND "ds"."informed" jbe@67: AND "ds"."satisfied" jbe@67: ) jbe@67: WHERE "id" = "initiative_id_v"; jbe@67: FOR "suggestion_id_v" IN jbe@67: SELECT "id" FROM "suggestion" jbe@67: WHERE "initiative_id" = "initiative_id_v" jbe@67: LOOP jbe@67: UPDATE "suggestion" SET jbe@67: "minus2_unfulfilled_count" = ( jbe@67: SELECT coalesce(sum("snapshot"."weight"), 0) jbe@67: FROM "issue" CROSS JOIN "opinion" jbe@67: JOIN "direct_interest_snapshot" AS "snapshot" jbe@67: ON "snapshot"."issue_id" = "issue"."id" jbe@67: AND "snapshot"."event" = "issue"."latest_snapshot_event" jbe@67: AND "snapshot"."member_id" = "opinion"."member_id" jbe@67: WHERE "issue"."id" = "issue_id_p" jbe@67: AND "opinion"."suggestion_id" = "suggestion_id_v" jbe@67: AND "opinion"."degree" = -2 jbe@67: AND "opinion"."fulfilled" = FALSE jbe@67: ), jbe@67: "minus2_fulfilled_count" = ( jbe@67: SELECT coalesce(sum("snapshot"."weight"), 0) jbe@67: FROM "issue" CROSS JOIN "opinion" jbe@67: JOIN "direct_interest_snapshot" AS "snapshot" jbe@67: ON "snapshot"."issue_id" = "issue"."id" jbe@67: AND "snapshot"."event" = "issue"."latest_snapshot_event" jbe@67: AND "snapshot"."member_id" = "opinion"."member_id" jbe@67: WHERE "issue"."id" = "issue_id_p" jbe@67: AND "opinion"."suggestion_id" = "suggestion_id_v" jbe@67: AND "opinion"."degree" = -2 jbe@67: AND "opinion"."fulfilled" = TRUE jbe@67: ), jbe@67: "minus1_unfulfilled_count" = ( jbe@67: SELECT coalesce(sum("snapshot"."weight"), 0) jbe@67: FROM "issue" CROSS JOIN "opinion" jbe@67: JOIN "direct_interest_snapshot" AS "snapshot" jbe@67: ON "snapshot"."issue_id" = "issue"."id" jbe@67: AND "snapshot"."event" = "issue"."latest_snapshot_event" jbe@67: AND "snapshot"."member_id" = "opinion"."member_id" jbe@67: WHERE "issue"."id" = "issue_id_p" jbe@67: AND "opinion"."suggestion_id" = "suggestion_id_v" jbe@67: AND "opinion"."degree" = -1 jbe@67: AND "opinion"."fulfilled" = FALSE jbe@67: ), jbe@67: "minus1_fulfilled_count" = ( jbe@67: SELECT coalesce(sum("snapshot"."weight"), 0) jbe@67: FROM "issue" CROSS JOIN "opinion" jbe@67: JOIN "direct_interest_snapshot" AS "snapshot" jbe@67: ON "snapshot"."issue_id" = "issue"."id" jbe@67: AND "snapshot"."event" = "issue"."latest_snapshot_event" jbe@67: AND "snapshot"."member_id" = "opinion"."member_id" jbe@67: WHERE "issue"."id" = "issue_id_p" jbe@67: AND "opinion"."suggestion_id" = "suggestion_id_v" jbe@67: AND "opinion"."degree" = -1 jbe@67: AND "opinion"."fulfilled" = TRUE jbe@67: ), jbe@67: "plus1_unfulfilled_count" = ( jbe@67: SELECT coalesce(sum("snapshot"."weight"), 0) jbe@67: FROM "issue" CROSS JOIN "opinion" jbe@67: JOIN "direct_interest_snapshot" AS "snapshot" jbe@67: ON "snapshot"."issue_id" = "issue"."id" jbe@67: AND "snapshot"."event" = "issue"."latest_snapshot_event" jbe@67: AND "snapshot"."member_id" = "opinion"."member_id" jbe@67: WHERE "issue"."id" = "issue_id_p" jbe@67: AND "opinion"."suggestion_id" = "suggestion_id_v" jbe@67: AND "opinion"."degree" = 1 jbe@67: AND "opinion"."fulfilled" = FALSE jbe@67: ), jbe@67: "plus1_fulfilled_count" = ( jbe@67: SELECT coalesce(sum("snapshot"."weight"), 0) jbe@67: FROM "issue" CROSS JOIN "opinion" jbe@67: JOIN "direct_interest_snapshot" AS "snapshot" jbe@67: ON "snapshot"."issue_id" = "issue"."id" jbe@67: AND "snapshot"."event" = "issue"."latest_snapshot_event" jbe@67: AND "snapshot"."member_id" = "opinion"."member_id" jbe@67: WHERE "issue"."id" = "issue_id_p" jbe@67: AND "opinion"."suggestion_id" = "suggestion_id_v" jbe@67: AND "opinion"."degree" = 1 jbe@67: AND "opinion"."fulfilled" = TRUE jbe@67: ), jbe@67: "plus2_unfulfilled_count" = ( jbe@67: SELECT coalesce(sum("snapshot"."weight"), 0) jbe@67: FROM "issue" CROSS JOIN "opinion" jbe@67: JOIN "direct_interest_snapshot" AS "snapshot" jbe@67: ON "snapshot"."issue_id" = "issue"."id" jbe@67: AND "snapshot"."event" = "issue"."latest_snapshot_event" jbe@67: AND "snapshot"."member_id" = "opinion"."member_id" jbe@67: WHERE "issue"."id" = "issue_id_p" jbe@67: AND "opinion"."suggestion_id" = "suggestion_id_v" jbe@67: AND "opinion"."degree" = 2 jbe@67: AND "opinion"."fulfilled" = FALSE jbe@67: ), jbe@67: "plus2_fulfilled_count" = ( jbe@67: SELECT coalesce(sum("snapshot"."weight"), 0) jbe@67: FROM "issue" CROSS JOIN "opinion" jbe@67: JOIN "direct_interest_snapshot" AS "snapshot" jbe@67: ON "snapshot"."issue_id" = "issue"."id" jbe@67: AND "snapshot"."event" = "issue"."latest_snapshot_event" jbe@67: AND "snapshot"."member_id" = "opinion"."member_id" jbe@67: WHERE "issue"."id" = "issue_id_p" jbe@67: AND "opinion"."suggestion_id" = "suggestion_id_v" jbe@67: AND "opinion"."degree" = 2 jbe@67: AND "opinion"."fulfilled" = TRUE jbe@67: ) jbe@67: WHERE "suggestion"."id" = "suggestion_id_v"; jbe@67: END LOOP; jbe@67: END LOOP; jbe@67: RETURN; jbe@67: END; jbe@67: $$; jbe@67: jbe@67: CREATE OR REPLACE FUNCTION "close_voting"("issue_id_p" "issue"."id"%TYPE) jbe@67: RETURNS VOID jbe@67: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@67: DECLARE jbe@67: "issue_row" "issue"%ROWTYPE; jbe@67: "member_id_v" "member"."id"%TYPE; jbe@67: BEGIN jbe@67: PERFORM "lock_issue"("issue_id_p"); jbe@67: SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p"; jbe@67: DELETE FROM "delegating_voter" jbe@67: WHERE "issue_id" = "issue_id_p"; jbe@67: DELETE FROM "direct_voter" jbe@67: WHERE "issue_id" = "issue_id_p" jbe@67: AND "autoreject" = TRUE; jbe@67: DELETE FROM "direct_voter" USING "member" jbe@67: WHERE "direct_voter"."member_id" = "member"."id" jbe@67: AND "direct_voter"."issue_id" = "issue_id_p" jbe@67: AND "member"."active" = FALSE; jbe@67: UPDATE "direct_voter" SET "weight" = 1 jbe@67: WHERE "issue_id" = "issue_id_p"; jbe@67: PERFORM "add_vote_delegations"("issue_id_p"); jbe@67: FOR "member_id_v" IN jbe@67: SELECT "interest"."member_id" jbe@67: FROM "interest" jbe@67: LEFT JOIN "direct_voter" jbe@67: ON "interest"."member_id" = "direct_voter"."member_id" jbe@67: AND "interest"."issue_id" = "direct_voter"."issue_id" jbe@67: LEFT JOIN "delegating_voter" jbe@67: ON "interest"."member_id" = "delegating_voter"."member_id" jbe@67: AND "interest"."issue_id" = "delegating_voter"."issue_id" jbe@67: WHERE "interest"."issue_id" = "issue_id_p" jbe@67: AND "interest"."autoreject" = TRUE jbe@67: AND "direct_voter"."member_id" ISNULL jbe@67: AND "delegating_voter"."member_id" ISNULL jbe@67: UNION SELECT "membership"."member_id" jbe@67: FROM "membership" jbe@67: LEFT JOIN "interest" jbe@67: ON "membership"."member_id" = "interest"."member_id" jbe@67: AND "interest"."issue_id" = "issue_id_p" jbe@67: LEFT JOIN "direct_voter" jbe@67: ON "membership"."member_id" = "direct_voter"."member_id" jbe@67: AND "direct_voter"."issue_id" = "issue_id_p" jbe@67: LEFT JOIN "delegating_voter" jbe@67: ON "membership"."member_id" = "delegating_voter"."member_id" jbe@67: AND "delegating_voter"."issue_id" = "issue_id_p" jbe@67: WHERE "membership"."area_id" = "issue_row"."area_id" jbe@67: AND "membership"."autoreject" = TRUE jbe@67: AND "interest"."autoreject" ISNULL jbe@67: AND "direct_voter"."member_id" ISNULL jbe@67: AND "delegating_voter"."member_id" ISNULL jbe@67: LOOP jbe@67: INSERT INTO "direct_voter" jbe@67: ("member_id", "issue_id", "weight", "autoreject") VALUES jbe@67: ("member_id_v", "issue_id_p", 1, TRUE); jbe@67: INSERT INTO "vote" ( jbe@67: "member_id", jbe@67: "issue_id", jbe@67: "initiative_id", jbe@67: "grade" jbe@67: ) SELECT jbe@67: "member_id_v" AS "member_id", jbe@67: "issue_id_p" AS "issue_id", jbe@67: "id" AS "initiative_id", jbe@67: -1 AS "grade" jbe@67: FROM "initiative" WHERE "issue_id" = "issue_id_p"; jbe@67: END LOOP; jbe@67: PERFORM "add_vote_delegations"("issue_id_p"); jbe@67: UPDATE "issue" SET jbe@67: "closed" = now(), jbe@67: "voter_count" = ( jbe@67: SELECT coalesce(sum("weight"), 0) jbe@67: FROM "direct_voter" WHERE "issue_id" = "issue_id_p" jbe@67: ) jbe@67: WHERE "id" = "issue_id_p"; jbe@67: UPDATE "initiative" SET jbe@67: "positive_votes" = "vote_counts"."positive_votes", jbe@67: "negative_votes" = "vote_counts"."negative_votes", jbe@67: "agreed" = CASE WHEN "majority_strict" THEN jbe@67: "vote_counts"."positive_votes" * "majority_den" > jbe@67: "majority_num" * jbe@67: ("vote_counts"."positive_votes"+"vote_counts"."negative_votes") jbe@67: ELSE jbe@67: "vote_counts"."positive_votes" * "majority_den" >= jbe@67: "majority_num" * jbe@67: ("vote_counts"."positive_votes"+"vote_counts"."negative_votes") jbe@67: END jbe@67: FROM jbe@67: ( SELECT jbe@67: "initiative"."id" AS "initiative_id", jbe@67: coalesce( jbe@67: sum( jbe@67: CASE WHEN "grade" > 0 THEN "direct_voter"."weight" ELSE 0 END jbe@67: ), jbe@67: 0 jbe@67: ) AS "positive_votes", jbe@67: coalesce( jbe@67: sum( jbe@67: CASE WHEN "grade" < 0 THEN "direct_voter"."weight" ELSE 0 END jbe@67: ), jbe@67: 0 jbe@67: ) AS "negative_votes" jbe@67: FROM "initiative" jbe@67: JOIN "issue" ON "initiative"."issue_id" = "issue"."id" jbe@67: JOIN "policy" ON "issue"."policy_id" = "policy"."id" jbe@67: LEFT JOIN "direct_voter" jbe@67: ON "direct_voter"."issue_id" = "initiative"."issue_id" jbe@67: LEFT JOIN "vote" jbe@67: ON "vote"."initiative_id" = "initiative"."id" jbe@67: AND "vote"."member_id" = "direct_voter"."member_id" jbe@67: WHERE "initiative"."issue_id" = "issue_id_p" jbe@67: AND "initiative"."admitted" -- NOTE: NULL case is handled too jbe@67: GROUP BY "initiative"."id" jbe@67: ) AS "vote_counts", jbe@67: "issue", jbe@67: "policy" jbe@67: WHERE "vote_counts"."initiative_id" = "initiative"."id" jbe@67: AND "issue"."id" = "initiative"."issue_id" jbe@67: AND "policy"."id" = "issue"."policy_id"; jbe@67: -- NOTE: "closed" column of issue must be set at this point jbe@67: DELETE FROM "battle" WHERE "issue_id" = "issue_id_p"; jbe@67: INSERT INTO "battle" ( jbe@67: "issue_id", jbe@67: "winning_initiative_id", "losing_initiative_id", jbe@67: "count" jbe@67: ) SELECT jbe@67: "issue_id", jbe@67: "winning_initiative_id", "losing_initiative_id", jbe@67: "count" jbe@67: FROM "battle_view" WHERE "issue_id" = "issue_id_p"; jbe@67: END; jbe@67: $$; jbe@67: jbe@67: CREATE OR REPLACE FUNCTION "check_issue" jbe@67: ( "issue_id_p" "issue"."id"%TYPE ) jbe@67: RETURNS VOID jbe@67: LANGUAGE 'plpgsql' VOLATILE AS $$ jbe@67: DECLARE jbe@67: "issue_row" "issue"%ROWTYPE; jbe@67: "policy_row" "policy"%ROWTYPE; jbe@67: "voting_requested_v" BOOLEAN; jbe@67: BEGIN jbe@67: PERFORM "lock_issue"("issue_id_p"); jbe@67: SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p"; jbe@67: -- only process open issues: jbe@67: IF "issue_row"."closed" ISNULL THEN jbe@67: SELECT * INTO "policy_row" FROM "policy" jbe@67: WHERE "id" = "issue_row"."policy_id"; jbe@67: -- create a snapshot, unless issue is already fully frozen: jbe@67: IF "issue_row"."fully_frozen" ISNULL THEN jbe@67: PERFORM "create_snapshot"("issue_id_p"); jbe@67: SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p"; jbe@67: END IF; jbe@67: -- eventually close or accept issues, which have not been accepted: jbe@67: IF "issue_row"."accepted" ISNULL THEN jbe@67: IF EXISTS ( jbe@67: SELECT NULL FROM "initiative" jbe@67: WHERE "issue_id" = "issue_id_p" jbe@67: AND "supporter_count" > 0 jbe@67: AND "supporter_count" * "policy_row"."issue_quorum_den" jbe@67: >= "issue_row"."population" * "policy_row"."issue_quorum_num" jbe@67: ) THEN jbe@67: -- accept issues, if supporter count is high enough jbe@67: PERFORM "set_snapshot_event"("issue_id_p", 'end_of_admission'); jbe@67: "issue_row"."accepted" = now(); -- NOTE: "issue_row" used later jbe@67: UPDATE "issue" SET "accepted" = "issue_row"."accepted" jbe@67: WHERE "id" = "issue_row"."id"; jbe@67: ELSIF jbe@67: now() >= "issue_row"."created" + "issue_row"."admission_time" jbe@67: THEN jbe@67: -- close issues, if admission time has expired jbe@67: PERFORM "set_snapshot_event"("issue_id_p", 'end_of_admission'); jbe@67: UPDATE "issue" SET "closed" = now() jbe@67: WHERE "id" = "issue_row"."id"; jbe@67: END IF; jbe@67: END IF; jbe@67: -- eventually half freeze issues: jbe@67: IF jbe@67: -- NOTE: issue can't be closed at this point, if it has been accepted jbe@67: "issue_row"."accepted" NOTNULL AND jbe@67: "issue_row"."half_frozen" ISNULL jbe@67: THEN jbe@67: SELECT jbe@67: CASE jbe@67: WHEN "vote_now" * 2 > "issue_row"."population" THEN jbe@67: TRUE jbe@67: WHEN "vote_later" * 2 > "issue_row"."population" THEN jbe@67: FALSE jbe@67: ELSE NULL jbe@67: END jbe@67: INTO "voting_requested_v" jbe@67: FROM "issue" WHERE "id" = "issue_id_p"; jbe@67: IF jbe@67: "voting_requested_v" OR ( jbe@67: "voting_requested_v" ISNULL AND jbe@67: now() >= "issue_row"."accepted" + "issue_row"."discussion_time" jbe@67: ) jbe@67: THEN jbe@67: PERFORM "set_snapshot_event"("issue_id_p", 'half_freeze'); jbe@67: "issue_row"."half_frozen" = now(); -- NOTE: "issue_row" used later jbe@67: UPDATE "issue" SET "half_frozen" = "issue_row"."half_frozen" jbe@67: WHERE "id" = "issue_row"."id"; jbe@67: END IF; jbe@67: END IF; jbe@67: -- close issues after some time, if all initiatives have been revoked: jbe@67: IF jbe@67: "issue_row"."closed" ISNULL AND jbe@67: NOT EXISTS ( jbe@67: -- all initiatives are revoked jbe@67: SELECT NULL FROM "initiative" jbe@67: WHERE "issue_id" = "issue_id_p" AND "revoked" ISNULL jbe@67: ) AND ( jbe@67: NOT EXISTS ( jbe@67: -- and no initiatives have been revoked lately jbe@67: SELECT NULL FROM "initiative" jbe@67: WHERE "issue_id" = "issue_id_p" jbe@67: AND now() < "revoked" + "issue_row"."verification_time" jbe@67: ) OR ( jbe@67: -- or verification time has elapsed jbe@67: "issue_row"."half_frozen" NOTNULL AND jbe@67: "issue_row"."fully_frozen" ISNULL AND jbe@67: now() >= "issue_row"."half_frozen" + "issue_row"."verification_time" jbe@67: ) jbe@67: ) jbe@67: THEN jbe@67: "issue_row"."closed" = now(); -- NOTE: "issue_row" used later jbe@67: UPDATE "issue" SET "closed" = "issue_row"."closed" jbe@67: WHERE "id" = "issue_row"."id"; jbe@67: END IF; jbe@67: -- fully freeze issue after verification time: jbe@67: IF jbe@67: "issue_row"."half_frozen" NOTNULL AND jbe@67: "issue_row"."fully_frozen" ISNULL AND jbe@67: "issue_row"."closed" ISNULL AND jbe@67: now() >= "issue_row"."half_frozen" + "issue_row"."verification_time" jbe@67: THEN jbe@67: PERFORM "freeze_after_snapshot"("issue_id_p"); jbe@67: -- NOTE: "issue" might change, thus "issue_row" has to be updated below jbe@67: END IF; jbe@67: SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p"; jbe@67: -- close issue by calling close_voting(...) after voting time: jbe@67: IF jbe@67: "issue_row"."closed" ISNULL AND jbe@67: "issue_row"."fully_frozen" NOTNULL AND jbe@67: now() >= "issue_row"."fully_frozen" + "issue_row"."voting_time" jbe@67: THEN jbe@67: PERFORM "close_voting"("issue_id_p"); jbe@67: END IF; jbe@67: END IF; jbe@67: RETURN; jbe@67: END; jbe@67: $$; jbe@67: jbe@67: jbe@67: DROP FUNCTION "global_lock"(); jbe@67: jbe@67: jbe@67: COMMIT;