Skip to content

Commit f7dcb00

Browse files
committed
Alter syncv2 to expand causals but not history, and skip child causals via except_causal
1 parent f0183c7 commit f7dcb00

2 files changed

Lines changed: 363 additions & 0 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
-- Takes a causal_id and returns a table of ALL hashes which are dependencies of that causal, EXCEPT for the
2+
-- ancestors of the causal.
3+
CREATE OR REPLACE FUNCTION dependencies_of_causals_without_ancestors(the_causal_ids INTEGER[]) RETURNS TABLE (hash TEXT) AS $$
4+
WITH RECURSIVE all_causals(causal_id, causal_hash, causal_namespace_hash_id) AS (
5+
-- Base causal
6+
SELECT DISTINCT causal.id, causal.hash, causal.namespace_hash_id
7+
FROM UNNEST(the_causal_ids) AS causal_id
8+
JOIN causals causal ON causal.id = causal_id
9+
UNION
10+
-- This nested CTE is required because RECURSIVE CTEs can't refer
11+
-- to the recursive table more than once.
12+
-- I don't fully understand why or how this works, but it does
13+
( WITH rec AS (
14+
SELECT tc.causal_id, tc.causal_namespace_hash_id
15+
FROM all_causals tc
16+
)
17+
SELECT child_causal.id, child_causal.hash, child_causal.namespace_hash_id
18+
FROM rec tc
19+
JOIN namespace_children nc ON tc.causal_namespace_hash_id = nc.parent_namespace_hash_id
20+
JOIN causals child_causal ON nc.child_causal_id = child_causal.id
21+
)
22+
), all_namespaces(namespace_hash_id, namespace_hash) AS (
23+
SELECT DISTINCT tc.causal_namespace_hash_id AS namespace_hash_id, bh.base32 as namespace_hash
24+
FROM all_causals tc
25+
JOIN branch_hashes bh ON tc.causal_namespace_hash_id = bh.id
26+
), all_patches(patch_id, patch_hash) AS (
27+
SELECT DISTINCT patch.id, patch.hash
28+
FROM all_namespaces an
29+
JOIN namespace_patches np ON an.namespace_hash_id = np.namespace_hash_id
30+
JOIN patches patch ON np.patch_id = patch.id
31+
),
32+
-- term components to start transitively joining dependencies to
33+
base_term_components(component_hash_id) AS (
34+
SELECT DISTINCT term.component_hash_id
35+
FROM all_namespaces an
36+
JOIN namespace_terms nt ON an.namespace_hash_id = nt.namespace_hash_id
37+
JOIN terms term ON nt.term_id = term.id
38+
UNION
39+
SELECT DISTINCT term.component_hash_id
40+
FROM all_patches ap
41+
JOIN patch_term_mappings ptm ON ap.patch_id = ptm.patch_id
42+
JOIN terms term ON ptm.to_term_id = term.id
43+
UNION
44+
-- term metadata
45+
SELECT DISTINCT term.component_hash_id
46+
FROM all_namespaces an
47+
JOIN namespace_terms nt ON an.namespace_hash_id = nt.namespace_hash_id
48+
JOIN namespace_term_metadata meta ON nt.id = meta.named_term
49+
JOIN terms term ON meta.metadata_term_id = term.id
50+
UNION
51+
-- type metadata
52+
SELECT DISTINCT term.component_hash_id
53+
FROM all_namespaces an
54+
JOIN namespace_types nt ON an.namespace_hash_id = nt.namespace_hash_id
55+
JOIN namespace_type_metadata meta ON nt.id = meta.named_type
56+
JOIN terms term ON meta.metadata_term_id = term.id
57+
),
58+
-- type components to start transitively joining dependencies to
59+
base_type_components(component_hash_id) AS (
60+
SELECT DISTINCT typ.component_hash_id
61+
FROM all_namespaces an
62+
JOIN namespace_types nt ON an.namespace_hash_id = nt.namespace_hash_id
63+
JOIN types typ ON nt.type_id = typ.id
64+
UNION
65+
SELECT DISTINCT typ.component_hash_id
66+
FROM all_namespaces an
67+
JOIN namespace_terms nt ON an.namespace_hash_id = nt.namespace_hash_id
68+
JOIN constructors con ON nt.constructor_id = con.id
69+
JOIN types typ ON con.type_id = typ.id
70+
UNION
71+
SELECT DISTINCT typ.component_hash_id
72+
FROM all_patches ap
73+
JOIN patch_type_mappings ptm ON ap.patch_id = ptm.patch_id
74+
JOIN types typ ON ptm.to_type_id = typ.id
75+
UNION
76+
SELECT DISTINCT typ.component_hash_id
77+
FROM all_patches ap
78+
JOIN patch_constructor_mappings pcm ON ap.patch_id = pcm.patch_id
79+
JOIN constructors con ON pcm.to_constructor_id = con.id
80+
JOIN types typ ON con.type_id = typ.id
81+
),
82+
-- All the dependencies we join in transitively from the known term & type components we depend on.
83+
all_components(component_hash_id) AS (
84+
SELECT DISTINCT btc.component_hash_id
85+
FROM base_term_components btc
86+
UNION
87+
SELECT DISTINCT btc.component_hash_id
88+
FROM base_type_components btc
89+
UNION
90+
( WITH rec AS (
91+
SELECT DISTINCT ac.component_hash_id
92+
FROM all_components ac
93+
)
94+
-- recursively union in term dependencies
95+
SELECT DISTINCT ref.component_hash_id
96+
FROM rec atc
97+
-- This joins in ALL the terms from the component, not just the one that caused the dependency on the
98+
-- component
99+
JOIN terms term ON atc.component_hash_id = term.component_hash_id
100+
JOIN term_local_component_references ref ON term.id = ref.term_id
101+
UNION
102+
-- recursively union in type dependencies
103+
SELECT DISTINCT ref.component_hash_id
104+
FROM rec atc
105+
-- This joins in ALL the types from the component, not just the one that caused the dependency on the
106+
-- component
107+
JOIN types typ ON atc.component_hash_id = typ.component_hash_id
108+
JOIN type_local_component_references ref ON typ.id = ref.type_id
109+
)
110+
)
111+
(SELECT ch.base32 AS hash
112+
FROM all_components ac
113+
JOIN component_hashes ch ON ac.component_hash_id = ch.id
114+
)
115+
UNION ALL
116+
(SELECT ap.patch_hash AS hash
117+
FROM all_patches ap
118+
)
119+
UNION ALL
120+
(SELECT an.namespace_hash AS hash
121+
FROM all_namespaces an
122+
)
123+
UNION ALL
124+
(SELECT ac.causal_hash AS hash
125+
FROM all_causals ac
126+
)
127+
$$ LANGUAGE SQL;

src/Share/Web/UCM/SyncV2/Queries.hs

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,242 @@ import Unison.SyncV2.Types (CBORBytes)
1818

1919
allSerializedDependenciesOfCausalCursor :: CausalId -> Set CausalHash -> CodebaseM e (PGCursor (CBORBytes TempEntity, Hash32))
2020
allSerializedDependenciesOfCausalCursor cid exceptCausalHashes = do
21+
ownerUserId <- asks codebaseOwner
22+
-- Create a temp table for storing the dependencies we know the calling client already has.
23+
execute_ [sql| CREATE TEMP TABLE except_causals (causal_id INTEGER PRIMARY KEY ) ON COMMIT DROP |]
24+
execute_ [sql| CREATE TEMP TABLE except_components ( component_hash_id INTEGER PRIMARY KEY ) ON COMMIT DROP |]
25+
execute_ [sql| CREATE TEMP TABLE except_namespaces ( branch_hash_ids INTEGER PRIMARY KEY ) ON COMMIT DROP |]
26+
execute_
27+
[sql|
28+
WITH the_causal_hashes(hash) AS (
29+
SELECT * FROM ^{singleColumnTable (toList exceptCausalHashes)}
30+
), known_causal_ids(causal_id) AS (
31+
SELECT c.id
32+
FROM the_causal_hashes tch
33+
JOIN causals c ON tch.hash = c.hash
34+
), dependency_hashes(hash) AS (
35+
SELECT DISTINCT deps.hash
36+
FROM dependencies_of_causals_without_ancestors((SELECT ARRAY_AGG(kci.causal_id) FROM known_causal_ids kci)) AS deps
37+
), do_causals AS (
38+
INSERT INTO except_causals(causal_id)
39+
SELECT DISTINCT causal.id
40+
FROM dependency_hashes dh
41+
JOIN causals causal ON dh.hash = causal.hash
42+
ON CONFLICT DO NOTHING
43+
), do_namespaces AS (
44+
INSERT INTO except_namespaces(branch_hash_ids)
45+
SELECT DISTINCT bh.id
46+
FROM dependency_hashes dh
47+
JOIN branch_hashes bh ON dh.hash = bh.base32
48+
ON CONFLICT DO NOTHING
49+
) INSERT INTO except_components(component_hash_id)
50+
SELECT DISTINCT ch.id
51+
FROM dependency_hashes dh
52+
JOIN component_hashes ch ON dh.hash = ch.base32
53+
ON CONFLICT DO NOTHING
54+
|]
55+
cursor <-
56+
PGCursor.newRowCursor @(CBORBytes TempEntity, Hash32, Maybe Int32)
57+
"serialized_entities"
58+
[sql|
59+
WITH RECURSIVE transitive_causals(causal_id, causal_hash, causal_namespace_hash_id) AS (
60+
SELECT causal.id, causal.hash, causal.namespace_hash_id
61+
FROM causals causal
62+
WHERE causal.id = #{cid}
63+
AND EXISTS (SELECT FROM causal_ownership co WHERE co.user_id = #{ownerUserId} AND co.causal_id = causal.id)
64+
AND NOT EXISTS (SELECT FROM except_causals ec WHERE ec.causal_id = causal.id)
65+
UNION
66+
-- This nested CTE is required because RECURSIVE CTEs can't refer
67+
-- to the recursive table more than once.
68+
( WITH rec AS (
69+
SELECT tc.causal_id, tc.causal_namespace_hash_id
70+
FROM transitive_causals tc
71+
)
72+
SELECT ancestor_causal.id, ancestor_causal.hash, ancestor_causal.namespace_hash_id
73+
FROM causal_ancestors ca
74+
JOIN rec tc ON ca.causal_id = tc.causal_id
75+
JOIN causals ancestor_causal ON ca.ancestor_id = ancestor_causal.id
76+
WHERE NOT EXISTS (SELECT FROM except_causals ec WHERE ec.causal_id = ancestor_causal.id)
77+
UNION
78+
SELECT child_causal.id, child_causal.hash, child_causal.namespace_hash_id
79+
FROM rec tc
80+
JOIN namespace_children nc ON tc.causal_namespace_hash_id = nc.parent_namespace_hash_id
81+
JOIN causals child_causal ON nc.child_causal_id = child_causal.id
82+
WHERE NOT EXISTS (SELECT FROM except_causals ec WHERE ec.causal_id = child_causal.id)
83+
)
84+
), all_namespaces(namespace_hash_id, namespace_hash) AS (
85+
SELECT DISTINCT tc.causal_namespace_hash_id AS namespace_hash_id, bh.base32 as namespace_hash
86+
FROM transitive_causals tc
87+
JOIN branch_hashes bh ON tc.causal_namespace_hash_id = bh.id
88+
WHERE NOT EXISTS (SELECT FROM except_namespaces en WHERE en.branch_hash_ids = tc.causal_namespace_hash_id)
89+
), all_patches(patch_id, patch_hash) AS (
90+
SELECT DISTINCT patch.id, patch.hash
91+
FROM all_namespaces an
92+
JOIN namespace_patches np ON an.namespace_hash_id = np.namespace_hash_id
93+
JOIN patches patch ON np.patch_id = patch.id
94+
),
95+
-- term components to start transitively joining dependencies to
96+
base_term_components(component_hash_id) AS (
97+
SELECT DISTINCT term.component_hash_id
98+
FROM all_namespaces an
99+
JOIN namespace_terms nt ON an.namespace_hash_id = nt.namespace_hash_id
100+
JOIN terms term ON nt.term_id = term.id
101+
UNION
102+
SELECT DISTINCT term.component_hash_id
103+
FROM all_patches ap
104+
JOIN patch_term_mappings ptm ON ap.patch_id = ptm.patch_id
105+
JOIN terms term ON ptm.to_term_id = term.id
106+
UNION
107+
-- term metadata
108+
SELECT DISTINCT term.component_hash_id
109+
FROM all_namespaces an
110+
JOIN namespace_terms nt ON an.namespace_hash_id = nt.namespace_hash_id
111+
JOIN namespace_term_metadata meta ON nt.id = meta.named_term
112+
JOIN terms term ON meta.metadata_term_id = term.id
113+
UNION
114+
-- type metadata
115+
SELECT DISTINCT term.component_hash_id
116+
FROM all_namespaces an
117+
JOIN namespace_types nt ON an.namespace_hash_id = nt.namespace_hash_id
118+
JOIN namespace_type_metadata meta ON nt.id = meta.named_type
119+
JOIN terms term ON meta.metadata_term_id = term.id
120+
WHERE NOT EXISTS (SELECT FROM except_components ec WHERE ec.component_hash_id = term.component_hash_id)
121+
),
122+
-- type components to start transitively joining dependencies to
123+
base_type_components(component_hash_id) AS (
124+
SELECT DISTINCT typ.component_hash_id
125+
FROM all_namespaces an
126+
JOIN namespace_types nt ON an.namespace_hash_id = nt.namespace_hash_id
127+
JOIN types typ ON nt.type_id = typ.id
128+
UNION
129+
SELECT DISTINCT typ.component_hash_id
130+
FROM all_namespaces an
131+
JOIN namespace_terms nt ON an.namespace_hash_id = nt.namespace_hash_id
132+
JOIN constructors con ON nt.constructor_id = con.id
133+
JOIN types typ ON con.type_id = typ.id
134+
UNION
135+
SELECT DISTINCT typ.component_hash_id
136+
FROM all_patches ap
137+
JOIN patch_type_mappings ptm ON ap.patch_id = ptm.patch_id
138+
JOIN types typ ON ptm.to_type_id = typ.id
139+
UNION
140+
SELECT DISTINCT typ.component_hash_id
141+
FROM all_patches ap
142+
JOIN patch_constructor_mappings pcm ON ap.patch_id = pcm.patch_id
143+
JOIN constructors con ON pcm.to_constructor_id = con.id
144+
JOIN types typ ON con.type_id = typ.id
145+
WHERE NOT EXISTS (SELECT FROM except_components ec WHERE ec.component_hash_id = typ.component_hash_id)
146+
),
147+
-- All the dependencies we join in transitively from the known term & type components we depend on.
148+
-- Unfortunately it's not possible to know which hashes are terms vs types :'(
149+
transitive_components(component_hash_id) AS (
150+
SELECT DISTINCT btc.component_hash_id
151+
FROM base_term_components btc
152+
UNION
153+
SELECT DISTINCT btc.component_hash_id
154+
FROM base_type_components btc
155+
UNION
156+
( WITH rec AS (
157+
SELECT component_hash_id
158+
FROM transitive_components tc
159+
)
160+
-- recursively union in term dependencies
161+
SELECT DISTINCT ref.component_hash_id
162+
FROM rec atc
163+
-- This joins in ALL the terms from the component, not just the one that caused the dependency on the
164+
-- component
165+
JOIN terms term ON atc.component_hash_id = term.component_hash_id
166+
JOIN term_local_component_references ref ON term.id = ref.term_id
167+
WHERE NOT EXISTS (SELECT FROM except_components ec WHERE ec.component_hash_id = ref.component_hash_id)
168+
UNION
169+
-- recursively union in type dependencies
170+
SELECT DISTINCT ref.component_hash_id
171+
FROM rec atc
172+
-- This joins in ALL the types from the component, not just the one that caused the dependency on the
173+
-- component
174+
JOIN types typ ON atc.component_hash_id = typ.component_hash_id
175+
JOIN type_local_component_references ref ON typ.id = ref.type_id
176+
WHERE NOT EXISTS (SELECT FROM except_components ec WHERE ec.component_hash_id = ref.component_hash_id)
177+
)
178+
)
179+
(SELECT bytes.bytes, ch.base32, cd.depth
180+
FROM transitive_components tc
181+
JOIN serialized_components sc ON sc.user_id = #{ownerUserId} AND tc.component_hash_id = sc.component_hash_id
182+
JOIN bytes ON sc.bytes_id = bytes.id
183+
JOIN component_hashes ch ON tc.component_hash_id = ch.id
184+
LEFT JOIN component_depth cd ON ch.id = cd.component_hash_id
185+
)
186+
UNION ALL
187+
(SELECT bytes.bytes, ap.patch_hash, pd.depth
188+
FROM all_patches ap
189+
JOIN serialized_patches sp ON ap.patch_id = sp.patch_id
190+
JOIN bytes ON sp.bytes_id = bytes.id
191+
LEFT JOIN patch_depth pd ON ap.patch_id = pd.patch_id
192+
)
193+
UNION ALL
194+
(SELECT bytes.bytes, an.namespace_hash, nd.depth
195+
FROM all_namespaces an
196+
JOIN serialized_namespaces sn ON an.namespace_hash_id = sn.namespace_hash_id
197+
JOIN bytes ON sn.bytes_id = bytes.id
198+
LEFT JOIN namespace_depth nd ON an.namespace_hash_id = nd.namespace_hash_id
199+
)
200+
UNION ALL
201+
(SELECT bytes.bytes, tc.causal_hash, cd.depth
202+
FROM transitive_causals tc
203+
JOIN serialized_causals sc ON tc.causal_id = sc.causal_id
204+
JOIN bytes ON sc.bytes_id = bytes.id
205+
LEFT JOIN causal_depth cd ON tc.causal_id = cd.causal_id
206+
)
207+
-- Put them in dependency order, nulls come first because we want to bail and
208+
-- report an error if we are somehow missing a depth.
209+
ORDER BY depth ASC NULLS FIRST
210+
|]
211+
pure
212+
( cursor <&> \(bytes, hash, depth) -> case depth of
213+
-- This should never happen, but is a sanity check in case we're missing a depth.
214+
-- Better than silently omitting a required result.
215+
Nothing -> error $ "allSerializedDependenciesOfCausalCursor: Missing depth for entity: " <> show hash
216+
Just _ -> (bytes, hash)
217+
)
218+
219+
_spineAndLibDependenciesOfCausalCursorNewish :: CausalId -> CodebaseM e (PGCursor (Hash32, IsCausalSpine, IsLibRoot))
220+
_spineAndLibDependenciesOfCausalCursorNewish cid = do
221+
ownerUserId <- asks codebaseOwner
222+
libSegmentTextId <- queryExpect1Col @Int64 [sql| SELECT text.id FROM text WHERE content_hash = text_hash('lib') |]
223+
PGCursor.newRowCursor
224+
"causal_dependencies"
225+
[sql|
226+
WITH causal_spine(causal_id, ord) AS (
227+
-- Empty OVER clause is valid and just numbers the rows in the order they come back,
228+
-- which is what we want in this case.
229+
-- Perhaps we can use a proper order-by on causal depth once that's available.
230+
SELECT ch.causal_id, ROW_NUMBER() OVER () FROM causal_history(#{cid}) AS ch
231+
WHERE EXISTS (SELECT FROM causal_ownership co WHERE co.user_id = #{ownerUserId} AND co.causal_id = #{cid})
232+
), lib_deps(causal_id, ord) AS (
233+
SELECT DISTINCT ON (lib_dep.child_causal_id) lib_dep.child_causal_id, cs.ord
234+
FROM causal_spine cs
235+
-- Spinal causal
236+
-- Root where all library roots are attached
237+
JOIN causals spine_causal ON spine_causal.id = cs.causal_id
238+
-- The actual library dependency children
239+
JOIN namespace_children lib_root_ns ON spine_causal.namespace_hash_id = lib_root_ns.parent_namespace_hash_id
240+
JOIN causals lib_root_causal ON lib_root_ns.child_causal_id = lib_root_causal.id
241+
JOIN namespace_children lib_dep ON lib_root_causal.namespace_hash_id = lib_dep.parent_namespace_hash_id
242+
WHERE lib_root_ns.name_segment_id = #{libSegmentTextId}
243+
ORDER BY lib_dep.child_causal_id, cs.ord ASC
244+
) SELECT c.hash AS hash, true AS is_spine, false AS is_lib, cs.ord AS ord
245+
FROM causal_spine cs
246+
JOIN causals c ON cs.causal_id = c.id
247+
UNION
248+
SELECT c.hash AS hash, false AS is_spine, true AS is_lib, ld.ord AS ord
249+
FROM lib_deps ld
250+
JOIN causals c ON ld.causal_id = c.id
251+
ORDER BY ord ASC, is_lib ASC, is_spine ASC
252+
|]
253+
<&> fmap (\(hash, isSpine, isLibRoot) -> (hash, if isSpine then IsCausalSpine else NotCausalSpine, if isLibRoot then IsLibRoot else NotLibRoot))
254+
255+
_allSerializedDependenciesOfCausalCursorOlder :: CausalId -> Set CausalHash -> CodebaseM e (PGCursor (CBORBytes TempEntity, Hash32))
256+
_allSerializedDependenciesOfCausalCursorOlder cid exceptCausalHashes = do
21257
ownerUserId <- asks codebaseOwner
22258
-- Create a temp table for storing the dependencies we know the calling client already has.
23259
execute_ [sql| CREATE TEMP TABLE except_causals (causal_id INTEGER PRIMARY KEY ) ON COMMIT DROP |]

0 commit comments

Comments
 (0)