Last active
August 29, 2015 14:18
-
-
Save wizpig64/9f14fbad3de98815fc4c to your computer and use it in GitHub Desktop.
PostgreSQL View and Triggers for One-to-One Tables
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- This is an implementation of a one-to-one relationship between two tables in PostgreSQL, as well as a view that | |
-- presents the two as a single table, accepting INSERTs, UPDATEs and DELETEs just like a flat table would. | |
-- One thing that may or may not be noteworthy is how foo.id's sequencer is manually incremented even when the user | |
-- specifies a non-null value. I needed this for my project (MS-Access was throwing a fit), but it may not be the | |
-- default behavior for serial primary keys. | |
-- todo for UPDATE and DELETE: it's possible that a foo exists without a bar relating to it. currently, UPDATE and | |
-- DELETE could be used to mess with rows on foo that shouldn't be messed with (for example they could be rows with | |
-- different one-to-one relationships). This won't matter for most applications, but it allows for some potentially | |
-- malicious actions that should be caught. | |
-- drop view foobar; | |
-- drop table bar; | |
-- drop table foo; | |
CREATE TABLE foo ( | |
id serial PRIMARY KEY, | |
a integer, | |
b integer | |
); | |
CREATE TABLE bar ( | |
foo_id integer PRIMARY KEY REFERENCES foo ON DELETE CASCADE ON UPDATE CASCADE, | |
c integer, | |
d integer | |
); | |
CREATE VIEW foobar AS | |
SELECT | |
foo.id, | |
foo.a, | |
foo.b, | |
bar.c, | |
bar.d | |
FROM foo, bar | |
WHERE foo.id = bar.foo_id | |
ORDER BY foo.id | |
; | |
CREATE OR REPLACE FUNCTION foobar_update() RETURNS TRIGGER AS $$ | |
DECLARE | |
new_id integer; | |
BEGIN | |
-- Perform the required operation on foo and bar | |
-- to reflect the change made to foobar. | |
IF (TG_OP = 'DELETE') THEN | |
DELETE FROM foo WHERE id = OLD.id; | |
-- no need to delete from bar, that should be cascaded. | |
IF NOT FOUND THEN RETURN NULL; END IF; | |
RETURN OLD; | |
ELSIF (TG_OP = 'UPDATE') THEN | |
UPDATE foo SET id = NEW.id, | |
a = NEW.a, | |
b = NEW.b WHERE id = OLD.id; | |
IF NOT FOUND THEN RETURN NULL; END IF; | |
UPDATE bar SET c = NEW.c, | |
d = NEW.d WHERE foo_id = NEW.id; | |
-- use NEW.id here because changes should be cascaded already. | |
IF NOT FOUND THEN RETURN NULL; END IF; | |
RETURN NEW; | |
ELSIF (TG_OP = 'INSERT') THEN | |
-- pk should be what is given by the user, or whatever's next in the sequence. | |
-- evaluating nextval() into a variable makes sure it's incremented always. | |
new_id := nextval('foo_id_seq'); | |
NEW.id := COALESCE(NEW.id, new_id); | |
INSERT INTO foo (id, | |
a, | |
b) | |
VALUES (NEW.id, | |
NEW.a, | |
NEW.b); | |
INSERT INTO bar (foo_id, | |
c, | |
d) | |
VALUES (NEW.id, | |
NEW.c, | |
NEW.d); | |
RETURN NEW; | |
END IF; | |
END; | |
$$ LANGUAGE plpgsql; | |
CREATE TRIGGER foobar_update_trigger | |
INSTEAD OF INSERT OR UPDATE OR DELETE ON foobar | |
FOR EACH ROW EXECUTE PROCEDURE foobar_update(); | |
-- now for some testing | |
INSERT INTO foobar (a, b, c, d) VALUES (1, 2, 3, 4); | |
INSERT INTO foobar (a, b, c, d) VALUES (2, 3, 4, 5); | |
INSERT INTO foobar (a, b, c, d) VALUES (3, 4, 5, 6); | |
INSERT INTO foobar (a, b, c, d) VALUES (4, 5, 6, 7); | |
INSERT INTO foobar (a, b, c, d) VALUES (5, 6, 7, 8); | |
INSERT INTO foobar (a, b, c, d) VALUES (6, 7, 8, 9); | |
INSERT INTO foobar (a, b, c, d) VALUES (1, default, default, default); | |
INSERT INTO foobar (a, b, c, d) VALUES (default, 1, default, default); | |
INSERT INTO foobar (a, b, c, d) VALUES (default, default, 1, default); | |
INSERT INTO foobar (a, b, c, d) VALUES (default, default, default, 1); | |
UPDATE foobar | |
SET b = 20 | |
WHERE id = 3; | |
UPDATE foobar | |
SET c = 25 | |
WHERE id = 4; | |
DELETE FROM foobar WHERE id = 5; | |
UPDATE foobar | |
set id = 5 | |
WHERE id = 1; | |
SELECT * FROM foobar; | |
-- should output: | |
-- id a b c d | |
-- 2 2 3 4 5 | |
-- 3 3 20 5 6 | |
-- 4 4 5 25 7 | |
-- 5 1 2 3 4 | |
-- 6 6 7 8 9 | |
-- 7 1 | |
-- 8 1 | |
-- 9 1 | |
-- 10 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment