@@ -18,6 +18,242 @@ import Unison.SyncV2.Types (CBORBytes)
1818
1919allSerializedDependenciesOfCausalCursor :: CausalId -> Set CausalHash -> CodebaseM e (PGCursor (CBORBytes TempEntity , Hash32 ))
2020allSerializedDependenciesOfCausalCursor 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