
poelzi@79: BEGIN;
poelzi@79:  
poelzi@79: CREATE OR REPLACE VIEW "liquid_feedback_version" AS
jbe@86:   SELECT * FROM (VALUES ('1.3.0', 1, 3, 0))
poelzi@79:   AS "subquery"("string", "major", "minor", "revision");
poelzi@79: 
jbe@86: 
jbe@86: -- update comment for column "fully_frozen" of table "issue"
jbe@86: 
jbe@86: 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@86: 
jbe@86: 
jbe@86: -- update comment for column "autoreject" of table "membership"
jbe@86: 
jbe@86: COMMENT ON COLUMN "membership"."autoreject" IS 'TRUE = member votes against all initiatives, if he is neither direct_ or delegating_voter; Entries in the "interest" table can override this setting.';
jbe@86: 
jbe@86: 
jbe@86: -- allow column "autoreject" of table "interest" to be NULL
jbe@86: -- (thus defaulting to "membership")
jbe@86: 
jbe@86: ALTER TABLE "interest" ALTER COLUMN "autoreject" DROP NOT NULL;
jbe@86: 
jbe@86: 
jbe@86: -- new table "ignored_issue" to allow members to ignore particular issues in certain states
jbe@86: 
jbe@86: CREATE TABLE "ignored_issue" (
jbe@86:         PRIMARY KEY ("issue_id", "member_id"),
jbe@86:         "issue_id"              INT4            REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
jbe@86:         "member_id"             INT4            REFERENCES "member" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
jbe@86:         "new"                   BOOLEAN         NOT NULL DEFAULT FALSE,
jbe@86:         "accepted"              BOOLEAN         NOT NULL DEFAULT FALSE,
jbe@86:         "half_frozen"           BOOLEAN         NOT NULL DEFAULT FALSE,
jbe@86:         "fully_frozen"          BOOLEAN         NOT NULL DEFAULT FALSE );
jbe@86: CREATE INDEX "ignored_issue_member_id_idx" ON "ignored_issue" ("member_id");
jbe@86: 
jbe@86: COMMENT ON TABLE "ignored_issue" IS 'Table to store member specific options to ignore issues in selected states';
jbe@86: 
jbe@93: COMMENT ON COLUMN "ignored_issue"."new"          IS 'Apply when issue is neither closed nor accepted';
jbe@93: COMMENT ON COLUMN "ignored_issue"."accepted"     IS 'Apply when issue is accepted but not (half_)frozen or closed';
jbe@93: COMMENT ON COLUMN "ignored_issue"."half_frozen"  IS 'Apply when issue is half_frozen but not fully_frozen or closed';
jbe@93: COMMENT ON COLUMN "ignored_issue"."fully_frozen" IS 'Apply when issue is fully_frozen (in voting) and not closed';
jbe@86: 
jbe@86: 
jbe@86: -- allow area and issue delegations with trustee_id set to NULL
jbe@86: -- (indicating that global or area delegation is void for that area or issue)
jbe@86: 
jbe@86: ALTER TABLE "delegation" ALTER COLUMN "trustee_id" DROP NOT NULL;
jbe@86: 
jbe@86: ALTER TABLE "delegation" ADD CONSTRAINT "no_global_delegation_to_null"
jbe@86:   CHECK ("trustee_id" NOTNULL OR "scope" != 'global');
jbe@86: 
jbe@86: 
jbe@86: -- disable and delete "copy_autoreject" trigger on table "interest"
jbe@86: 
jbe@86: DROP TRIGGER "copy_autoreject" ON "interest";
jbe@86: DROP FUNCTION "copy_autoreject_trigger"();
jbe@86: 
jbe@86: 
jbe@86: -- update comments on delegation views
jbe@86: 
jbe@86: COMMENT ON VIEW "active_delegation" IS 'Helper view for views "global_delegation", "area_delegation" and "issue_delegation": Contains delegations where the truster_id refers to an active member and includes those delegations where trustee_id is NULL';
jbe@86: 
jbe@86: COMMENT ON VIEW "area_delegation" IS 'Resulting area delegations from active members; can include rows with trustee_id set to NULL';
jbe@86: 
jbe@86: COMMENT ON VIEW "issue_delegation" IS 'Resulting issue delegations from active members; can include rows with trustee_id set to NULL';
jbe@86: 
jbe@86: 
jbe@93: -- support for explicitly disabled delegations in "delegation_chain" functions
jbe@86: 
jbe@86: DROP FUNCTION "delegation_chain"
jbe@86:   ( "member"."id"%TYPE,
jbe@86:     "area"."id"%TYPE,
jbe@86:     "issue"."id"%TYPE );
jbe@86: 
jbe@86: DROP FUNCTION "delegation_chain"
jbe@86:   ( "member"."id"%TYPE,
jbe@86:     "area"."id"%TYPE,
jbe@86:     "issue"."id"%TYPE,
jbe@86:     "member"."id"%TYPE );
jbe@86: 
jbe@86: DROP TYPE "delegation_chain_row";
jbe@86: 
jbe@86: CREATE TYPE "delegation_chain_row" AS (
jbe@86:         "index"                 INT4,
jbe@86:         "member_id"             INT4,
jbe@86:         "member_active"         BOOLEAN,
jbe@86:         "participation"         BOOLEAN,
jbe@86:         "overridden"            BOOLEAN,
jbe@86:         "scope_in"              "delegation_scope",
jbe@86:         "scope_out"             "delegation_scope",
jbe@86:         "disabled_out"          BOOLEAN,
jbe@86:         "loop"                  "delegation_chain_loop_tag" );
jbe@86: 
jbe@86: COMMENT ON TYPE "delegation_chain_row" IS 'Type of rows returned by "delegation_chain"(...) functions';
jbe@86: 
jbe@86: COMMENT ON COLUMN "delegation_chain_row"."index"         IS 'Index starting with 0 and counting up';
jbe@86: 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@86: COMMENT ON COLUMN "delegation_chain_row"."overridden"    IS 'True, if an entry with lower index has "participation" set to true';
jbe@86: COMMENT ON COLUMN "delegation_chain_row"."scope_in"      IS 'Scope of used incoming delegation';
jbe@86: COMMENT ON COLUMN "delegation_chain_row"."scope_out"     IS 'Scope of used outgoing delegation';
jbe@86: COMMENT ON COLUMN "delegation_chain_row"."disabled_out"  IS 'Outgoing delegation is explicitly disabled by a delegation with trustee_id set to NULL';
jbe@86: COMMENT ON COLUMN "delegation_chain_row"."loop"          IS 'Not null, if member is part of a loop, see "delegation_chain_loop_tag" type';
jbe@86: 
jbe@86: 
jbe@86: CREATE FUNCTION "delegation_chain"
jbe@86:   ( "member_id_p"           "member"."id"%TYPE,
jbe@86:     "area_id_p"             "area"."id"%TYPE,
jbe@86:     "issue_id_p"            "issue"."id"%TYPE,
jbe@86:     "simulate_trustee_id_p" "member"."id"%TYPE )
jbe@86:   RETURNS SETOF "delegation_chain_row"
jbe@86:   LANGUAGE 'plpgsql' STABLE AS $$
jbe@86:     DECLARE
jbe@86:       "issue_row"          "issue"%ROWTYPE;
jbe@86:       "visited_member_ids" INT4[];  -- "member"."id"%TYPE[]
jbe@86:       "loop_member_id_v"   "member"."id"%TYPE;
jbe@86:       "output_row"         "delegation_chain_row";
jbe@86:       "output_rows"        "delegation_chain_row"[];
jbe@86:       "delegation_row"     "delegation"%ROWTYPE;
jbe@86:       "row_count"          INT4;
jbe@86:       "i"                  INT4;
jbe@86:       "loop_v"             BOOLEAN;
jbe@86:     BEGIN
jbe@86:       SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p";
jbe@86:       "visited_member_ids" := '{}';
jbe@86:       "loop_member_id_v"   := NULL;
jbe@86:       "output_rows"        := '{}';
jbe@86:       "output_row"."index"         := 0;
jbe@86:       "output_row"."member_id"     := "member_id_p";
jbe@86:       "output_row"."member_active" := TRUE;
jbe@86:       "output_row"."participation" := FALSE;
jbe@86:       "output_row"."overridden"    := FALSE;
jbe@86:       "output_row"."disabled_out"  := FALSE;
jbe@86:       "output_row"."scope_out"     := NULL;
jbe@86:       LOOP
jbe@86:         IF "visited_member_ids" @> ARRAY["output_row"."member_id"] THEN
jbe@86:           "loop_member_id_v" := "output_row"."member_id";
jbe@86:         ELSE
jbe@86:           "visited_member_ids" :=
jbe@86:             "visited_member_ids" || "output_row"."member_id";
jbe@86:         END IF;
jbe@86:         IF "output_row"."participation" THEN
jbe@86:           "output_row"."overridden" := TRUE;
jbe@86:         END IF;
jbe@86:         "output_row"."scope_in" := "output_row"."scope_out";
jbe@86:         IF EXISTS (
jbe@86:           SELECT NULL FROM "member" 
jbe@86:           WHERE "id" = "output_row"."member_id" AND "active"
jbe@86:         ) THEN
jbe@86:           IF "area_id_p" ISNULL AND "issue_id_p" ISNULL THEN
jbe@86:             SELECT * INTO "delegation_row" FROM "delegation"
jbe@86:               WHERE "truster_id" = "output_row"."member_id"
jbe@86:               AND "scope" = 'global';
jbe@86:           ELSIF "area_id_p" NOTNULL AND "issue_id_p" ISNULL THEN
jbe@86:             "output_row"."participation" := EXISTS (
jbe@86:               SELECT NULL FROM "membership"
jbe@86:               WHERE "area_id" = "area_id_p"
jbe@86:               AND "member_id" = "output_row"."member_id"
jbe@86:             );
jbe@86:             SELECT * INTO "delegation_row" FROM "delegation"
jbe@86:               WHERE "truster_id" = "output_row"."member_id"
jbe@86:               AND ("scope" = 'global' OR "area_id" = "area_id_p")
jbe@86:               ORDER BY "scope" DESC;
jbe@86:           ELSIF "area_id_p" ISNULL AND "issue_id_p" NOTNULL THEN
jbe@86:             "output_row"."participation" := EXISTS (
jbe@86:               SELECT NULL FROM "interest"
jbe@86:               WHERE "issue_id" = "issue_id_p"
jbe@86:               AND "member_id" = "output_row"."member_id"
jbe@86:             );
jbe@86:             SELECT * INTO "delegation_row" FROM "delegation"
jbe@86:               WHERE "truster_id" = "output_row"."member_id"
jbe@86:               AND ("scope" = 'global' OR
jbe@86:                 "area_id" = "issue_row"."area_id" OR
jbe@86:                 "issue_id" = "issue_id_p"
jbe@86:               )
jbe@86:               ORDER BY "scope" DESC;
jbe@86:           ELSE
jbe@86:             RAISE EXCEPTION 'Either area_id or issue_id or both must be NULL.';
jbe@86:           END IF;
jbe@86:         ELSE
jbe@86:           "output_row"."member_active" := FALSE;
jbe@86:           "output_row"."participation" := FALSE;
jbe@86:           "output_row"."scope_out"     := NULL;
jbe@86:           "delegation_row" := ROW(NULL);
jbe@86:         END IF;
jbe@86:         IF
jbe@86:           "output_row"."member_id" = "member_id_p" AND
jbe@86:           "simulate_trustee_id_p" NOTNULL
jbe@86:         THEN
jbe@86:           "output_row"."scope_out" := CASE
jbe@86:             WHEN "area_id_p" ISNULL  AND "issue_id_p" ISNULL  THEN 'global'
jbe@86:             WHEN "area_id_p" NOTNULL AND "issue_id_p" ISNULL  THEN 'area'
jbe@86:             WHEN "area_id_p" ISNULL  AND "issue_id_p" NOTNULL THEN 'issue'
jbe@86:           END;
jbe@86:           "output_rows" := "output_rows" || "output_row";
jbe@86:           "output_row"."member_id" := "simulate_trustee_id_p";
jbe@86:         ELSIF "delegation_row"."trustee_id" NOTNULL THEN
jbe@86:           "output_row"."scope_out" := "delegation_row"."scope";
jbe@86:           "output_rows" := "output_rows" || "output_row";
jbe@86:           "output_row"."member_id" := "delegation_row"."trustee_id";
jbe@86:         ELSIF "delegation_row"."scope" NOTNULL THEN
jbe@86:           "output_row"."scope_out" := "delegation_row"."scope";
jbe@86:           "output_row"."disabled_out" := TRUE;
jbe@86:           "output_rows" := "output_rows" || "output_row";
jbe@86:           EXIT;
jbe@86:         ELSE
jbe@86:           "output_row"."scope_out" := NULL;
jbe@86:           "output_rows" := "output_rows" || "output_row";
jbe@86:           EXIT;
jbe@86:         END IF;
jbe@86:         EXIT WHEN "loop_member_id_v" NOTNULL;
jbe@86:         "output_row"."index" := "output_row"."index" + 1;
jbe@86:       END LOOP;
jbe@86:       "row_count" := array_upper("output_rows", 1);
jbe@86:       "i"      := 1;
jbe@86:       "loop_v" := FALSE;
jbe@86:       LOOP
jbe@86:         "output_row" := "output_rows"["i"];
jbe@86:         EXIT WHEN "output_row" ISNULL;
jbe@86:         IF "loop_v" THEN
jbe@86:           IF "i" + 1 = "row_count" THEN
jbe@86:             "output_row"."loop" := 'last';
jbe@86:           ELSIF "i" = "row_count" THEN
jbe@86:             "output_row"."loop" := 'repetition';
jbe@86:           ELSE
jbe@86:             "output_row"."loop" := 'intermediate';
jbe@86:           END IF;
jbe@86:         ELSIF "output_row"."member_id" = "loop_member_id_v" THEN
jbe@86:           "output_row"."loop" := 'first';
jbe@86:           "loop_v" := TRUE;
jbe@86:         END IF;
jbe@86:         IF "area_id_p" ISNULL AND "issue_id_p" ISNULL THEN
jbe@86:           "output_row"."participation" := NULL;
jbe@86:         END IF;
jbe@86:         RETURN NEXT "output_row";
jbe@86:         "i" := "i" + 1;
jbe@86:       END LOOP;
jbe@86:       RETURN;
jbe@86:     END;
jbe@86:   $$;
jbe@86: 
jbe@86: COMMENT ON FUNCTION "delegation_chain"
jbe@86:   ( "member"."id"%TYPE,
jbe@86:     "area"."id"%TYPE,
jbe@86:     "issue"."id"%TYPE,
jbe@86:     "member"."id"%TYPE )
jbe@86:   IS 'Helper function for frontends to display delegation chains; Not part of internal voting logic';
jbe@86: 
jbe@86: CREATE FUNCTION "delegation_chain"
jbe@86:   ( "member_id_p" "member"."id"%TYPE,
jbe@86:     "area_id_p"   "area"."id"%TYPE,
jbe@86:     "issue_id_p"  "issue"."id"%TYPE )
jbe@86:   RETURNS SETOF "delegation_chain_row"
jbe@86:   LANGUAGE 'plpgsql' STABLE AS $$
jbe@86:     DECLARE
jbe@86:       "result_row" "delegation_chain_row";
jbe@86:     BEGIN
jbe@86:       FOR "result_row" IN
jbe@86:         SELECT * FROM "delegation_chain"(
jbe@86:           "member_id_p", "area_id_p", "issue_id_p", NULL
jbe@86:         )
jbe@86:       LOOP
jbe@86:         RETURN NEXT "result_row";
jbe@86:       END LOOP;
jbe@86:       RETURN;
jbe@86:     END;
jbe@86:   $$;
jbe@86: 
jbe@86: COMMENT ON FUNCTION "delegation_chain"
jbe@86:   ( "member"."id"%TYPE,
jbe@86:     "area"."id"%TYPE,
jbe@86:     "issue"."id"%TYPE )
jbe@86:   IS 'Shortcut for "delegation_chain"(...) function where 4th parameter is null';
jbe@86: 
jbe@86: 
jbe@86: -- delete entries of "ignored_issue" table in "delete_member"(...) and "delete_private_data"() functions
jbe@86: 
jbe@94: CREATE OR REPLACE FUNCTION "clean_issue"("issue_id_p" "issue"."id"%TYPE)
jbe@94:   RETURNS VOID
jbe@94:   LANGUAGE 'plpgsql' VOLATILE AS $$
jbe@94:     DECLARE
jbe@94:       "issue_row" "issue"%ROWTYPE;
jbe@94:     BEGIN
jbe@94:       SELECT * INTO "issue_row"
jbe@94:         FROM "issue" WHERE "id" = "issue_id_p"
jbe@94:         FOR UPDATE;
jbe@94:       IF "issue_row"."cleaned" ISNULL THEN
jbe@94:         UPDATE "issue" SET
jbe@94:           "closed" = NULL,
jbe@94:           "ranks_available" = FALSE
jbe@94:           WHERE "id" = "issue_id_p";
jbe@94:         DELETE FROM "delegating_voter"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         DELETE FROM "direct_voter"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         DELETE FROM "delegating_interest_snapshot"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         DELETE FROM "direct_interest_snapshot"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         DELETE FROM "delegating_population_snapshot"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         DELETE FROM "direct_population_snapshot"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         DELETE FROM "ignored_issue"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         DELETE FROM "delegation"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         DELETE FROM "supporter"
jbe@94:           WHERE "issue_id" = "issue_id_p";
jbe@94:         UPDATE "issue" SET
jbe@94:           "closed"          = "issue_row"."closed",
jbe@94:           "ranks_available" = "issue_row"."ranks_available",
jbe@94:           "cleaned"         = now()
jbe@94:           WHERE "id" = "issue_id_p";
jbe@94:       END IF;
jbe@94:       RETURN;
jbe@94:     END;
jbe@94:   $$;
jbe@94: 
jbe@86: CREATE OR REPLACE FUNCTION "delete_member"("member_id_p" "member"."id"%TYPE)
jbe@86:   RETURNS VOID
jbe@86:   LANGUAGE 'plpgsql' VOLATILE AS $$
jbe@86:     BEGIN
jbe@86:       UPDATE "member" SET
jbe@86:         "last_login"                   = NULL,
jbe@86:         "login"                        = NULL,
jbe@86:         "password"                     = NULL,
jbe@86:         "active"                       = FALSE,
jbe@86:         "notify_email"                 = NULL,
jbe@86:         "notify_email_unconfirmed"     = NULL,
jbe@86:         "notify_email_secret"          = NULL,
jbe@86:         "notify_email_secret_expiry"   = NULL,
jbe@86:         "notify_email_lock_expiry"     = NULL,
jbe@86:         "password_reset_secret"        = NULL,
jbe@86:         "password_reset_secret_expiry" = NULL,
jbe@86:         "organizational_unit"          = NULL,
jbe@86:         "internal_posts"               = NULL,
jbe@86:         "realname"                     = NULL,
jbe@86:         "birthday"                     = NULL,
jbe@86:         "address"                      = NULL,
jbe@86:         "email"                        = NULL,
jbe@86:         "xmpp_address"                 = NULL,
jbe@86:         "website"                      = NULL,
jbe@86:         "phone"                        = NULL,
jbe@86:         "mobile_phone"                 = NULL,
jbe@86:         "profession"                   = NULL,
jbe@86:         "external_memberships"         = NULL,
jbe@86:         "external_posts"               = NULL,
jbe@86:         "statement"                    = NULL
jbe@86:         WHERE "id" = "member_id_p";
jbe@86:       -- "text_search_data" is updated by triggers
jbe@86:       DELETE FROM "setting"            WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "setting_map"        WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "member_relation_setting" WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "member_image"       WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "contact"            WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "area_setting"       WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "issue_setting"      WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "initiative_setting" WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "suggestion_setting" WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "membership"         WHERE "member_id" = "member_id_p";
jbe@94:       DELETE FROM "ignored_issue"      WHERE "member_id" = "member_id_p";
jbe@86:       DELETE FROM "delegation"         WHERE "truster_id" = "member_id_p";
jbe@86:       DELETE FROM "direct_voter" USING "issue"
jbe@86:         WHERE "direct_voter"."issue_id" = "issue"."id"
jbe@86:         AND "issue"."closed" ISNULL
jbe@86:         AND "member_id" = "member_id_p";
jbe@86:       RETURN;
jbe@86:     END;
jbe@86:   $$;
jbe@86: 
jbe@86: CREATE OR REPLACE FUNCTION "delete_private_data"()
jbe@86:   RETURNS VOID
jbe@86:   LANGUAGE 'plpgsql' VOLATILE AS $$
jbe@86:     BEGIN
jbe@86:       UPDATE "member" SET
jbe@86:         "last_login"                   = NULL,
jbe@86:         "login"                        = NULL,
jbe@86:         "password"                     = NULL,
jbe@86:         "notify_email"                 = NULL,
jbe@86:         "notify_email_unconfirmed"     = NULL,
jbe@86:         "notify_email_secret"          = NULL,
jbe@86:         "notify_email_secret_expiry"   = NULL,
jbe@86:         "notify_email_lock_expiry"     = NULL,
jbe@86:         "password_reset_secret"        = NULL,
jbe@86:         "password_reset_secret_expiry" = NULL,
jbe@86:         "organizational_unit"          = NULL,
jbe@86:         "internal_posts"               = NULL,
jbe@86:         "realname"                     = NULL,
jbe@86:         "birthday"                     = NULL,
jbe@86:         "address"                      = NULL,
jbe@86:         "email"                        = NULL,
jbe@86:         "xmpp_address"                 = NULL,
jbe@86:         "website"                      = NULL,
jbe@86:         "phone"                        = NULL,
jbe@86:         "mobile_phone"                 = NULL,
jbe@86:         "profession"                   = NULL,
jbe@86:         "external_memberships"         = NULL,
jbe@86:         "external_posts"               = NULL,
jbe@86:         "statement"                    = NULL;
jbe@86:       -- "text_search_data" is updated by triggers
jbe@86:       DELETE FROM "invite_code";
jbe@86:       DELETE FROM "setting";
jbe@86:       DELETE FROM "setting_map";
jbe@86:       DELETE FROM "member_relation_setting";
jbe@86:       DELETE FROM "member_image";
jbe@86:       DELETE FROM "contact";
jbe@86:       DELETE FROM "session";
jbe@86:       DELETE FROM "area_setting";
jbe@86:       DELETE FROM "issue_setting";
jbe@86:       DELETE FROM "initiative_setting";
jbe@86:       DELETE FROM "suggestion_setting";
jbe@94:       DELETE FROM "ignored_issue";
jbe@86:       DELETE FROM "direct_voter" USING "issue"
jbe@86:         WHERE "direct_voter"."issue_id" = "issue"."id"
jbe@86:         AND "issue"."closed" ISNULL;
jbe@86:       RETURN;
jbe@86:     END;
jbe@86:   $$;
jbe@86: 
jbe@86: 
poelzi@79: COMMIT;




