Skip to content

Commit fdf775f

Browse files
committed
Revive entity depth sort
1 parent 567107e commit fdf775f

File tree

5 files changed

+244
-0
lines changed

5 files changed

+244
-0
lines changed
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
-- We can track the maximum dependency depth of any sub-dag rooted at each entity.
2+
-- The depth of any entity is simply the maximum depth of any of its children plus one.
3+
-- This allows us to trivially sort entities into a valid dependency order without needing a complex topological
4+
-- sort at query time.
5+
6+
-- Unfortunately we can't use triggers for most of these since for some entities their depth is dependent on
7+
-- references which, due to foreign keys, must be inserted AFTER the entity itself, it must be run after all
8+
-- the entity's local references are inserted, but there's no way for us to trigger
9+
-- only when the LAST one of those is done, so we'd need to run this on every
10+
-- local reference insert, and remove the optimistic exit in the case where the row
11+
-- already exists, which is a big waste.
12+
--
13+
-- Instead we just run these functions manually after an entity's references are all inserted.
14+
15+
CREATE TABLE causal_depth (
16+
causal_id INTEGER PRIMARY KEY REFERENCES causals (id) ON DELETE CASCADE,
17+
depth INTEGER NOT NULL
18+
);
19+
20+
CREATE TABLE component_depth (
21+
component_hash_id INTEGER PRIMARY KEY REFERENCES component_hashes (id) ON DELETE CASCADE,
22+
depth INTEGER NOT NULL
23+
);
24+
25+
CREATE TABLE namespace_depth (
26+
namespace_hash_id INTEGER PRIMARY KEY REFERENCES branch_hashes (id) ON DELETE CASCADE,
27+
depth INTEGER NOT NULL
28+
);
29+
30+
CREATE TABLE patch_depth (
31+
patch_id INTEGER PRIMARY KEY REFERENCES patches (id) ON DELETE CASCADE,
32+
depth INTEGER NOT NULL
33+
);
34+
35+
36+
-- Triggers
37+
38+
CREATE OR REPLACE FUNCTION update_causal_depth(the_causal_id integer) RETURNS VOID AS $$
39+
DECLARE
40+
max_namespace_depth INTEGER;
41+
max_child_causal_depth INTEGER;
42+
the_namespace_hash_id INTEGER;
43+
BEGIN
44+
-- If there's already a depth entry for this causal, we're done.
45+
IF EXISTS (SELECT FROM causal_depth cd WHERE cd.causal_id = the_causal_id) THEN
46+
RETURN;
47+
END IF;
48+
49+
SELECT c.namespace_hash_id INTO the_namespace_hash_id
50+
FROM causals c
51+
WHERE c.id = the_causal_id;
52+
-- Find the max depth of the associated namespace
53+
-- Find the max depth of any child causal
54+
-- Set the depth of this causal to the max of those two plus one
55+
SELECT COALESCE(MAX(nd.depth), -1) INTO max_namespace_depth
56+
FROM namespace_depth nd
57+
WHERE nd.namespace_hash_id = the_namespace_hash_id;
58+
SELECT COALESCE(MAX(cd.depth), -1) INTO max_child_causal_depth
59+
FROM causal_depth cd
60+
JOIN causal_ancestors ca ON cd.causal_id = ca.ancestor_id
61+
WHERE ca.causal_id = the_causal_id;
62+
INSERT INTO causal_depth (causal_id, depth)
63+
VALUES (the_causal_id, GREATEST(max_namespace_depth, max_child_causal_depth) + 1);
64+
65+
RETURN;
66+
END;
67+
$$ LANGUAGE plpgsql;
68+
69+
CREATE OR REPLACE FUNCTION update_component_depth(the_component_hash_id integer) RETURNS VOID AS $$
70+
DECLARE
71+
max_referenced_component_depth INTEGER;
72+
BEGIN
73+
RAISE NOTICE 'Updating component depth for %', the_component_hash_id;
74+
-- If there's already a depth entry for this component, we're done.
75+
IF EXISTS (SELECT FROM component_depth cd WHERE cd.component_hash_id = the_component_hash_id) THEN
76+
RETURN;
77+
END IF;
78+
-- Find the max depth of any component referenced by this component
79+
-- Set the depth of this component to that plus one
80+
SELECT COALESCE(MAX(refs.depth), -1) INTO max_referenced_component_depth
81+
FROM (
82+
( SELECT cd.depth AS depth
83+
FROM terms t
84+
JOIN term_local_component_references cr
85+
ON cr.term_id = t.id
86+
JOIN component_depth cd
87+
ON cd.component_hash_id = cr.component_hash_id
88+
WHERE t.component_hash_id = the_component_hash_id
89+
) UNION
90+
( SELECT cd.depth AS depth
91+
FROM types t
92+
JOIN type_local_component_references cr
93+
ON cr.type_id = t.id
94+
JOIN component_depth cd
95+
ON cd.component_hash_id = cr.component_hash_id
96+
WHERE t.component_hash_id = the_component_hash_id
97+
)
98+
) AS refs;
99+
INSERT INTO component_depth (component_hash_id, depth)
100+
VALUES (the_component_hash_id, max_referenced_component_depth + 1);
101+
RETURN;
102+
END;
103+
$$ LANGUAGE plpgsql;
104+
105+
CREATE OR REPLACE FUNCTION update_namespace_depth(the_namespace_hash_id integer) RETURNS VOID AS $$
106+
DECLARE
107+
max_child_causal_depth INTEGER;
108+
max_patch_depth INTEGER;
109+
max_referenced_component_depth INTEGER;
110+
BEGIN
111+
-- If there's already a depth entry for this namespace, we're done.
112+
IF EXISTS (SELECT FROM namespace_depth nd WHERE nd.namespace_hash_id = the_namespace_hash_id) THEN
113+
RETURN;
114+
END IF;
115+
-- Find the max depth of any child causal
116+
-- Find the max depth of any patch
117+
-- Find the max depth of any component referenced by a term, type, or term metadata in this namespace
118+
-- Set the depth of this namespace to the max of those plus one
119+
SELECT COALESCE(MAX(cd.depth), -1) INTO max_child_causal_depth
120+
FROM causal_depth cd
121+
JOIN namespace_children nc ON cd.causal_id = nc.child_causal_id
122+
WHERE nc.parent_namespace_hash_id = the_namespace_hash_id;
123+
SELECT COALESCE(MAX(pd.depth), -1) INTO max_patch_depth
124+
FROM patch_depth pd
125+
JOIN namespace_patches np ON pd.patch_id = np.patch_id
126+
WHERE np.namespace_hash_id = the_namespace_hash_id;
127+
SELECT COALESCE(MAX(depth), -1) INTO max_referenced_component_depth
128+
FROM (
129+
-- direct term references
130+
( SELECT cd.depth AS depth
131+
FROM component_depth cd
132+
JOIN term_local_component_references cr
133+
ON cd.component_hash_id = cr.component_hash_id
134+
JOIN terms t
135+
ON cr.term_id = t.id
136+
JOIN namespace_terms nt
137+
ON t.id = nt.term_id
138+
WHERE nt.namespace_hash_id = the_namespace_hash_id
139+
) UNION
140+
-- term metadata references
141+
( SELECT cd.depth AS depth
142+
FROM component_depth cd
143+
JOIN terms t
144+
ON cd.component_hash_id = t.component_hash_id
145+
JOIN namespace_term_metadata ntm
146+
ON ntm.metadata_term_id = t.id
147+
JOIN namespace_terms nt
148+
ON ntm.named_term = nt.id
149+
WHERE nt.namespace_hash_id = the_namespace_hash_id
150+
) UNION
151+
-- direct constructor references
152+
( SELECT cd.depth AS depth
153+
FROM component_depth cd
154+
JOIN constructors c
155+
ON cd.component_hash_id = c.constructor_type_component_hash_id
156+
JOIN namespace_terms nt
157+
ON c.id = nt.constructor_id
158+
WHERE nt.namespace_hash_id = the_namespace_hash_id
159+
) UNION
160+
-- direct type references
161+
( SELECT cd.depth AS depth
162+
FROM component_depth cd
163+
JOIN type_local_component_references cr
164+
ON cd.component_hash_id = cr.component_hash_id
165+
JOIN types t
166+
ON cr.type_id = t.id
167+
JOIN namespace_types nt
168+
ON t.id = nt.type_id
169+
WHERE nt.namespace_hash_id = the_namespace_hash_id
170+
) UNION
171+
-- type metadata references
172+
( SELECT cd.depth AS depth
173+
FROM component_depth cd
174+
JOIN terms t
175+
ON cd.component_hash_id = t.component_hash_id
176+
JOIN namespace_type_metadata ntm
177+
ON ntm.metadata_term_id = t.id
178+
JOIN namespace_types nt
179+
ON ntm.named_type = nt.id
180+
WHERE nt.namespace_hash_id = the_namespace_hash_id
181+
)
182+
) AS refs;
183+
INSERT INTO namespace_depth (namespace_hash_id, depth)
184+
VALUES (the_namespace_hash_id, GREATEST(max_child_causal_depth, max_patch_depth, max_referenced_component_depth) + 1);
185+
186+
RETURN;
187+
END;
188+
$$ LANGUAGE plpgsql;
189+
190+
CREATE OR REPLACE FUNCTION update_patch_depth(the_patch_id integer) RETURNS VOID AS $$
191+
DECLARE
192+
max_referenced_component_depth INTEGER;
193+
BEGIN
194+
-- If there's already a depth entry for this patch, we're done.
195+
IF EXISTS (SELECT FROM patch_depth pd WHERE pd.patch_id = the_patch_id) THEN
196+
RETURN;
197+
END IF;
198+
-- Find the max depth of any term component referenced by a patch
199+
-- Find the max depth of any type component referenced by a patch
200+
-- Set the depth of this patch to that plus one
201+
202+
SELECT COALESCE(MAX(cd.depth), -1) INTO max_referenced_component_depth
203+
FROM (
204+
-- term references
205+
( SELECT from_term_component_hash_id AS component_hash_id
206+
FROM patch_term_mappings
207+
WHERE patch_id = the_patch_id
208+
) UNION
209+
-- constructor mappings
210+
( SELECT from_constructor_component_hash_id AS component_hash_id
211+
FROM patch_constructor_mappings
212+
WHERE patch_id = the_patch_id
213+
) UNION
214+
-- type references
215+
( SELECT from_type_component_hash_id AS component_hash_id
216+
FROM patch_type_mappings
217+
WHERE patch_id = the_patch_id
218+
)
219+
) AS refs JOIN component_depth cd
220+
ON cd.component_hash_id = refs.component_hash_id;
221+
INSERT INTO patch_depth (patch_id, depth)
222+
VALUES (the_patch_id, max_referenced_component_depth + 1);
223+
224+
RETURN;
225+
END;
226+
$$ LANGUAGE plpgsql;

src/Share/Postgres/Causal/Queries.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ savePgNamespace maySerialized mayBh b@(BranchFull.Branch {terms, types, patches,
642642
|]
643643
-- Note: this must be run AFTER inserting the namespace and all its children.
644644
execute_ [sql| SELECT save_namespace(#{bhId}) |]
645+
execute_ [sql| SELECT update_namespace_depth(#{bhId}) |]
645646

646647
saveSerializedNamespace :: (QueryM m) => BranchHashId -> CBORBytes TempEntity -> m ()
647648
saveSerializedNamespace bhId (CBORBytes bytes) = do
@@ -785,6 +786,7 @@ saveCausal maySerializedCausal mayCh bhId ancestorIds = do
785786
SELECT #{cId}, a.ancestor_id
786787
FROM ancestors a
787788
|]
789+
execute_ [sql| SELECT update_causal_depth(#{cId}) |]
788790
pure cId
789791

790792
saveSerializedCausal :: (QueryM m) => CausalId -> CBORBytes TempEntity -> m ()

src/Share/Postgres/Definitions/Queries.hs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,10 @@ saveEncodedTermComponent componentHash maySerialized elements = do
863863
SELECT defn_mappings.term_id, defn_mappings.local_index, defn_mappings.component_hash_id
864864
FROM defn_mappings
865865
|]
866+
execute_
867+
[sql|
868+
SELECT update_component_depth(#{componentHashId})
869+
|]
866870
pure termIds
867871

868872
saveTypeComponent :: ComponentHash -> Maybe TempEntity -> [(PgLocalIds, DeclFormat.Decl Symbol)] -> CodebaseM e ()
@@ -1013,6 +1017,10 @@ saveTypeComponent componentHash maySerialized elements = do
10131017
FROM defn_mappings
10141018
|]
10151019
saveConstructors (zip (toList typeIds) elements)
1020+
execute_
1021+
[sql|
1022+
SELECT update_component_depth(#{componentHashId})
1023+
|]
10161024
pure typeIds
10171025

10181026
-- | Efficiently resolve all pg Ids across selected Local Ids.

src/Share/Postgres/Patches/Queries.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ savePatch maySerialized patchHash PatchFull.Patch {termEdits, typeEdits} = do
233233
LEFT JOIN types to_type
234234
ON to_type.component_hash_id = to_type_component_hash_id AND to_type.component_index = to_type_component_index
235235
|]
236+
execute_ [sql| SELECT update_patch_depth(#{patchId}) |]
236237
pure patchId
237238
termsTable :: [(Maybe ComponentHashId, Maybe Int64 {- from comp index -}, Maybe TextId, Maybe ComponentHashId, Maybe Int64 {- to comp index -}, Maybe TextId, Maybe PatchFullTermEdit.Typing, Bool)]
238239
constructorsTable :: [(ComponentHashId, Int64 {- from comp index -}, Int64 {- from constr index -}, Maybe ComponentHashId, Maybe Int64 {- to comp index-}, Maybe Int64 {- to constr index -}, Maybe PatchFullTermEdit.Typing, Bool)]

transcripts/sql/inserts.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,19 @@ INSERT INTO namespaces(namespace_hash_id, contained_terms, deep_contained_terms,
107107
VALUES (0, 0, 0, 0, 0, 0, 0)
108108
ON CONFLICT DO NOTHING;
109109

110+
INSERT INTO namespace_depth(namespace_hash_id, depth)
111+
VALUES (0, 0)
112+
ON CONFLICT DO NOTHING;
110113

111114
-- Initialize the empty causal
112115
INSERT INTO causals(id, hash, namespace_hash_id)
113116
VALUES (0, 'sg60bvjo91fsoo7pkh9gejbn0qgc95vra87ap6l5d35ri0lkaudl7bs12d71sf3fh6p23teemuor7mk1i9n567m50ibakcghjec5ajg', 0)
114117
ON CONFLICT DO NOTHING;
115118

119+
INSERT INTO causal_depth(causal_id, depth)
120+
VALUES (0, 0)
121+
ON CONFLICT DO NOTHING;
122+
116123
-- Projects
117124
INSERT INTO projects (
118125
id,

0 commit comments

Comments
 (0)