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