1
|
/* To get the place trigger, replace taxonconcept with place and remove
|
2
|
references to matched_concept_id. */
|
3
|
|
4
|
----- Functions
|
5
|
|
6
|
CREATE OR REPLACE FUNCTION taxonconcept_update_ancestors(new taxonconcept, old_parent_id integer DEFAULT NULL::integer)
|
7
|
RETURNS void AS
|
8
|
$BODY$
|
9
|
DECLARE
|
10
|
-- Use matched_concept_id's ancestors instead if available
|
11
|
parent_id_ taxonconcept.taxonconcept_id%TYPE := COALESCE(
|
12
|
NULLIF(new.matched_concept_id, new.taxonconcept_id), new.parent_id);
|
13
|
BEGIN
|
14
|
IF parent_id_ IS DISTINCT FROM old_parent_id THEN
|
15
|
DECLARE
|
16
|
-- These include the parent itself
|
17
|
old_ancestors integer[] := (
|
18
|
SELECT array_agg(ancestor_id) FROM taxonconcept_ancestor
|
19
|
WHERE taxonconcept_id = old_parent_id
|
20
|
);
|
21
|
new_ancestors integer[] := (
|
22
|
SELECT array_agg(ancestor_id) FROM taxonconcept_ancestor
|
23
|
WHERE taxonconcept_id = parent_id_
|
24
|
);
|
25
|
descendant_id integer;
|
26
|
BEGIN
|
27
|
FOR descendant_id IN -- also includes self
|
28
|
SELECT taxonconcept_id
|
29
|
FROM taxonconcept_ancestor
|
30
|
WHERE ancestor_id = new.taxonconcept_id
|
31
|
LOOP
|
32
|
-- Delete old parent's ancestors
|
33
|
DELETE FROM taxonconcept_ancestor
|
34
|
WHERE taxonconcept_id = descendant_id
|
35
|
AND ancestor_id = ANY (old_ancestors)
|
36
|
;
|
37
|
|
38
|
-- Add new parent's ancestors
|
39
|
INSERT INTO taxonconcept_ancestor
|
40
|
(taxonconcept_id, ancestor_id)
|
41
|
SELECT descendant_id, unnest(new_ancestors)
|
42
|
;
|
43
|
END LOOP;
|
44
|
END;
|
45
|
END IF;
|
46
|
|
47
|
/* Note: We don't need an ON DELETE trigger to update the descendants'
|
48
|
ancestors when a node is deleted, because the
|
49
|
taxonconcept_ancestor.ancestor_id foreign key is ON DELETE CASCADE. */
|
50
|
END;
|
51
|
$BODY$
|
52
|
LANGUAGE plpgsql VOLATILE
|
53
|
COST 100;
|
54
|
|
55
|
CREATE OR REPLACE FUNCTION taxonconcept_update_ancestors_on_insert()
|
56
|
RETURNS trigger AS
|
57
|
$BODY$
|
58
|
BEGIN
|
59
|
/* Add "ancestor" for this node. This is useful for queries, because you
|
60
|
don't have to separately test if the leaf node is the one you're looking
|
61
|
for, in addition to that leaf node's ancestors. */
|
62
|
INSERT
|
63
|
INTO taxonconcept_ancestor
|
64
|
(taxonconcept_id, ancestor_id)
|
65
|
VALUES (new.taxonconcept_id, new.taxonconcept_id)
|
66
|
;
|
67
|
|
68
|
PERFORM taxonconcept_update_ancestors(new);
|
69
|
|
70
|
RETURN new;
|
71
|
END;
|
72
|
$BODY$
|
73
|
LANGUAGE plpgsql VOLATILE
|
74
|
COST 100;
|
75
|
|
76
|
CREATE OR REPLACE FUNCTION taxonconcept_update_ancestors_on_update()
|
77
|
RETURNS trigger AS
|
78
|
$BODY$
|
79
|
BEGIN
|
80
|
PERFORM taxonconcept_update_ancestors(new, COALESCE(
|
81
|
NULLIF(old.matched_concept_id, old.taxonconcept_id), old.parent_id));
|
82
|
|
83
|
RETURN new;
|
84
|
END;
|
85
|
$BODY$
|
86
|
LANGUAGE plpgsql VOLATILE
|
87
|
COST 100;
|
88
|
|
89
|
----- Ancestors table
|
90
|
|
91
|
CREATE TABLE taxonconcept_ancestor
|
92
|
(
|
93
|
taxonconcept_id integer NOT NULL,
|
94
|
ancestor_id integer NOT NULL,
|
95
|
CONSTRAINT taxonconcept_ancestor_pkey PRIMARY KEY (taxonconcept_id , ancestor_id ),
|
96
|
CONSTRAINT taxonconcept_ancestor_ancestor_id_fkey FOREIGN KEY (ancestor_id)
|
97
|
REFERENCES taxonconcept (taxonconcept_id) MATCH SIMPLE
|
98
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
99
|
CONSTRAINT taxonconcept_ancestor_taxonconcept_id_fkey FOREIGN KEY (taxonconcept_id)
|
100
|
REFERENCES taxonconcept (taxonconcept_id) MATCH SIMPLE
|
101
|
ON UPDATE CASCADE ON DELETE CASCADE
|
102
|
)
|
103
|
WITH (
|
104
|
OIDS=FALSE
|
105
|
);
|
106
|
COMMENT ON TABLE taxonconcept_ancestor
|
107
|
IS 'Stores the accepted ancestors of a taxonconcept. Auto-populated, so should not be manually modified.';
|
108
|
|
109
|
CREATE INDEX taxonconcept_ancestor_descendants
|
110
|
ON taxonconcept_ancestor
|
111
|
USING btree
|
112
|
(ancestor_id , taxonconcept_id );
|
113
|
|
114
|
----- Triggers
|
115
|
|
116
|
CREATE TRIGGER taxonconcept_update_ancestors_on_insert
|
117
|
AFTER INSERT
|
118
|
ON taxonconcept
|
119
|
FOR EACH ROW
|
120
|
EXECUTE PROCEDURE taxonconcept_update_ancestors_on_insert();
|
121
|
|
122
|
CREATE TRIGGER taxonconcept_update_ancestors_on_update
|
123
|
AFTER UPDATE
|
124
|
ON taxonconcept
|
125
|
FOR EACH ROW
|
126
|
EXECUTE PROCEDURE taxonconcept_update_ancestors_on_update();
|