1use std::fmt::Debug;
37use std::sync::LazyLock;
38
39use mz_ore::collections::HashSet;
40use mz_proto::TryFromProtoError;
41use mz_repr::Diff;
42use mz_repr::adt::jsonb::Jsonb;
43use mz_storage_types::StorageDiff;
44use mz_storage_types::sources::SourceData;
45#[cfg(test)]
46use proptest_derive::Arbitrary;
47use tracing::error;
48
49use crate::durable::debug::CollectionType;
50use crate::durable::objects::serialization::{ProtoType, RustType, proto};
51use crate::durable::objects::{DurableType, FenceToken};
52use crate::durable::persist::Timestamp;
53use crate::durable::transaction::TransactionBatch;
54use crate::durable::{DurableCatalogError, Epoch};
55use crate::memory;
56
57pub trait IntoStateUpdateKindJson:
59 Into<StateUpdateKindJson> + PartialEq + Eq + PartialOrd + Ord + Debug + Clone
60{
61 type Error: Debug;
62
63 fn try_from(raw: StateUpdateKindJson) -> Result<Self, Self::Error>;
64}
65impl<
66 T: Into<StateUpdateKindJson>
67 + TryFrom<StateUpdateKindJson>
68 + PartialEq
69 + Eq
70 + PartialOrd
71 + Ord
72 + Debug
73 + Clone,
74> IntoStateUpdateKindJson for T
75where
76 T::Error: Debug,
77{
78 type Error = T::Error;
79
80 fn try_from(raw: StateUpdateKindJson) -> Result<Self, Self::Error> {
81 <T as TryFrom<StateUpdateKindJson>>::try_from(raw)
82 }
83}
84
85pub(crate) trait TryIntoStateUpdateKind: IntoStateUpdateKindJson {
87 type Error: Debug;
88
89 fn try_into(self) -> Result<StateUpdateKind, <Self as TryIntoStateUpdateKind>::Error>;
90}
91impl<T: IntoStateUpdateKindJson + TryInto<StateUpdateKind>> TryIntoStateUpdateKind for T
92where
93 <T as TryInto<StateUpdateKind>>::Error: Debug,
94{
95 type Error = <T as TryInto<StateUpdateKind>>::Error;
96
97 fn try_into(self) -> Result<StateUpdateKind, <T as TryInto<StateUpdateKind>>::Error> {
98 <T as TryInto<StateUpdateKind>>::try_into(self)
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
104pub struct StateUpdate<T: IntoStateUpdateKindJson = StateUpdateKind> {
105 pub kind: T,
107 pub ts: Timestamp,
109 pub diff: Diff,
111}
112
113impl StateUpdate {
114 pub(crate) fn from_txn_batch_ts(
116 txn_batch: TransactionBatch,
117 ts: Timestamp,
118 ) -> impl Iterator<Item = StateUpdate> {
119 Self::from_txn_batch(txn_batch).map(move |(kind, diff)| StateUpdate { kind, ts, diff })
120 }
121
122 pub(crate) fn from_txn_batch(
124 txn_batch: TransactionBatch,
125 ) -> impl Iterator<Item = (StateUpdateKind, Diff)> {
126 fn from_batch<K, V>(
127 batch: Vec<(K, V, Diff)>,
128 kind: fn(K, V) -> StateUpdateKind,
129 ) -> impl Iterator<Item = (StateUpdateKind, Diff)> {
130 batch
131 .into_iter()
132 .map(move |(k, v, diff)| (kind(k, v), diff))
133 }
134 let TransactionBatch {
135 databases,
136 schemas,
137 items,
138 item_columns,
139 index_columns,
140 comments,
141 roles,
142 role_auth,
143 clusters,
144 cluster_replicas,
145 network_policies,
146 cluster_replica_sizes,
147 introspection_sources,
148 id_allocator,
149 configs,
150 settings,
151 source_references,
152 system_gid_mapping,
153 system_configurations,
154 cluster_system_configurations,
155 replica_system_configurations,
156 default_privileges,
157 system_privileges,
158 storage_collection_metadata,
159 unfinalized_shards,
160 txn_wal_shard,
161 audit_log_updates,
162 upper: _,
163 ..
164 } = txn_batch;
165 let databases = from_batch(databases, StateUpdateKind::Database);
166 let schemas = from_batch(schemas, StateUpdateKind::Schema);
167 let items = from_batch(items, StateUpdateKind::Item);
168 let item_columns = from_batch(item_columns, StateUpdateKind::ItemColumns);
169 let index_columns = from_batch(index_columns, StateUpdateKind::IndexColumns);
170 let comments = from_batch(comments, StateUpdateKind::Comment);
171 let roles = from_batch(roles, StateUpdateKind::Role);
172 let role_auth = from_batch(role_auth, StateUpdateKind::RoleAuth);
173 let clusters = from_batch(clusters, StateUpdateKind::Cluster);
174 let cluster_replicas = from_batch(cluster_replicas, StateUpdateKind::ClusterReplica);
175 let network_policies = from_batch(network_policies, StateUpdateKind::NetworkPolicy);
176 let cluster_replica_sizes =
177 from_batch(cluster_replica_sizes, StateUpdateKind::ClusterReplicaSize);
178 let introspection_sources = from_batch(
179 introspection_sources,
180 StateUpdateKind::IntrospectionSourceIndex,
181 );
182 let id_allocators = from_batch(id_allocator, StateUpdateKind::IdAllocator);
183 let configs = from_batch(configs, StateUpdateKind::Config);
184 let settings = from_batch(settings, StateUpdateKind::Setting);
185 let system_object_mappings =
186 from_batch(system_gid_mapping, StateUpdateKind::SystemObjectMapping);
187 let system_configurations =
188 from_batch(system_configurations, StateUpdateKind::SystemConfiguration);
189 let cluster_system_configurations = from_batch(
190 cluster_system_configurations,
191 StateUpdateKind::ClusterSystemConfiguration,
192 );
193 let replica_system_configurations = from_batch(
194 replica_system_configurations,
195 StateUpdateKind::ReplicaSystemConfiguration,
196 );
197 let default_privileges = from_batch(default_privileges, StateUpdateKind::DefaultPrivilege);
198 let source_references = from_batch(source_references, StateUpdateKind::SourceReferences);
199 let system_privileges = from_batch(system_privileges, StateUpdateKind::SystemPrivilege);
200 let storage_collection_metadata = from_batch(
201 storage_collection_metadata,
202 StateUpdateKind::StorageCollectionMetadata,
203 );
204 let unfinalized_shards = from_batch(unfinalized_shards, StateUpdateKind::UnfinalizedShard);
205 let txn_wal_shard = from_batch(txn_wal_shard, StateUpdateKind::TxnWalShard);
206 let audit_logs = from_batch(audit_log_updates, StateUpdateKind::AuditLog);
207
208 databases
209 .chain(schemas)
210 .chain(items)
211 .chain(item_columns)
212 .chain(index_columns)
213 .chain(comments)
214 .chain(roles)
215 .chain(role_auth)
216 .chain(clusters)
217 .chain(cluster_replicas)
218 .chain(network_policies)
219 .chain(cluster_replica_sizes)
220 .chain(introspection_sources)
221 .chain(id_allocators)
222 .chain(configs)
223 .chain(settings)
224 .chain(source_references)
225 .chain(system_object_mappings)
226 .chain(system_configurations)
227 .chain(cluster_system_configurations)
228 .chain(replica_system_configurations)
229 .chain(default_privileges)
230 .chain(system_privileges)
231 .chain(storage_collection_metadata)
232 .chain(unfinalized_shards)
233 .chain(txn_wal_shard)
234 .chain(audit_logs)
235 }
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
243#[cfg_attr(test, derive(Arbitrary))]
244pub enum StateUpdateKind {
245 AuditLog(proto::AuditLogKey, ()),
246 Cluster(proto::ClusterKey, proto::ClusterValue),
247 ClusterReplica(proto::ClusterReplicaKey, proto::ClusterReplicaValue),
248 ClusterReplicaSize(proto::ClusterReplicaSizeKey, proto::ClusterReplicaSizeValue),
249 Comment(proto::CommentKey, proto::CommentValue),
250 Config(proto::ConfigKey, proto::ConfigValue),
251 Database(proto::DatabaseKey, proto::DatabaseValue),
252 DefaultPrivilege(proto::DefaultPrivilegesKey, proto::DefaultPrivilegesValue),
253 FenceToken(FenceToken),
254 IdAllocator(proto::IdAllocKey, proto::IdAllocValue),
255 IndexColumns(proto::IndexColumnsKey, proto::IndexColumnsValue),
256 IntrospectionSourceIndex(
257 proto::ClusterIntrospectionSourceIndexKey,
258 proto::ClusterIntrospectionSourceIndexValue,
259 ),
260 Item(proto::ItemKey, proto::ItemValue),
261 ItemColumns(proto::ItemColumnsKey, proto::ItemColumnsValue),
262 NetworkPolicy(proto::NetworkPolicyKey, proto::NetworkPolicyValue),
263 Role(proto::RoleKey, proto::RoleValue),
264 RoleAuth(proto::RoleAuthKey, proto::RoleAuthValue),
265 Schema(proto::SchemaKey, proto::SchemaValue),
266 Setting(proto::SettingKey, proto::SettingValue),
267 SourceReferences(proto::SourceReferencesKey, proto::SourceReferencesValue),
268 SystemConfiguration(
269 proto::ServerConfigurationKey,
270 proto::ServerConfigurationValue,
271 ),
272 ClusterSystemConfiguration(
273 proto::ClusterSystemConfigurationKey,
274 proto::ClusterSystemConfigurationValue,
275 ),
276 ReplicaSystemConfiguration(
277 proto::ReplicaSystemConfigurationKey,
278 proto::ReplicaSystemConfigurationValue,
279 ),
280 SystemObjectMapping(proto::GidMappingKey, proto::GidMappingValue),
281 SystemPrivilege(proto::SystemPrivilegesKey, proto::SystemPrivilegesValue),
282 StorageCollectionMetadata(
283 proto::StorageCollectionMetadataKey,
284 proto::StorageCollectionMetadataValue,
285 ),
286 UnfinalizedShard(proto::UnfinalizedShardKey, ()),
287 TxnWalShard((), proto::TxnWalShardValue),
288}
289
290impl StateUpdateKind {
291 pub(crate) fn collection_type(&self) -> Option<CollectionType> {
292 match self {
293 StateUpdateKind::AuditLog(_, _) => Some(CollectionType::AuditLog),
294 StateUpdateKind::Cluster(_, _) => Some(CollectionType::ComputeInstance),
295 StateUpdateKind::ClusterReplica(_, _) => Some(CollectionType::ComputeReplicas),
296 StateUpdateKind::ClusterReplicaSize(_, _) => Some(CollectionType::ClusterReplicaSize),
297 StateUpdateKind::Comment(_, _) => Some(CollectionType::Comments),
298 StateUpdateKind::Config(_, _) => Some(CollectionType::Config),
299 StateUpdateKind::Database(_, _) => Some(CollectionType::Database),
300 StateUpdateKind::DefaultPrivilege(_, _) => Some(CollectionType::DefaultPrivileges),
301 StateUpdateKind::FenceToken(_) => None,
302 StateUpdateKind::IdAllocator(_, _) => Some(CollectionType::IdAlloc),
303 StateUpdateKind::IndexColumns(_, _) => Some(CollectionType::IndexColumns),
304 StateUpdateKind::IntrospectionSourceIndex(_, _) => {
305 Some(CollectionType::ComputeIntrospectionSourceIndex)
306 }
307 StateUpdateKind::Item(_, _) => Some(CollectionType::Item),
308 StateUpdateKind::ItemColumns(_, _) => Some(CollectionType::ItemColumns),
309 StateUpdateKind::NetworkPolicy(_, _) => Some(CollectionType::NetworkPolicy),
310 StateUpdateKind::Role(_, _) => Some(CollectionType::Role),
311 StateUpdateKind::RoleAuth(_, _) => Some(CollectionType::RoleAuth),
312 StateUpdateKind::Schema(_, _) => Some(CollectionType::Schema),
313 StateUpdateKind::Setting(_, _) => Some(CollectionType::Setting),
314 StateUpdateKind::SourceReferences(_, _) => Some(CollectionType::SourceReferences),
315 StateUpdateKind::SystemConfiguration(_, _) => Some(CollectionType::SystemConfiguration),
316 StateUpdateKind::ClusterSystemConfiguration(_, _) => {
317 Some(CollectionType::ClusterSystemConfiguration)
318 }
319 StateUpdateKind::ReplicaSystemConfiguration(_, _) => {
320 Some(CollectionType::ReplicaSystemConfiguration)
321 }
322 StateUpdateKind::SystemObjectMapping(_, _) => Some(CollectionType::SystemGidMapping),
323 StateUpdateKind::SystemPrivilege(_, _) => Some(CollectionType::SystemPrivileges),
324 StateUpdateKind::StorageCollectionMetadata(_, _) => {
325 Some(CollectionType::StorageCollectionMetadata)
326 }
327 StateUpdateKind::UnfinalizedShard(_, _) => Some(CollectionType::UnfinalizedShard),
328 StateUpdateKind::TxnWalShard(_, _) => Some(CollectionType::TxnWalShard),
329 }
330 }
331}
332
333#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
335pub struct StateUpdateKindJson(Jsonb);
336
337impl StateUpdateKindJson {
338 pub(crate) fn from_serde<S: serde::Serialize>(s: S) -> Self {
339 let serde_value = serde_json::to_value(s).expect("valid json");
340 let row = Jsonb::from_serde_json(serde_value).expect("valid json");
341 StateUpdateKindJson(row)
342 }
343
344 pub(crate) fn to_serde<D: serde::de::DeserializeOwned>(&self) -> D {
345 self.try_to_serde().expect("jsonb should roundtrip")
346 }
347
348 pub(crate) fn try_to_serde<D: serde::de::DeserializeOwned>(
349 &self,
350 ) -> Result<D, serde_json::error::Error> {
351 let serde_value = self.0.as_ref().to_serde_json();
352 serde_json::from_value::<D>(serde_value)
353 }
354
355 fn kind(&self) -> &str {
356 let row = self.0.row();
357 let mut iter = row.unpack_first().unwrap_map().iter();
358 let datum = iter
359 .find_map(|(field, datum)| if field == "kind" { Some(datum) } else { None })
360 .expect("kind field must exist");
361 datum.unwrap_str()
362 }
363
364 pub(crate) fn is_always_deserializable(&self) -> bool {
366 static DESERIALIZABLE_KINDS: LazyLock<HashSet<String>> = LazyLock::new(|| {
369 [
370 StateUpdateKind::FenceToken(FenceToken {
371 deploy_generation: 1,
372 epoch: Epoch::new(1).expect("non-zero"),
373 }),
374 StateUpdateKind::Config(
375 proto::ConfigKey { key: String::new() },
376 proto::ConfigValue { value: 1 },
377 ),
378 StateUpdateKind::Setting(
379 proto::SettingKey {
380 name: String::new(),
381 },
382 proto::SettingValue {
383 value: String::new(),
384 },
385 ),
386 StateUpdateKind::AuditLog(
387 proto::AuditLogKey {
388 event: proto::AuditLogEvent::V1(proto::AuditLogEventV1 {
389 id: 1,
390 event_type: proto::audit_log_event_v1::EventType::Create,
391 object_type: proto::audit_log_event_v1::ObjectType::Cluster,
392 user: None,
393 occurred_at: proto::EpochMillis { millis: 1 },
394 details: proto::audit_log_event_v1::Details::ResetAllV1(
395 proto::Empty {},
396 ),
397 }),
398 },
399 (),
400 ),
401 ]
402 .into_iter()
403 .map(|kind| {
404 let json_kind: StateUpdateKindJson = kind.into();
405 json_kind.kind().to_string()
406 })
407 .collect()
408 });
409 DESERIALIZABLE_KINDS.contains(self.kind())
410 }
411
412 pub(crate) fn is_audit_log(&self) -> bool {
414 static AUDIT_LOG_KIND: LazyLock<String> = LazyLock::new(|| {
417 let audit_log = StateUpdateKind::AuditLog(
418 proto::AuditLogKey {
419 event: proto::AuditLogEvent::V1(proto::AuditLogEventV1 {
420 id: 1,
421 event_type: proto::audit_log_event_v1::EventType::Create,
422 object_type: proto::audit_log_event_v1::ObjectType::Cluster,
423 user: None,
424 occurred_at: proto::EpochMillis { millis: 1 },
425 details: proto::audit_log_event_v1::Details::ResetAllV1(proto::Empty {}),
426 }),
427 },
428 (),
429 );
430 let json_kind: StateUpdateKindJson = audit_log.into();
431 json_kind.kind().to_string()
432 });
433 &*AUDIT_LOG_KIND == self.kind()
434 }
435}
436
437type PersistStateUpdate = ((SourceData, ()), Timestamp, StorageDiff);
439
440impl TryFrom<&StateUpdate<StateUpdateKind>> for Option<memory::objects::StateUpdate> {
441 type Error = DurableCatalogError;
442
443 fn try_from(
444 StateUpdate { kind, ts, diff }: &StateUpdate<StateUpdateKind>,
445 ) -> Result<Self, Self::Error> {
446 let kind: Option<memory::objects::StateUpdateKind> = TryInto::try_into(kind)?;
447 let update = kind.map(|kind| memory::objects::StateUpdate {
448 kind,
449 ts: ts.clone(),
450 diff: diff.clone().try_into().expect("invalid diff"),
451 });
452 Ok(update)
453 }
454}
455
456impl TryFrom<&StateUpdateKind> for Option<memory::objects::StateUpdateKind> {
457 type Error = DurableCatalogError;
458
459 fn try_from(kind: &StateUpdateKind) -> Result<Self, Self::Error> {
460 fn into_durable<PK, PV, T>(key: &PK, value: &PV) -> Result<T, DurableCatalogError>
461 where
462 PK: ProtoType<T::Key> + Clone,
463 PV: ProtoType<T::Value> + Clone,
464 T: DurableType,
465 {
466 let key = key.clone().into_rust()?;
467 let value = value.clone().into_rust()?;
468 Ok(T::from_key_value(key, value))
469 }
470
471 Ok(match kind {
472 StateUpdateKind::AuditLog(key, value) => {
473 let audit_log = into_durable(key, value)?;
474 Some(memory::objects::StateUpdateKind::AuditLog(audit_log))
475 }
476 StateUpdateKind::Cluster(key, value) => {
477 let cluster = into_durable(key, value)?;
478 Some(memory::objects::StateUpdateKind::Cluster(cluster))
479 }
480 StateUpdateKind::ClusterReplica(key, value) => {
481 let cluster_replica = into_durable(key, value)?;
482 Some(memory::objects::StateUpdateKind::ClusterReplica(
483 cluster_replica,
484 ))
485 }
486 StateUpdateKind::Comment(key, value) => {
487 let comment = into_durable(key, value)?;
488 Some(memory::objects::StateUpdateKind::Comment(comment))
489 }
490 StateUpdateKind::Database(key, value) => {
491 let database = into_durable(key, value)?;
492 Some(memory::objects::StateUpdateKind::Database(database))
493 }
494 StateUpdateKind::DefaultPrivilege(key, value) => {
495 let default_privilege = into_durable(key, value)?;
496 Some(memory::objects::StateUpdateKind::DefaultPrivilege(
497 default_privilege,
498 ))
499 }
500 StateUpdateKind::Item(key, value) => {
501 let item = into_durable(key, value)?;
502 Some(memory::objects::StateUpdateKind::Item(item))
503 }
504 StateUpdateKind::IntrospectionSourceIndex(key, value) => {
505 let introspection_source_index = into_durable(key, value)?;
506 Some(memory::objects::StateUpdateKind::IntrospectionSourceIndex(
507 introspection_source_index,
508 ))
509 }
510 StateUpdateKind::NetworkPolicy(key, value) => {
511 let policy = into_durable(key, value)?;
512 Some(memory::objects::StateUpdateKind::NetworkPolicy(policy))
513 }
514 StateUpdateKind::ClusterReplicaSize(key, value) => {
515 let size = into_durable(key, value)?;
516 Some(memory::objects::StateUpdateKind::ClusterReplicaSize(size))
517 }
518 StateUpdateKind::Role(key, value) => {
519 let role = into_durable(key, value)?;
520 Some(memory::objects::StateUpdateKind::Role(role))
521 }
522 StateUpdateKind::RoleAuth(key, value) => {
523 let role_auth = into_durable(key, value)?;
524 Some(memory::objects::StateUpdateKind::RoleAuth(role_auth))
525 }
526 StateUpdateKind::Schema(key, value) => {
527 let schema = into_durable(key, value)?;
528 Some(memory::objects::StateUpdateKind::Schema(schema))
529 }
530 StateUpdateKind::SourceReferences(key, value) => {
531 let source_references = into_durable(key, value)?;
532 Some(memory::objects::StateUpdateKind::SourceReferences(
533 source_references,
534 ))
535 }
536 StateUpdateKind::StorageCollectionMetadata(key, value) => {
537 let storage_collection_metadata = into_durable(key, value)?;
538 Some(memory::objects::StateUpdateKind::StorageCollectionMetadata(
539 storage_collection_metadata,
540 ))
541 }
542 StateUpdateKind::SystemConfiguration(key, value) => {
543 let system_configuration = into_durable(key, value)?;
544 Some(memory::objects::StateUpdateKind::SystemConfiguration(
545 system_configuration,
546 ))
547 }
548 StateUpdateKind::ClusterSystemConfiguration(key, value) => {
549 let cluster_system_configuration = into_durable(key, value)?;
550 Some(
551 memory::objects::StateUpdateKind::ClusterSystemConfiguration(
552 cluster_system_configuration,
553 ),
554 )
555 }
556 StateUpdateKind::ReplicaSystemConfiguration(key, value) => {
557 let replica_system_configuration = into_durable(key, value)?;
558 Some(
559 memory::objects::StateUpdateKind::ReplicaSystemConfiguration(
560 replica_system_configuration,
561 ),
562 )
563 }
564 StateUpdateKind::SystemObjectMapping(key, value) => {
565 let system_object_mapping = into_durable(key, value)?;
566 Some(memory::objects::StateUpdateKind::SystemObjectMapping(
567 system_object_mapping,
568 ))
569 }
570 StateUpdateKind::SystemPrivilege(key, value) => {
571 let system_privilege = into_durable(key, value)?;
572 Some(memory::objects::StateUpdateKind::SystemPrivilege(
573 system_privilege,
574 ))
575 }
576 StateUpdateKind::UnfinalizedShard(key, value) => {
577 let unfinalized_shard = into_durable(key, value)?;
578 Some(memory::objects::StateUpdateKind::UnfinalizedShard(
579 unfinalized_shard,
580 ))
581 }
582 StateUpdateKind::Config(_, _)
584 | StateUpdateKind::FenceToken(_)
585 | StateUpdateKind::IdAllocator(_, _)
586 | StateUpdateKind::IndexColumns(_, _)
587 | StateUpdateKind::ItemColumns(_, _)
588 | StateUpdateKind::Setting(_, _)
589 | StateUpdateKind::TxnWalShard(_, _) => None,
590 })
591 }
592}
593
594impl TryFrom<StateUpdate<StateUpdateKindJson>> for StateUpdate<StateUpdateKind> {
595 type Error = String;
596
597 fn try_from(update: StateUpdate<StateUpdateKindJson>) -> Result<Self, Self::Error> {
598 Ok(StateUpdate {
599 kind: TryInto::try_into(update.kind)?,
600 ts: update.ts,
601 diff: update.diff,
602 })
603 }
604}
605
606impl TryFrom<StateUpdateKindJson> for StateUpdateKind {
607 type Error = String;
608
609 fn try_from(value: StateUpdateKindJson) -> Result<Self, Self::Error> {
610 let kind: proto::StateUpdateKind = value.try_to_serde().map_err(|err| err.to_string())?;
611 StateUpdateKind::from_proto(kind).map_err(|err| err.to_string())
612 }
613}
614
615impl TryFrom<&StateUpdateKindJson> for StateUpdateKind {
616 type Error = String;
617
618 fn try_from(value: &StateUpdateKindJson) -> Result<Self, Self::Error> {
619 let kind: proto::StateUpdateKind = value.try_to_serde().map_err(|err| err.to_string())?;
620 StateUpdateKind::from_proto(kind).map_err(|err| err.to_string())
621 }
622}
623
624impl From<StateUpdateKind> for StateUpdateKindJson {
625 fn from(value: StateUpdateKind) -> Self {
626 let kind = value.into_proto_owned();
627 StateUpdateKindJson::from_serde(kind)
628 }
629}
630
631impl RustType<proto::StateUpdateKind> for StateUpdateKind {
635 fn into_proto(&self) -> proto::StateUpdateKind {
636 error!("unexpected clone of catalog data");
637 self.clone().into_proto_owned()
638 }
639
640 fn into_proto_owned(self) -> proto::StateUpdateKind {
641 match self {
642 StateUpdateKind::AuditLog(key, ()) => {
643 proto::StateUpdateKind::AuditLog(proto::AuditLog { key })
644 }
645 StateUpdateKind::Cluster(key, value) => {
646 proto::StateUpdateKind::Cluster(proto::Cluster { key, value })
647 }
648 StateUpdateKind::ClusterReplica(key, value) => {
649 proto::StateUpdateKind::ClusterReplica(proto::ClusterReplica { key, value })
650 }
651 StateUpdateKind::Comment(key, value) => {
652 proto::StateUpdateKind::Comment(proto::Comment { key, value })
653 }
654 StateUpdateKind::IndexColumns(key, value) => {
655 proto::StateUpdateKind::IndexColumns(proto::IndexColumns { key, value })
656 }
657 StateUpdateKind::ItemColumns(key, value) => {
658 proto::StateUpdateKind::ItemColumns(proto::ItemColumns { key, value })
659 }
660 StateUpdateKind::Config(key, value) => {
661 proto::StateUpdateKind::Config(proto::Config { key, value })
662 }
663 StateUpdateKind::Database(key, value) => {
664 proto::StateUpdateKind::Database(proto::Database { key, value })
665 }
666 StateUpdateKind::DefaultPrivilege(key, value) => {
667 proto::StateUpdateKind::DefaultPrivileges(proto::DefaultPrivileges { key, value })
668 }
669 StateUpdateKind::FenceToken(fence_token) => {
670 proto::StateUpdateKind::FenceToken(proto::FenceToken {
671 deploy_generation: fence_token.deploy_generation,
672 epoch: fence_token.epoch.get(),
673 })
674 }
675 StateUpdateKind::IdAllocator(key, value) => {
676 proto::StateUpdateKind::IdAlloc(proto::IdAlloc { key, value })
677 }
678 StateUpdateKind::IntrospectionSourceIndex(key, value) => {
679 proto::StateUpdateKind::ClusterIntrospectionSourceIndex(
680 proto::ClusterIntrospectionSourceIndex { key, value },
681 )
682 }
683 StateUpdateKind::Item(key, value) => {
684 proto::StateUpdateKind::Item(proto::Item { key, value })
685 }
686 StateUpdateKind::NetworkPolicy(key, value) => {
687 proto::StateUpdateKind::NetworkPolicy(proto::NetworkPolicy { key, value })
688 }
689 StateUpdateKind::ClusterReplicaSize(key, value) => {
690 proto::StateUpdateKind::ClusterReplicaSize(proto::ClusterReplicaSize { key, value })
691 }
692 StateUpdateKind::Role(key, value) => {
693 proto::StateUpdateKind::Role(proto::Role { key, value })
694 }
695 StateUpdateKind::RoleAuth(key, value) => {
696 proto::StateUpdateKind::RoleAuth(proto::RoleAuth { key, value })
697 }
698 StateUpdateKind::Schema(key, value) => {
699 proto::StateUpdateKind::Schema(proto::Schema { key, value })
700 }
701 StateUpdateKind::Setting(key, value) => {
702 proto::StateUpdateKind::Setting(proto::Setting { key, value })
703 }
704 StateUpdateKind::SourceReferences(key, value) => {
705 proto::StateUpdateKind::SourceReferences(proto::SourceReferences { key, value })
706 }
707 StateUpdateKind::SystemConfiguration(key, value) => {
708 proto::StateUpdateKind::ServerConfiguration(proto::ServerConfiguration {
709 key,
710 value,
711 })
712 }
713 StateUpdateKind::ClusterSystemConfiguration(key, value) => {
714 proto::StateUpdateKind::ClusterSystemConfiguration(
715 proto::ClusterSystemConfiguration { key, value },
716 )
717 }
718 StateUpdateKind::ReplicaSystemConfiguration(key, value) => {
719 proto::StateUpdateKind::ReplicaSystemConfiguration(
720 proto::ReplicaSystemConfiguration { key, value },
721 )
722 }
723 StateUpdateKind::SystemObjectMapping(key, value) => {
724 proto::StateUpdateKind::GidMapping(proto::GidMapping { key, value })
725 }
726 StateUpdateKind::SystemPrivilege(key, value) => {
727 proto::StateUpdateKind::SystemPrivileges(proto::SystemPrivileges { key, value })
728 }
729 StateUpdateKind::StorageCollectionMetadata(key, value) => {
730 proto::StateUpdateKind::StorageCollectionMetadata(
731 proto::StorageCollectionMetadata { key, value },
732 )
733 }
734 StateUpdateKind::UnfinalizedShard(key, ()) => {
735 proto::StateUpdateKind::UnfinalizedShard(proto::UnfinalizedShard { key })
736 }
737 StateUpdateKind::TxnWalShard((), value) => {
738 proto::StateUpdateKind::TxnWalShard(proto::TxnWalShard { value })
739 }
740 }
741 }
742
743 fn from_proto(proto: proto::StateUpdateKind) -> Result<StateUpdateKind, TryFromProtoError> {
744 Ok(match proto {
745 proto::StateUpdateKind::AuditLog(proto::AuditLog { key }) => {
746 StateUpdateKind::AuditLog(key, ())
747 }
748 proto::StateUpdateKind::Cluster(proto::Cluster { key, value }) => {
749 StateUpdateKind::Cluster(key, value)
750 }
751 proto::StateUpdateKind::ClusterReplica(proto::ClusterReplica { key, value }) => {
752 StateUpdateKind::ClusterReplica(key, value)
753 }
754 proto::StateUpdateKind::Comment(proto::Comment { key, value }) => {
755 StateUpdateKind::Comment(key, value)
756 }
757 proto::StateUpdateKind::IndexColumns(proto::IndexColumns { key, value }) => {
758 StateUpdateKind::IndexColumns(key, value)
759 }
760 proto::StateUpdateKind::ItemColumns(proto::ItemColumns { key, value }) => {
761 StateUpdateKind::ItemColumns(key, value)
762 }
763 proto::StateUpdateKind::Config(proto::Config { key, value }) => {
764 StateUpdateKind::Config(key, value)
765 }
766 proto::StateUpdateKind::Database(proto::Database { key, value }) => {
767 StateUpdateKind::Database(key, value)
768 }
769 proto::StateUpdateKind::DefaultPrivileges(proto::DefaultPrivileges { key, value }) => {
770 StateUpdateKind::DefaultPrivilege(key, value)
771 }
772 proto::StateUpdateKind::FenceToken(proto::FenceToken {
773 deploy_generation,
774 epoch,
775 }) => StateUpdateKind::FenceToken(FenceToken {
776 deploy_generation,
777 epoch: Epoch::new(epoch).ok_or_else(|| {
778 TryFromProtoError::missing_field("state_update_kind::Epoch::epoch")
779 })?,
780 }),
781 proto::StateUpdateKind::IdAlloc(proto::IdAlloc { key, value }) => {
782 StateUpdateKind::IdAllocator(key, value)
783 }
784 proto::StateUpdateKind::ClusterIntrospectionSourceIndex(
785 proto::ClusterIntrospectionSourceIndex { key, value },
786 ) => StateUpdateKind::IntrospectionSourceIndex(key, value),
787 proto::StateUpdateKind::Item(proto::Item { key, value }) => {
788 StateUpdateKind::Item(key, value)
789 }
790 proto::StateUpdateKind::Role(proto::Role { key, value }) => {
791 StateUpdateKind::Role(key, value)
792 }
793 proto::StateUpdateKind::RoleAuth(proto::RoleAuth { key, value }) => {
794 StateUpdateKind::RoleAuth(key, value)
795 }
796 proto::StateUpdateKind::Schema(proto::Schema { key, value }) => {
797 StateUpdateKind::Schema(key, value)
798 }
799 proto::StateUpdateKind::Setting(proto::Setting { key, value }) => {
800 StateUpdateKind::Setting(key, value)
801 }
802 proto::StateUpdateKind::ServerConfiguration(proto::ServerConfiguration {
803 key,
804 value,
805 }) => StateUpdateKind::SystemConfiguration(key, value),
806 proto::StateUpdateKind::ClusterSystemConfiguration(
807 proto::ClusterSystemConfiguration { key, value },
808 ) => StateUpdateKind::ClusterSystemConfiguration(key, value),
809 proto::StateUpdateKind::ReplicaSystemConfiguration(
810 proto::ReplicaSystemConfiguration { key, value },
811 ) => StateUpdateKind::ReplicaSystemConfiguration(key, value),
812 proto::StateUpdateKind::GidMapping(proto::GidMapping { key, value }) => {
813 StateUpdateKind::SystemObjectMapping(key, value)
814 }
815 proto::StateUpdateKind::SystemPrivileges(proto::SystemPrivileges { key, value }) => {
816 StateUpdateKind::SystemPrivilege(key, value)
817 }
818 proto::StateUpdateKind::StorageCollectionMetadata(
819 proto::StorageCollectionMetadata { key, value },
820 ) => StateUpdateKind::StorageCollectionMetadata(key, value),
821 proto::StateUpdateKind::UnfinalizedShard(proto::UnfinalizedShard { key }) => {
822 StateUpdateKind::UnfinalizedShard(key, ())
823 }
824 proto::StateUpdateKind::TxnWalShard(proto::TxnWalShard { value }) => {
825 StateUpdateKind::TxnWalShard((), value)
826 }
827 proto::StateUpdateKind::SourceReferences(proto::SourceReferences { key, value }) => {
828 StateUpdateKind::SourceReferences(key, value)
829 }
830 proto::StateUpdateKind::NetworkPolicy(proto::NetworkPolicy { key, value }) => {
831 StateUpdateKind::NetworkPolicy(key, value)
832 }
833 proto::StateUpdateKind::ClusterReplicaSize(proto::ClusterReplicaSize {
834 key,
835 value,
836 }) => StateUpdateKind::ClusterReplicaSize(key, value),
837 })
838 }
839}
840
841impl From<PersistStateUpdate> for StateUpdate<StateUpdateKindJson> {
844 fn from(kvtd: PersistStateUpdate) -> Self {
845 let ((key, ()), ts, diff) = kvtd;
846 StateUpdate {
847 kind: StateUpdateKindJson::from(key),
848 ts,
849 diff: diff.into(),
850 }
851 }
852}
853
854impl From<StateUpdateKindJson> for SourceData {
855 fn from(value: StateUpdateKindJson) -> SourceData {
856 let row = value.0.into_row();
857 SourceData(Ok(row))
858 }
859}
860
861impl From<SourceData> for StateUpdateKindJson {
862 fn from(value: SourceData) -> Self {
863 let row = value.0.expect("only Ok values stored in catalog shard");
864 StateUpdateKindJson(Jsonb::from_row(row))
865 }
866}
867
868#[cfg(test)]
869mod tests {
870 use mz_persist_types::Codec;
871 use mz_repr::{RelationDesc, SqlScalarType};
872 use mz_storage_types::sources::SourceData;
873 use proptest::prelude::*;
874
875 use crate::durable::Epoch;
876 use crate::durable::objects::FenceToken;
877 use crate::durable::objects::serialization::proto;
878 use crate::durable::objects::state_update::{StateUpdateKind, StateUpdateKindJson};
879
880 #[mz_ore::test]
881 #[cfg_attr(miri, ignore)]
882 fn kind_test() {
883 let test_cases = [
884 (
885 StateUpdateKind::FenceToken(FenceToken {
886 deploy_generation: 1,
887 epoch: Epoch::new(1).expect("non-zero"),
888 }),
889 "FenceToken",
890 ),
891 (
892 StateUpdateKind::Config(
893 proto::ConfigKey { key: String::new() },
894 proto::ConfigValue { value: 1 },
895 ),
896 "Config",
897 ),
898 (
899 StateUpdateKind::Setting(
900 proto::SettingKey {
901 name: String::new(),
902 },
903 proto::SettingValue {
904 value: String::new(),
905 },
906 ),
907 "Setting",
908 ),
909 (
910 StateUpdateKind::AuditLog(
911 proto::AuditLogKey {
912 event: proto::AuditLogEvent::V1(proto::AuditLogEventV1 {
913 id: 1,
914 event_type: proto::audit_log_event_v1::EventType::Create,
915 object_type: proto::audit_log_event_v1::ObjectType::Cluster,
916 user: None,
917 occurred_at: proto::EpochMillis { millis: 4 },
918 details: proto::audit_log_event_v1::Details::ResetAllV1(
919 proto::Empty {},
920 ),
921 }),
922 },
923 (),
924 ),
925 "AuditLog",
926 ),
927 ];
928
929 for (kind, expected) in test_cases {
930 let json_kind: StateUpdateKindJson = kind.into();
931 let kind = json_kind.kind().to_string();
932 assert_eq!(expected, kind);
933 }
934 }
935
936 proptest! {
937 #[mz_ore::test]
938 #[cfg_attr(miri, ignore)] fn proptest_state_update_kind_roundtrip(kind: StateUpdateKind) {
940 let raw = StateUpdateKindJson::from(kind.clone());
943 let desc = RelationDesc::builder().with_column("a", SqlScalarType::Jsonb.nullable(false)).finish();
944
945 let source_data = SourceData::from(raw.clone());
947 let mut encoded = Vec::new();
948 source_data.encode(&mut encoded);
949 let decoded = SourceData::decode(&encoded, &desc).expect("should be valid SourceData");
950 prop_assert_eq!(&source_data, &decoded);
951 let decoded = StateUpdateKindJson::from(decoded);
952 prop_assert_eq!(&raw, &decoded);
953
954 let decoded = StateUpdateKind::try_from(decoded).expect("should be valid StateUpdateKind");
956 prop_assert_eq!(&kind, &decoded);
957 }
958 }
959}