1 |
5549
|
aaronmk
|
/* To get the place trigger, replace taxonconcept with place and remove
|
2 |
|
|
references to matched_concept_id. */
|
3 |
1561
|
aaronmk
|
|
4 |
5549
|
aaronmk
|
----- Functions
|
5 |
1558
|
aaronmk
|
|
6 |
5549
|
aaronmk
|
CREATE OR REPLACE FUNCTION taxonconcept_update_ancestors(new taxonconcept, old_parent_id integer DEFAULT NULL::integer)
|
7 |
|
|
RETURNS void AS
|
8 |
1558
|
aaronmk
|
$BODY$
|
9 |
5549
|
aaronmk
|
DECLARE
|
10 |
|
|
-- Use matched_concept_id's ancestors instead if available
|
11 |
5554
|
aaronmk
|
parent_id_ taxonconcept.taxonconcept_id%TYPE := COALESCE(
|
12 |
|
|
NULLIF(new.matched_concept_id, new.taxonconcept_id), new.parent_id);
|
13 |
1558
|
aaronmk
|
BEGIN
|
14 |
5549
|
aaronmk
|
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 |
1558
|
aaronmk
|
END IF;
|
46 |
1559
|
aaronmk
|
|
47 |
5549
|
aaronmk
|
/* Note: We don't need an ON DELETE trigger to update the descendants'
|
48 |
5551
|
aaronmk
|
ancestors when a node is deleted, because the
|
49 |
|
|
taxonconcept_ancestor.ancestor_id foreign key is ON DELETE CASCADE. */
|
50 |
5549
|
aaronmk
|
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 |
1559
|
aaronmk
|
INSERT
|
63 |
5283
|
aaronmk
|
INTO taxonconcept_ancestor
|
64 |
|
|
(taxonconcept_id, ancestor_id)
|
65 |
|
|
VALUES (new.taxonconcept_id, new.taxonconcept_id)
|
66 |
1559
|
aaronmk
|
;
|
67 |
|
|
|
68 |
5549
|
aaronmk
|
PERFORM taxonconcept_update_ancestors(new);
|
69 |
1559
|
aaronmk
|
|
70 |
5549
|
aaronmk
|
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 |
5554
|
aaronmk
|
PERFORM taxonconcept_update_ancestors(new, COALESCE(
|
81 |
|
|
NULLIF(old.matched_concept_id, old.taxonconcept_id), old.parent_id));
|
82 |
1559
|
aaronmk
|
|
83 |
1558
|
aaronmk
|
RETURN new;
|
84 |
|
|
END;
|
85 |
|
|
$BODY$
|
86 |
|
|
LANGUAGE plpgsql VOLATILE
|
87 |
|
|
COST 100;
|
88 |
|
|
|
89 |
|
|
----- Ancestors table
|
90 |
|
|
|
91 |
5283
|
aaronmk
|
CREATE TABLE taxonconcept_ancestor
|
92 |
1558
|
aaronmk
|
(
|
93 |
5283
|
aaronmk
|
taxonconcept_id integer NOT NULL,
|
94 |
1558
|
aaronmk
|
ancestor_id integer NOT NULL,
|
95 |
5283
|
aaronmk
|
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 |
1558
|
aaronmk
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
99 |
5283
|
aaronmk
|
CONSTRAINT taxonconcept_ancestor_taxonconcept_id_fkey FOREIGN KEY (taxonconcept_id)
|
100 |
|
|
REFERENCES taxonconcept (taxonconcept_id) MATCH SIMPLE
|
101 |
1558
|
aaronmk
|
ON UPDATE CASCADE ON DELETE CASCADE
|
102 |
|
|
)
|
103 |
|
|
WITH (
|
104 |
|
|
OIDS=FALSE
|
105 |
|
|
);
|
106 |
5549
|
aaronmk
|
COMMENT ON TABLE taxonconcept_ancestor
|
107 |
|
|
IS 'Stores the accepted ancestors of a taxonconcept. Auto-populated, so should not be manually modified.';
|
108 |
1558
|
aaronmk
|
|
109 |
5549
|
aaronmk
|
CREATE INDEX taxonconcept_ancestor_descendants
|
110 |
|
|
ON taxonconcept_ancestor
|
111 |
|
|
USING btree
|
112 |
|
|
(ancestor_id , taxonconcept_id );
|
113 |
1558
|
aaronmk
|
|
114 |
5549
|
aaronmk
|
----- Triggers
|
115 |
|
|
|
116 |
|
|
CREATE TRIGGER taxonconcept_update_ancestors_on_insert
|
117 |
|
|
AFTER INSERT
|
118 |
5283
|
aaronmk
|
ON taxonconcept
|
119 |
1558
|
aaronmk
|
FOR EACH ROW
|
120 |
5549
|
aaronmk
|
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();
|