Re: There can be only one! How to avoid the "highlander-problem". - Mailing list pgsql-general

From Erwin Brandstetter
Subject Re: There can be only one! How to avoid the "highlander-problem".
Date
Msg-id 4660BD3A.7000603@falter.at
Whole thread Raw
In response to There can be only one! How to avoid the "highlander-problem".  (Erwin Brandstetter <brandstetter@falter.at>)
Responses Re: There can be only one! How to avoid the "highlander-problem".  (Erwin Brandstetter <brsaweda@gmail.com>)
List pgsql-general
RETURN was missing in the AFTER triggers. here is the corrected version:

----- begin of code

CREATE TABLE mankind
(
 man_id integer primary key,
 people_id integer NOT NULL,              -- references table people ..,
but that's irrelevant here ..
 king boolean NOT NULL DEFAULT false
);


---- Only one king per people ----
/* But no partial UNIQUE INDEX, because my solution needs temporary
"duplicates". Peoples will have to trust the triggers.
I _could_ implement it with a DEFERRED table constraint, IF partial
indices were supported with table constraints,
but they are not in pg 8.1.x or 8.2.x. Pseudo-Code example:
   ALTER TABLE mankind
    ADD CONSTRAINT mankind_people_uni_king_idx UNIQUE (people_id) WHERE
king[ = true]
    DEFERRABLE INITIALLY DEFERRED;
I create (a non-unique) index anyway, to speed up the triggers.
*/
CREATE INDEX mankind_king_idx   ON mankind (people_id)
 WHERE king;


---- trigger BEFORE UPDATE ---- To keep it simple we make world racist.
Men cannot migrate.

CREATE OR REPLACE FUNCTION trg_mankind_upbef()
 RETURNS "trigger" AS
$BODY$
BEGIN

IF NEW.people_id <> OLD.people_id THEN              -- NOT NULL allows "<>"
   RAISE EXCEPTION 'This is a racist world! Men cannot migrate.';
END IF;

IF NEW.man_id <> OLD.man_id THEN                    -- NOT NULL allows "<>"
   RAISE EXCEPTION 'A man has only one life and cannot change his
identity.';
END IF;

RETURN NEW;

END;
$BODY$
 LANGUAGE 'plpgsql' VOLATILE;

CREATE TRIGGER upbef
 BEFORE UPDATE
 ON mankind
 FOR EACH ROW
 EXECUTE PROCEDURE trg_mankind_upbef();


---- trigger AFTER UPDATE ----

CREATE OR REPLACE FUNCTION trg_mankind_upaft()
 RETURNS "trigger" AS
$BODY$
DECLARE
   kings int4;
BEGIN

IF NEW.king <> OLD.king THEN                          -- NOT NULL allows
"<>"
  kings := count(*)::int4 FROM mankind WHERE people_id = NEW.people_id
AND king;
  raise warning '%', kings;
   IF kings = 1 THEN
           --do nothing;

   ELSIF kings < 1 THEN
       RAISE EXCEPTION 'You must make another man king to get rid of the
old king!';

   ELSIF kings > 1 THEN
       UPDATE mankind
           SET king = FALSE
        WHERE people_id = NEW.people_id
           AND man_id <> NEW.man_id                    -- God save the
new king!
           AND king;
   END IF;
END IF;

RETURN NULL;

END;
$BODY$
 LANGUAGE 'plpgsql' VOLATILE;

CREATE TRIGGER upaft
 AFTER UPDATE
 ON mankind
 FOR EACH ROW
 EXECUTE PROCEDURE trg_mankind_upaft();


---- trigger BEFORE INSERT ----

CREATE OR REPLACE FUNCTION trg_mankind_insbef()
 RETURNS "trigger" AS
$BODY$
BEGIN

IF NOT EXISTS (SELECT 1 FROM mankind WHERE people_id = NEW.people_id) THEN
  NEW.king := true;                                    -- firstborn is
always king.
END IF;

RETURN NEW;

END;
$BODY$
 LANGUAGE 'plpgsql' VOLATILE;

CREATE TRIGGER insbef
 BEFORE INSERT
 ON mankind
 FOR EACH ROW
 EXECUTE PROCEDURE trg_mankind_insbef();


---- trigger AFTER INSERT ----

CREATE OR REPLACE FUNCTION trg_mankind_insaft()
 RETURNS "trigger" AS
$BODY$
DECLARE
   kings int4;
BEGIN

kings := count(*)::int4 FROM mankind WHERE people_id = NEW.people_id AND
king;

IF kings = 1 THEN
       --do nothing;

ELSIF kings > 1 THEN
   UPDATE mankind
       SET king = FALSE
    WHERE people_id = NEW.people_id
       AND man_id <> NEW.man_id                       -- God save the
new king!
       AND king;

ELSIF kings < 1 THEN                                   -- actually,
should never occur, because of trigger BEFORE INSERT
   UPDATE mankind
       SET king = TRUE
    WHERE man_id = NEW.man_id;                        -- the new man is
as good a king as any.

END IF;

RETURN NULL;

END;
$BODY$
 LANGUAGE 'plpgsql' VOLATILE;

CREATE TRIGGER insaft
 AFTER INSERT
 ON mankind
 FOR EACH ROW
 EXECUTE PROCEDURE trg_mankind_insaft();


---- trigger AFTER DELETE ---- (if old king dies)

CREATE OR REPLACE FUNCTION trg_mankind_delaft()
 RETURNS "trigger" AS
$BODY$
BEGIN

-- We trust the triggers and do not check if there was another king, as
there can be only one.
-- AND NOT EXISTS (SELECT 1 FROM mankind WHERE people_id = OLD.people_id
AND king)
IF OLD.king THEN
  UPDATE mankind SET king = true
   WHERE man_id = (SELECT man_id FROM mankind WHERE people_id =
OLD.people_id LIMIT 1);
END IF;

RETURN NULL;

END;
$BODY$
 LANGUAGE 'plpgsql' VOLATILE;

CREATE TRIGGER delaft
 AFTER DELETE
 ON mankind
 FOR EACH ROW
 EXECUTE PROCEDURE trg_mankind_delaft();

----- end of code

/Erwin

pgsql-general by date:

Previous
From: "Alexander Staubo"
Date:
Subject: Re: multimaster
Next
From: Jim Nasby
Date:
Subject: Re: [HACKERS] table partitioning pl/pgsql helpers