mz_adapter/coord/catalog_implications/
parsed_state_updates.rs1use mz_catalog::builtin::BUILTIN_LOG_LOOKUP;
17use mz_catalog::memory::objects::{
18 CatalogItem, DataSourceDesc, StateDiff, StateUpdate, StateUpdateKind,
19};
20use mz_catalog::{durable, memory};
21use mz_compute_client::logging::LogVariant;
22use mz_controller_types::ClusterId;
23use mz_ore::instrument;
24use mz_repr::{CatalogItemId, GlobalId, Timestamp};
25use mz_storage_types::connections::inline::IntoInlineConnection;
26use mz_storage_types::sources::GenericSourceConnection;
27
28use crate::catalog::CatalogState;
30
31#[derive(Debug, Clone)]
33pub struct ParsedStateUpdate {
34 pub kind: ParsedStateUpdateKind,
35 pub ts: Timestamp,
36 pub diff: StateDiff,
37}
38
39#[derive(Debug, Clone)]
41pub enum ParsedStateUpdateKind {
42 Item {
43 durable_item: durable::objects::Item,
44 parsed_item: memory::objects::CatalogItem,
45 connection: Option<GenericSourceConnection>,
46 parsed_full_name: String,
47 },
48 Cluster {
49 durable_cluster: durable::objects::Cluster,
50 parsed_cluster: memory::objects::Cluster,
51 },
52 ClusterReplica {
53 durable_cluster_replica: durable::objects::ClusterReplica,
54 parsed_cluster_replica: memory::objects::ClusterReplica,
55 },
56 IntrospectionSourceIndex {
57 cluster_id: ClusterId,
58 log: LogVariant,
59 index_id: GlobalId,
60 },
61 ReplicaSystemConfiguration {
66 durable: durable::objects::ReplicaSystemConfiguration,
67 },
68 SystemConfiguration {
72 durable: durable::objects::SystemConfiguration,
73 },
74}
75
76#[instrument(level = "debug")]
94pub fn parse_state_update(
95 catalog: &CatalogState,
96 state_update: StateUpdate,
97) -> Option<ParsedStateUpdate> {
98 let kind = match state_update.kind {
99 StateUpdateKind::Item(item) => Some(parse_item_update(catalog, item)),
100 StateUpdateKind::Cluster(cluster) => Some(parse_cluster_update(catalog, cluster)),
101 StateUpdateKind::ClusterReplica(replica) => {
102 Some(parse_cluster_replica_update(catalog, replica))
103 }
104 StateUpdateKind::IntrospectionSourceIndex(isi) => {
105 Some(parse_introspection_source_index_update(isi))
106 }
107 StateUpdateKind::ReplicaSystemConfiguration(durable) => {
108 Some(ParsedStateUpdateKind::ReplicaSystemConfiguration { durable })
109 }
110 StateUpdateKind::SystemConfiguration(durable) => {
111 Some(ParsedStateUpdateKind::SystemConfiguration { durable })
112 }
113 _ => {
114 None
119 }
120 };
121
122 kind.map(|kind| ParsedStateUpdate {
123 kind,
124 ts: state_update.ts,
125 diff: state_update.diff,
126 })
127}
128
129fn parse_item_update(
130 catalog: &CatalogState,
131 durable_item: durable::objects::Item,
132) -> ParsedStateUpdateKind {
133 let (parsed_item, connection, parsed_full_name) =
134 parse_item_update_common(catalog, &durable_item.id);
135
136 ParsedStateUpdateKind::Item {
137 durable_item,
138 parsed_item,
139 connection,
140 parsed_full_name,
141 }
142}
143
144fn parse_item_update_common(
145 catalog: &CatalogState,
146 item_id: &CatalogItemId,
147) -> (CatalogItem, Option<GenericSourceConnection>, String) {
148 let entry = catalog.get_entry(item_id);
149
150 let parsed_item = entry.item().clone();
151 let parsed_full_name = catalog
152 .resolve_full_name(entry.name(), entry.conn_id())
153 .to_string();
154
155 let connection = match &parsed_item {
156 memory::objects::CatalogItem::Source(source) => {
157 if let DataSourceDesc::Ingestion { desc, .. }
158 | DataSourceDesc::OldSyntaxIngestion { desc, .. } = &source.data_source
159 {
160 Some(desc.connection.clone().into_inline_connection(catalog))
161 } else {
162 None
163 }
164 }
165 _ => None,
166 };
167
168 (parsed_item, connection, parsed_full_name)
169}
170
171fn parse_cluster_update(
172 catalog: &CatalogState,
173 durable_cluster: durable::objects::Cluster,
174) -> ParsedStateUpdateKind {
175 let parsed_cluster = catalog.get_cluster(durable_cluster.id);
176
177 ParsedStateUpdateKind::Cluster {
178 durable_cluster,
179 parsed_cluster: parsed_cluster.clone(),
180 }
181}
182
183fn parse_introspection_source_index_update(
184 isi: durable::objects::IntrospectionSourceIndex,
185) -> ParsedStateUpdateKind {
186 let builtin_log = BUILTIN_LOG_LOOKUP
187 .get(isi.name.as_str())
188 .expect("introspection source index must reference a known log");
189
190 ParsedStateUpdateKind::IntrospectionSourceIndex {
191 cluster_id: isi.cluster_id,
192 log: builtin_log.variant.clone(),
193 index_id: isi.index_id,
194 }
195}
196
197fn parse_cluster_replica_update(
198 catalog: &CatalogState,
199 durable_cluster_replica: durable::objects::ClusterReplica,
200) -> ParsedStateUpdateKind {
201 let parsed_cluster_replica = catalog.get_cluster_replica(
202 durable_cluster_replica.cluster_id,
203 durable_cluster_replica.replica_id,
204 );
205
206 ParsedStateUpdateKind::ClusterReplica {
207 durable_cluster_replica,
208 parsed_cluster_replica: parsed_cluster_replica.clone(),
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use mz_catalog::durable::objects::{ReplicaSystemConfiguration, SystemConfiguration};
215 use mz_catalog::memory::objects::{StateDiff, StateUpdate, StateUpdateKind};
216 use mz_controller_types::ReplicaId;
217 use mz_repr::Timestamp;
218
219 use crate::catalog::Catalog;
220
221 use super::{ParsedStateUpdateKind, parse_state_update};
222
223 #[mz_ore::test(tokio::test)]
227 #[cfg_attr(miri, ignore)] async fn replica_system_configuration_is_parsed() {
229 Catalog::with_debug(|catalog| async move {
230 let durable = ReplicaSystemConfiguration {
231 replica_id: ReplicaId::User(1),
232 name: "persist_pager".to_string(),
233 value: "on".to_string(),
234 };
235 let update = StateUpdate {
236 kind: StateUpdateKind::ReplicaSystemConfiguration(durable),
237 ts: Timestamp::MIN,
238 diff: StateDiff::Addition,
239 };
240 let parsed = parse_state_update(catalog.state(), update)
241 .expect("replica system configuration must produce a parsed update");
242 assert!(matches!(
243 parsed.kind,
244 ParsedStateUpdateKind::ReplicaSystemConfiguration { .. }
245 ));
246 })
247 .await
248 }
249
250 #[mz_ore::test(tokio::test)]
256 #[cfg_attr(miri, ignore)] async fn system_configuration_is_parsed() {
258 Catalog::with_debug(|catalog| async move {
259 let durable = SystemConfiguration {
260 name: "max_connections".to_string(),
261 value: "42".to_string(),
262 };
263 let update = StateUpdate {
264 kind: StateUpdateKind::SystemConfiguration(durable),
265 ts: Timestamp::MIN,
266 diff: StateDiff::Addition,
267 };
268 let parsed = parse_state_update(catalog.state(), update)
269 .expect("system configuration must produce a parsed update");
270 assert!(matches!(
271 parsed.kind,
272 ParsedStateUpdateKind::SystemConfiguration { .. }
273 ));
274 })
275 .await
276 }
277}