1pub mod serialization;
29pub(crate) mod state_update;
30
31use std::cmp::Ordering;
32use std::collections::BTreeMap;
33
34use mz_audit_log::VersionedEvent;
35use mz_controller::clusters::ReplicaLogging;
36use mz_controller_types::{ClusterId, ReplicaId};
37use mz_persist_types::ShardId;
38use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem};
39use mz_repr::network_policy_id::NetworkPolicyId;
40use mz_repr::role_id::RoleId;
41use mz_repr::{CatalogItemId, GlobalId, RelationVersion};
42use mz_sql::catalog::{
43 CatalogItemType, DefaultPrivilegeAclItem, DefaultPrivilegeObject, ObjectType, RoleAttributes,
44 RoleMembership, RoleVars,
45};
46use mz_sql::names::{CommentObjectId, DatabaseId, SchemaId};
47use mz_sql::plan::{ClusterSchedule, NetworkPolicyRule};
48use proptest_derive::Arbitrary;
49
50use crate::builtin::RUNTIME_ALTERABLE_FINGERPRINT_SENTINEL;
51use crate::durable::Epoch;
52use crate::durable::objects::serialization::proto;
53
54pub trait DurableType: Sized {
69 type Key;
70 type Value;
71
72 fn into_key_value(self) -> (Self::Key, Self::Value);
74
75 fn from_key_value(key: Self::Key, value: Self::Value) -> Self;
78
79 fn key(&self) -> Self::Key;
84}
85
86#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
87pub struct Database {
88 pub id: DatabaseId,
89 pub oid: u32,
90 pub name: String,
91 pub owner_id: RoleId,
92 pub privileges: Vec<MzAclItem>,
93}
94
95impl DurableType for Database {
96 type Key = DatabaseKey;
97 type Value = DatabaseValue;
98
99 fn into_key_value(self) -> (Self::Key, Self::Value) {
100 (
101 DatabaseKey { id: self.id },
102 DatabaseValue {
103 oid: self.oid,
104 name: self.name,
105 owner_id: self.owner_id,
106 privileges: self.privileges,
107 },
108 )
109 }
110
111 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
112 Self {
113 id: key.id,
114 oid: value.oid,
115 name: value.name,
116 owner_id: value.owner_id,
117 privileges: value.privileges,
118 }
119 }
120
121 fn key(&self) -> Self::Key {
122 DatabaseKey { id: self.id }
123 }
124}
125
126#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
127pub struct Schema {
128 pub id: SchemaId,
129 pub oid: u32,
130 pub name: String,
131 pub database_id: Option<DatabaseId>,
132 pub owner_id: RoleId,
133 pub privileges: Vec<MzAclItem>,
134}
135
136impl DurableType for Schema {
137 type Key = SchemaKey;
138 type Value = SchemaValue;
139
140 fn into_key_value(self) -> (Self::Key, Self::Value) {
141 (
142 SchemaKey { id: self.id },
143 SchemaValue {
144 oid: self.oid,
145 database_id: self.database_id,
146 name: self.name,
147 owner_id: self.owner_id,
148 privileges: self.privileges,
149 },
150 )
151 }
152
153 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
154 Self {
155 id: key.id,
156 oid: value.oid,
157 name: value.name,
158 database_id: value.database_id,
159 owner_id: value.owner_id,
160 privileges: value.privileges,
161 }
162 }
163
164 fn key(&self) -> Self::Key {
165 SchemaKey { id: self.id }
166 }
167}
168
169#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
170pub struct Role {
171 pub id: RoleId,
172 pub oid: u32,
173 pub name: String,
174 pub attributes: RoleAttributes,
175 pub membership: RoleMembership,
176 pub vars: RoleVars,
177}
178
179impl DurableType for Role {
180 type Key = RoleKey;
181 type Value = RoleValue;
182
183 fn into_key_value(self) -> (Self::Key, Self::Value) {
184 (
185 RoleKey { id: self.id },
186 RoleValue {
187 oid: self.oid,
188 name: self.name,
189 attributes: self.attributes,
190 membership: self.membership,
191 vars: self.vars,
192 },
193 )
194 }
195
196 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
197 Self {
198 id: key.id,
199 oid: value.oid,
200 name: value.name,
201 attributes: value.attributes,
202 membership: value.membership,
203 vars: value.vars,
204 }
205 }
206
207 fn key(&self) -> Self::Key {
208 RoleKey { id: self.id }
209 }
210}
211
212#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
213pub struct RoleAuth {
214 pub role_id: RoleId,
215 pub password_hash: Option<String>,
216 pub updated_at: u64,
217}
218
219impl DurableType for RoleAuth {
220 type Key = RoleAuthKey;
221 type Value = RoleAuthValue;
222
223 fn into_key_value(self) -> (Self::Key, Self::Value) {
224 (
225 RoleAuthKey {
226 role_id: self.role_id,
227 },
228 RoleAuthValue {
229 password_hash: self.password_hash,
230 updated_at: self.updated_at,
231 },
232 )
233 }
234
235 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
236 Self {
237 role_id: key.role_id,
238 password_hash: value.password_hash,
239 updated_at: value.updated_at,
240 }
241 }
242
243 fn key(&self) -> Self::Key {
244 RoleAuthKey {
245 role_id: self.role_id,
246 }
247 }
248}
249
250#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
251pub struct NetworkPolicy {
252 pub name: String,
253 pub id: NetworkPolicyId,
254 pub oid: u32,
255 pub rules: Vec<NetworkPolicyRule>,
256 pub owner_id: RoleId,
257 pub(crate) privileges: Vec<MzAclItem>,
258}
259
260impl DurableType for NetworkPolicy {
261 type Key = NetworkPolicyKey;
262 type Value = NetworkPolicyValue;
263
264 fn into_key_value(self) -> (Self::Key, Self::Value) {
265 (
266 NetworkPolicyKey { id: self.id },
267 NetworkPolicyValue {
268 oid: self.oid,
269 name: self.name,
270 rules: self.rules,
271 owner_id: self.owner_id,
272 privileges: self.privileges,
273 },
274 )
275 }
276
277 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
278 Self {
279 id: key.id,
280 oid: value.oid,
281 name: value.name,
282 rules: value.rules,
283 owner_id: value.owner_id,
284 privileges: value.privileges,
285 }
286 }
287
288 fn key(&self) -> Self::Key {
289 NetworkPolicyKey { id: self.id }
290 }
291}
292
293#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
294pub struct Cluster {
295 pub id: ClusterId,
296 pub name: String,
297 pub owner_id: RoleId,
298 pub privileges: Vec<MzAclItem>,
299 pub config: ClusterConfig,
300}
301
302impl DurableType for Cluster {
303 type Key = ClusterKey;
304 type Value = ClusterValue;
305
306 fn into_key_value(self) -> (Self::Key, Self::Value) {
307 (
308 ClusterKey { id: self.id },
309 ClusterValue {
310 name: self.name,
311 owner_id: self.owner_id,
312 privileges: self.privileges,
313 config: self.config,
314 },
315 )
316 }
317
318 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
319 Self {
320 id: key.id,
321 name: value.name,
322 owner_id: value.owner_id,
323 privileges: value.privileges,
324 config: value.config,
325 }
326 }
327
328 fn key(&self) -> Self::Key {
329 ClusterKey { id: self.id }
330 }
331}
332
333#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)]
334pub struct ClusterConfig {
335 pub variant: ClusterVariant,
336 pub workload_class: Option<String>,
337}
338
339#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)]
340pub enum ClusterVariant {
341 Managed(ClusterVariantManaged),
342 Unmanaged,
343}
344
345#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)]
346pub struct ClusterVariantManaged {
347 pub size: String,
348 pub availability_zones: Vec<String>,
349 pub logging: ReplicaLogging,
350 pub replication_factor: u32,
351 pub optimizer_feature_overrides: BTreeMap<String, String>,
352 pub schedule: ClusterSchedule,
353}
354
355#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
356pub struct IntrospectionSourceIndex {
357 pub cluster_id: ClusterId,
358 pub name: String,
359 pub item_id: CatalogItemId,
360 pub index_id: GlobalId,
361 pub oid: u32,
362}
363
364impl DurableType for IntrospectionSourceIndex {
365 type Key = ClusterIntrospectionSourceIndexKey;
366 type Value = ClusterIntrospectionSourceIndexValue;
367
368 fn into_key_value(self) -> (Self::Key, Self::Value) {
369 (
370 ClusterIntrospectionSourceIndexKey {
371 cluster_id: self.cluster_id,
372 name: self.name,
373 },
374 ClusterIntrospectionSourceIndexValue {
375 catalog_id: self
376 .item_id
377 .try_into()
378 .expect("cluster introspection source index mapping must be an Introspection Source Index ID"),
379 global_id: self
380 .index_id
381 .try_into()
382 .expect("cluster introspection source index mapping must be a Introspection Source Index ID"),
383 oid: self.oid,
384 },
385 )
386 }
387
388 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
389 Self {
390 cluster_id: key.cluster_id,
391 name: key.name,
392 item_id: value.catalog_id.into(),
393 index_id: value.global_id.into(),
394 oid: value.oid,
395 }
396 }
397
398 fn key(&self) -> Self::Key {
399 ClusterIntrospectionSourceIndexKey {
400 cluster_id: self.cluster_id,
401 name: self.name.clone(),
402 }
403 }
404}
405
406#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
407pub struct ClusterReplica {
408 pub cluster_id: ClusterId,
409 pub replica_id: ReplicaId,
410 pub name: String,
411 pub config: ReplicaConfig,
412 pub owner_id: RoleId,
413}
414
415impl DurableType for ClusterReplica {
416 type Key = ClusterReplicaKey;
417 type Value = ClusterReplicaValue;
418
419 fn into_key_value(self) -> (Self::Key, Self::Value) {
420 (
421 ClusterReplicaKey {
422 id: self.replica_id,
423 },
424 ClusterReplicaValue {
425 cluster_id: self.cluster_id,
426 name: self.name,
427 config: self.config,
428 owner_id: self.owner_id,
429 },
430 )
431 }
432
433 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
434 Self {
435 cluster_id: value.cluster_id,
436 replica_id: key.id,
437 name: value.name,
438 config: value.config,
439 owner_id: value.owner_id,
440 }
441 }
442
443 fn key(&self) -> Self::Key {
444 ClusterReplicaKey {
445 id: self.replica_id,
446 }
447 }
448}
449
450#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)]
454pub struct ReplicaConfig {
455 pub location: ReplicaLocation,
456 pub logging: ReplicaLogging,
457}
458
459impl From<mz_controller::clusters::ReplicaConfig> for ReplicaConfig {
460 fn from(config: mz_controller::clusters::ReplicaConfig) -> Self {
461 Self {
462 location: config.location.into(),
463 logging: config.compute.logging,
464 }
465 }
466}
467
468#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
469pub enum ReplicaLocation {
470 Unmanaged {
471 storagectl_addrs: Vec<String>,
472 computectl_addrs: Vec<String>,
473 },
474 Managed {
475 size: String,
476 availability_zone: Option<String>,
478 internal: bool,
479 billed_as: Option<String>,
480 pending: bool,
481 },
482}
483
484impl From<mz_controller::clusters::ReplicaLocation> for ReplicaLocation {
485 fn from(loc: mz_controller::clusters::ReplicaLocation) -> Self {
486 match loc {
487 mz_controller::clusters::ReplicaLocation::Unmanaged(
488 mz_controller::clusters::UnmanagedReplicaLocation {
489 storagectl_addrs,
490 computectl_addrs,
491 },
492 ) => Self::Unmanaged {
493 storagectl_addrs,
494 computectl_addrs,
495 },
496 mz_controller::clusters::ReplicaLocation::Managed(
497 mz_controller::clusters::ManagedReplicaLocation {
498 allocation: _,
499 size,
500 availability_zones,
501 billed_as,
502 internal,
503 pending,
504 },
505 ) => ReplicaLocation::Managed {
506 size,
507 availability_zone:
508 if let mz_controller::clusters::ManagedReplicaAvailabilityZones::FromReplica(
509 Some(az),
510 ) = availability_zones
511 {
512 Some(az)
513 } else {
514 None
515 },
516 internal,
517 billed_as,
518 pending,
519 },
520 }
521 }
522}
523
524#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
525pub struct Item {
526 pub id: CatalogItemId,
527 pub oid: u32,
528 pub global_id: GlobalId,
529 pub schema_id: SchemaId,
530 pub name: String,
531 pub create_sql: String,
532 pub owner_id: RoleId,
533 pub privileges: Vec<MzAclItem>,
534 pub extra_versions: BTreeMap<RelationVersion, GlobalId>,
535}
536
537impl Item {
538 pub fn item_type(&self) -> CatalogItemType {
539 item_type(&self.create_sql)
540 }
541}
542
543impl DurableType for Item {
544 type Key = ItemKey;
545 type Value = ItemValue;
546
547 fn into_key_value(self) -> (Self::Key, Self::Value) {
548 (
549 ItemKey { id: self.id },
550 ItemValue {
551 oid: self.oid,
552 global_id: self.global_id,
553 schema_id: self.schema_id,
554 name: self.name,
555 create_sql: self.create_sql,
556 owner_id: self.owner_id,
557 privileges: self.privileges,
558 extra_versions: self.extra_versions,
559 },
560 )
561 }
562
563 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
564 Self {
565 id: key.id,
566 oid: value.oid,
567 global_id: value.global_id,
568 schema_id: value.schema_id,
569 name: value.name,
570 create_sql: value.create_sql,
571 owner_id: value.owner_id,
572 privileges: value.privileges,
573 extra_versions: value.extra_versions,
574 }
575 }
576
577 fn key(&self) -> Self::Key {
578 ItemKey { id: self.id }
579 }
580}
581
582#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
583pub struct SourceReferences {
584 pub source_id: CatalogItemId,
585 pub updated_at: u64,
586 pub references: Vec<SourceReference>,
587}
588
589#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Arbitrary)]
590pub struct SourceReference {
591 pub name: String,
592 pub namespace: Option<String>,
593 pub columns: Vec<String>,
594}
595
596impl DurableType for SourceReferences {
597 type Key = SourceReferencesKey;
598 type Value = SourceReferencesValue;
599
600 fn into_key_value(self) -> (Self::Key, Self::Value) {
601 (
602 SourceReferencesKey {
603 source_id: self.source_id,
604 },
605 SourceReferencesValue {
606 updated_at: self.updated_at,
607 references: self.references,
608 },
609 )
610 }
611
612 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
613 Self {
614 source_id: key.source_id,
615 updated_at: value.updated_at,
616 references: value.references,
617 }
618 }
619
620 fn key(&self) -> Self::Key {
621 SourceReferencesKey {
622 source_id: self.source_id,
623 }
624 }
625}
626
627#[derive(Debug, Copy, Clone, Ord, PartialOrd, PartialEq, Eq)]
629pub struct SystemCatalogItemId(u64);
630
631impl TryFrom<CatalogItemId> for SystemCatalogItemId {
632 type Error = &'static str;
633
634 fn try_from(val: CatalogItemId) -> Result<Self, Self::Error> {
635 match val {
636 CatalogItemId::System(x) => Ok(SystemCatalogItemId(x)),
637 CatalogItemId::IntrospectionSourceIndex(_) => Err("introspection_source_index"),
638 CatalogItemId::User(_) => Err("user"),
639 CatalogItemId::Transient(_) => Err("transient"),
640 }
641 }
642}
643
644impl From<SystemCatalogItemId> for CatalogItemId {
645 fn from(val: SystemCatalogItemId) -> Self {
646 CatalogItemId::System(val.0)
647 }
648}
649
650#[derive(Debug, Copy, Clone, Ord, PartialOrd, PartialEq, Eq)]
652pub struct IntrospectionSourceIndexCatalogItemId(u64);
653
654impl TryFrom<CatalogItemId> for IntrospectionSourceIndexCatalogItemId {
655 type Error = &'static str;
656
657 fn try_from(val: CatalogItemId) -> Result<Self, Self::Error> {
658 match val {
659 CatalogItemId::System(_) => Err("system"),
660 CatalogItemId::IntrospectionSourceIndex(x) => {
661 Ok(IntrospectionSourceIndexCatalogItemId(x))
662 }
663 CatalogItemId::User(_) => Err("user"),
664 CatalogItemId::Transient(_) => Err("transient"),
665 }
666 }
667}
668
669impl From<IntrospectionSourceIndexCatalogItemId> for CatalogItemId {
670 fn from(val: IntrospectionSourceIndexCatalogItemId) -> Self {
671 CatalogItemId::IntrospectionSourceIndex(val.0)
672 }
673}
674
675#[derive(Debug, Copy, Clone, Ord, PartialOrd, PartialEq, Eq)]
677pub struct SystemGlobalId(u64);
678
679impl TryFrom<GlobalId> for SystemGlobalId {
680 type Error = &'static str;
681
682 fn try_from(val: GlobalId) -> Result<Self, Self::Error> {
683 match val {
684 GlobalId::System(x) => Ok(SystemGlobalId(x)),
685 GlobalId::IntrospectionSourceIndex(_) => Err("introspection_source_index"),
686 GlobalId::User(_) => Err("user"),
687 GlobalId::Transient(_) => Err("transient"),
688 GlobalId::Explain => Err("explain"),
689 }
690 }
691}
692
693impl From<SystemGlobalId> for GlobalId {
694 fn from(val: SystemGlobalId) -> Self {
695 GlobalId::System(val.0)
696 }
697}
698
699#[derive(Debug, Copy, Clone, Ord, PartialOrd, PartialEq, Eq)]
701pub struct IntrospectionSourceIndexGlobalId(u64);
702
703impl TryFrom<GlobalId> for IntrospectionSourceIndexGlobalId {
704 type Error = &'static str;
705
706 fn try_from(val: GlobalId) -> Result<Self, Self::Error> {
707 match val {
708 GlobalId::System(_) => Err("system"),
709 GlobalId::IntrospectionSourceIndex(x) => Ok(IntrospectionSourceIndexGlobalId(x)),
710 GlobalId::User(_) => Err("user"),
711 GlobalId::Transient(_) => Err("transient"),
712 GlobalId::Explain => Err("explain"),
713 }
714 }
715}
716
717impl From<IntrospectionSourceIndexGlobalId> for GlobalId {
718 fn from(val: IntrospectionSourceIndexGlobalId) -> Self {
719 GlobalId::IntrospectionSourceIndex(val.0)
720 }
721}
722
723#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
724pub struct SystemObjectDescription {
725 pub schema_name: String,
726 pub object_type: CatalogItemType,
727 pub object_name: String,
728}
729
730#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
731pub struct SystemObjectUniqueIdentifier {
732 pub catalog_id: CatalogItemId,
733 pub global_id: GlobalId,
734 pub fingerprint: String,
735}
736
737impl SystemObjectUniqueIdentifier {
738 pub fn runtime_alterable(&self) -> bool {
739 self.fingerprint == RUNTIME_ALTERABLE_FINGERPRINT_SENTINEL
740 }
741}
742
743#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
751pub struct SystemObjectMapping {
752 pub description: SystemObjectDescription,
753 pub unique_identifier: SystemObjectUniqueIdentifier,
754}
755
756impl DurableType for SystemObjectMapping {
757 type Key = GidMappingKey;
758 type Value = GidMappingValue;
759
760 fn into_key_value(self) -> (Self::Key, Self::Value) {
761 (
762 GidMappingKey {
763 schema_name: self.description.schema_name,
764 object_type: self.description.object_type,
765 object_name: self.description.object_name,
766 },
767 GidMappingValue {
768 catalog_id: self
769 .unique_identifier
770 .catalog_id
771 .try_into()
772 .expect("catalog_id to be in the system namespace"),
773 global_id: self
774 .unique_identifier
775 .global_id
776 .try_into()
777 .expect("collection_id to be in the system namespace"),
778 fingerprint: self.unique_identifier.fingerprint,
779 },
780 )
781 }
782
783 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
784 Self {
785 description: SystemObjectDescription {
786 schema_name: key.schema_name,
787 object_type: key.object_type,
788 object_name: key.object_name,
789 },
790 unique_identifier: SystemObjectUniqueIdentifier {
791 catalog_id: value.catalog_id.into(),
792 global_id: value.global_id.into(),
793 fingerprint: value.fingerprint,
794 },
795 }
796 }
797
798 fn key(&self) -> Self::Key {
799 GidMappingKey {
800 schema_name: self.description.schema_name.clone(),
801 object_type: self.description.object_type.clone(),
802 object_name: self.description.object_name.clone(),
803 }
804 }
805}
806
807#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
808pub struct DefaultPrivilege {
809 pub object: DefaultPrivilegeObject,
810 pub acl_item: DefaultPrivilegeAclItem,
811}
812
813impl DurableType for DefaultPrivilege {
814 type Key = DefaultPrivilegesKey;
815 type Value = DefaultPrivilegesValue;
816
817 fn into_key_value(self) -> (Self::Key, Self::Value) {
818 (
819 DefaultPrivilegesKey {
820 role_id: self.object.role_id,
821 database_id: self.object.database_id,
822 schema_id: self.object.schema_id,
823 object_type: self.object.object_type,
824 grantee: self.acl_item.grantee,
825 },
826 DefaultPrivilegesValue {
827 privileges: self.acl_item.acl_mode,
828 },
829 )
830 }
831
832 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
833 Self {
834 object: DefaultPrivilegeObject {
835 role_id: key.role_id,
836 database_id: key.database_id,
837 schema_id: key.schema_id,
838 object_type: key.object_type,
839 },
840 acl_item: DefaultPrivilegeAclItem {
841 grantee: key.grantee,
842 acl_mode: value.privileges,
843 },
844 }
845 }
846
847 fn key(&self) -> Self::Key {
848 DefaultPrivilegesKey {
849 role_id: self.object.role_id,
850 database_id: self.object.database_id,
851 schema_id: self.object.schema_id,
852 object_type: self.object.object_type,
853 grantee: self.acl_item.grantee,
854 }
855 }
856}
857
858#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
859pub struct Comment {
860 pub object_id: CommentObjectId,
861 pub sub_component: Option<usize>,
862 pub comment: String,
863}
864
865impl DurableType for Comment {
866 type Key = CommentKey;
867 type Value = CommentValue;
868
869 fn into_key_value(self) -> (Self::Key, Self::Value) {
870 (
871 CommentKey {
872 object_id: self.object_id,
873 sub_component: self.sub_component,
874 },
875 CommentValue {
876 comment: self.comment,
877 },
878 )
879 }
880
881 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
882 Self {
883 object_id: key.object_id,
884 sub_component: key.sub_component,
885 comment: value.comment,
886 }
887 }
888
889 fn key(&self) -> Self::Key {
890 CommentKey {
891 object_id: self.object_id,
892 sub_component: self.sub_component,
893 }
894 }
895}
896
897#[derive(Debug, Clone, PartialEq, Eq)]
898pub struct IdAlloc {
899 pub name: String,
900 pub next_id: u64,
901}
902
903impl DurableType for IdAlloc {
904 type Key = IdAllocKey;
905 type Value = IdAllocValue;
906
907 fn into_key_value(self) -> (Self::Key, Self::Value) {
908 (
909 IdAllocKey { name: self.name },
910 IdAllocValue {
911 next_id: self.next_id,
912 },
913 )
914 }
915
916 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
917 Self {
918 name: key.name,
919 next_id: value.next_id,
920 }
921 }
922
923 fn key(&self) -> Self::Key {
924 IdAllocKey {
925 name: self.name.clone(),
926 }
927 }
928}
929
930#[derive(Debug, Clone, PartialEq, Eq)]
931pub struct Config {
932 pub key: String,
933 pub value: u64,
934}
935
936impl DurableType for Config {
937 type Key = ConfigKey;
938 type Value = ConfigValue;
939
940 fn into_key_value(self) -> (Self::Key, Self::Value) {
941 (
942 ConfigKey { key: self.key },
943 ConfigValue { value: self.value },
944 )
945 }
946
947 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
948 Self {
949 key: key.key,
950 value: value.value,
951 }
952 }
953
954 fn key(&self) -> Self::Key {
955 ConfigKey {
956 key: self.key.clone(),
957 }
958 }
959}
960
961#[derive(Debug, Clone)]
962pub struct Setting {
963 pub name: String,
964 pub value: String,
965}
966
967impl DurableType for Setting {
968 type Key = SettingKey;
969 type Value = SettingValue;
970
971 fn into_key_value(self) -> (Self::Key, Self::Value) {
972 (
973 SettingKey { name: self.name },
974 SettingValue { value: self.value },
975 )
976 }
977
978 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
979 Self {
980 name: key.name,
981 value: value.value,
982 }
983 }
984
985 fn key(&self) -> Self::Key {
986 SettingKey {
987 name: self.name.clone(),
988 }
989 }
990}
991
992#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
993pub struct SystemConfiguration {
994 pub name: String,
995 pub value: String,
996}
997
998impl DurableType for SystemConfiguration {
999 type Key = ServerConfigurationKey;
1000 type Value = ServerConfigurationValue;
1001
1002 fn into_key_value(self) -> (Self::Key, Self::Value) {
1003 (
1004 ServerConfigurationKey { name: self.name },
1005 ServerConfigurationValue { value: self.value },
1006 )
1007 }
1008
1009 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
1010 Self {
1011 name: key.name,
1012 value: value.value,
1013 }
1014 }
1015
1016 fn key(&self) -> Self::Key {
1017 ServerConfigurationKey {
1018 name: self.name.clone(),
1019 }
1020 }
1021}
1022
1023impl DurableType for MzAclItem {
1024 type Key = SystemPrivilegesKey;
1025 type Value = SystemPrivilegesValue;
1026
1027 fn into_key_value(self) -> (Self::Key, Self::Value) {
1028 (
1029 SystemPrivilegesKey {
1030 grantee: self.grantee,
1031 grantor: self.grantor,
1032 },
1033 SystemPrivilegesValue {
1034 acl_mode: self.acl_mode,
1035 },
1036 )
1037 }
1038
1039 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
1040 Self {
1041 grantee: key.grantee,
1042 grantor: key.grantor,
1043 acl_mode: value.acl_mode,
1044 }
1045 }
1046
1047 fn key(&self) -> Self::Key {
1048 SystemPrivilegesKey {
1049 grantee: self.grantee,
1050 grantor: self.grantor,
1051 }
1052 }
1053}
1054
1055#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
1056pub struct AuditLog {
1057 pub event: VersionedEvent,
1058}
1059
1060impl DurableType for AuditLog {
1061 type Key = AuditLogKey;
1062 type Value = ();
1063
1064 fn into_key_value(self) -> (Self::Key, Self::Value) {
1065 (AuditLogKey { event: self.event }, ())
1066 }
1067
1068 fn from_key_value(key: Self::Key, _value: Self::Value) -> Self {
1069 Self { event: key.event }
1070 }
1071
1072 fn key(&self) -> Self::Key {
1073 AuditLogKey {
1074 event: self.event.clone(),
1075 }
1076 }
1077}
1078
1079#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
1080pub struct StorageCollectionMetadata {
1081 pub id: GlobalId,
1082 pub shard: ShardId,
1083}
1084
1085impl DurableType for StorageCollectionMetadata {
1086 type Key = StorageCollectionMetadataKey;
1087 type Value = StorageCollectionMetadataValue;
1088
1089 fn into_key_value(self) -> (Self::Key, Self::Value) {
1090 (
1091 StorageCollectionMetadataKey { id: self.id },
1092 StorageCollectionMetadataValue { shard: self.shard },
1093 )
1094 }
1095
1096 fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
1097 Self {
1098 id: key.id,
1099 shard: value.shard,
1100 }
1101 }
1102
1103 fn key(&self) -> Self::Key {
1104 StorageCollectionMetadataKey { id: self.id }
1105 }
1106}
1107
1108#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
1109pub struct UnfinalizedShard {
1110 pub shard: ShardId,
1111}
1112
1113impl DurableType for UnfinalizedShard {
1114 type Key = UnfinalizedShardKey;
1115 type Value = ();
1116
1117 fn into_key_value(self) -> (Self::Key, Self::Value) {
1118 (UnfinalizedShardKey { shard: self.shard }, ())
1119 }
1120
1121 fn from_key_value(key: Self::Key, _value: Self::Value) -> Self {
1122 Self { shard: key.shard }
1123 }
1124
1125 fn key(&self) -> Self::Key {
1126 UnfinalizedShardKey {
1127 shard: self.shard.clone(),
1128 }
1129 }
1130}
1131
1132#[derive(Debug, Clone, PartialEq, Eq, Default)]
1136pub struct Snapshot {
1137 pub databases: BTreeMap<proto::DatabaseKey, proto::DatabaseValue>,
1138 pub schemas: BTreeMap<proto::SchemaKey, proto::SchemaValue>,
1139 pub roles: BTreeMap<proto::RoleKey, proto::RoleValue>,
1140 pub role_auth: BTreeMap<proto::RoleAuthKey, proto::RoleAuthValue>,
1141 pub items: BTreeMap<proto::ItemKey, proto::ItemValue>,
1142 pub comments: BTreeMap<proto::CommentKey, proto::CommentValue>,
1143 pub clusters: BTreeMap<proto::ClusterKey, proto::ClusterValue>,
1144 pub network_policies: BTreeMap<proto::NetworkPolicyKey, proto::NetworkPolicyValue>,
1145 pub cluster_replicas: BTreeMap<proto::ClusterReplicaKey, proto::ClusterReplicaValue>,
1146 pub introspection_sources: BTreeMap<
1147 proto::ClusterIntrospectionSourceIndexKey,
1148 proto::ClusterIntrospectionSourceIndexValue,
1149 >,
1150 pub id_allocator: BTreeMap<proto::IdAllocKey, proto::IdAllocValue>,
1151 pub configs: BTreeMap<proto::ConfigKey, proto::ConfigValue>,
1152 pub settings: BTreeMap<proto::SettingKey, proto::SettingValue>,
1153 pub system_object_mappings: BTreeMap<proto::GidMappingKey, proto::GidMappingValue>,
1154 pub system_configurations:
1155 BTreeMap<proto::ServerConfigurationKey, proto::ServerConfigurationValue>,
1156 pub default_privileges: BTreeMap<proto::DefaultPrivilegesKey, proto::DefaultPrivilegesValue>,
1157 pub source_references: BTreeMap<proto::SourceReferencesKey, proto::SourceReferencesValue>,
1158 pub system_privileges: BTreeMap<proto::SystemPrivilegesKey, proto::SystemPrivilegesValue>,
1159 pub storage_collection_metadata:
1160 BTreeMap<proto::StorageCollectionMetadataKey, proto::StorageCollectionMetadataValue>,
1161 pub unfinalized_shards: BTreeMap<proto::UnfinalizedShardKey, ()>,
1162 pub txn_wal_shard: BTreeMap<(), proto::TxnWalShardValue>,
1163}
1164
1165impl Snapshot {
1166 pub fn empty() -> Snapshot {
1167 Snapshot::default()
1168 }
1169}
1170
1171#[derive(Debug, Clone, PartialEq, Eq, Hash, Arbitrary)]
1176pub struct FenceToken {
1177 pub(crate) deploy_generation: u64,
1178 pub(crate) epoch: Epoch,
1179}
1180
1181impl PartialOrd for FenceToken {
1182 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1183 Some(self.cmp(other))
1184 }
1185}
1186
1187impl Ord for FenceToken {
1188 fn cmp(&self, other: &Self) -> Ordering {
1189 self.deploy_generation
1190 .cmp(&other.deploy_generation)
1191 .then(self.epoch.cmp(&other.epoch))
1192 }
1193}
1194
1195#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1196pub struct SettingKey {
1197 pub(crate) name: String,
1198}
1199
1200#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1201pub struct SettingValue {
1202 pub(crate) value: String,
1203}
1204
1205#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1206pub struct IdAllocKey {
1207 pub(crate) name: String,
1208}
1209
1210#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1211pub struct IdAllocValue {
1212 pub(crate) next_id: u64,
1213}
1214
1215#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1216pub struct GidMappingKey {
1217 pub(crate) schema_name: String,
1218 pub(crate) object_type: CatalogItemType,
1219 pub(crate) object_name: String,
1220}
1221
1222#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1223pub struct GidMappingValue {
1224 pub(crate) catalog_id: SystemCatalogItemId,
1225 pub(crate) global_id: SystemGlobalId,
1226 pub(crate) fingerprint: String,
1227}
1228
1229#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1230pub struct ClusterKey {
1231 pub(crate) id: ClusterId,
1232}
1233
1234#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1235pub struct ClusterValue {
1236 pub(crate) name: String,
1237 pub(crate) owner_id: RoleId,
1238 pub(crate) privileges: Vec<MzAclItem>,
1239 pub(crate) config: ClusterConfig,
1240}
1241
1242#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1243pub struct ClusterIntrospectionSourceIndexKey {
1244 pub(crate) cluster_id: ClusterId,
1245 pub(crate) name: String,
1246}
1247
1248#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1249pub struct ClusterIntrospectionSourceIndexValue {
1250 pub(crate) catalog_id: IntrospectionSourceIndexCatalogItemId,
1251 pub(crate) global_id: IntrospectionSourceIndexGlobalId,
1252 pub(crate) oid: u32,
1253}
1254
1255#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1256pub struct ClusterReplicaKey {
1257 pub(crate) id: ReplicaId,
1258}
1259
1260#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1261pub struct ClusterReplicaValue {
1262 pub(crate) cluster_id: ClusterId,
1263 pub(crate) name: String,
1264 pub(crate) config: ReplicaConfig,
1265 pub(crate) owner_id: RoleId,
1266}
1267
1268#[derive(Clone, Copy, Debug, PartialOrd, PartialEq, Eq, Ord, Hash, Arbitrary)]
1269pub struct DatabaseKey {
1270 pub(crate) id: DatabaseId,
1271}
1272
1273#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Arbitrary)]
1274pub struct DatabaseValue {
1275 pub(crate) name: String,
1276 pub(crate) owner_id: RoleId,
1277 pub(crate) privileges: Vec<MzAclItem>,
1278 pub(crate) oid: u32,
1279}
1280
1281#[derive(Clone, Copy, Debug, PartialOrd, PartialEq, Eq, Ord, Hash, Arbitrary)]
1282pub struct SourceReferencesKey {
1283 pub(crate) source_id: CatalogItemId,
1284}
1285
1286#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Arbitrary)]
1287pub struct SourceReferencesValue {
1288 pub(crate) references: Vec<SourceReference>,
1289 pub(crate) updated_at: u64,
1290}
1291
1292#[derive(Clone, Copy, Debug, PartialOrd, PartialEq, Eq, Ord, Hash, Arbitrary)]
1293pub struct SchemaKey {
1294 pub(crate) id: SchemaId,
1295}
1296
1297#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Arbitrary)]
1298pub struct SchemaValue {
1299 pub(crate) database_id: Option<DatabaseId>,
1300 pub(crate) name: String,
1301 pub(crate) owner_id: RoleId,
1302 pub(crate) privileges: Vec<MzAclItem>,
1303 pub(crate) oid: u32,
1304}
1305
1306#[derive(Clone, PartialOrd, PartialEq, Eq, Ord, Hash, Debug, Arbitrary)]
1307pub struct ItemKey {
1308 pub(crate) id: CatalogItemId,
1309}
1310
1311#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Arbitrary)]
1312pub struct ItemValue {
1313 pub(crate) schema_id: SchemaId,
1314 pub(crate) name: String,
1315 pub(crate) create_sql: String,
1316 pub(crate) owner_id: RoleId,
1317 pub(crate) privileges: Vec<MzAclItem>,
1318 pub(crate) oid: u32,
1319 pub(crate) global_id: GlobalId,
1320 pub(crate) extra_versions: BTreeMap<RelationVersion, GlobalId>,
1321}
1322
1323impl ItemValue {
1324 pub fn item_type(&self) -> CatalogItemType {
1325 item_type(&self.create_sql)
1326 }
1327}
1328
1329fn item_type(create_sql: &str) -> CatalogItemType {
1330 let mut tokens = create_sql.split_whitespace();
1334 assert_eq!(tokens.next(), Some("CREATE"));
1335 match tokens.next() {
1336 Some("TABLE") => CatalogItemType::Table,
1337 Some("SOURCE") | Some("SUBSOURCE") => CatalogItemType::Source,
1338 Some("SINK") => CatalogItemType::Sink,
1339 Some("VIEW") => CatalogItemType::View,
1340 Some("MATERIALIZED") => {
1341 assert_eq!(tokens.next(), Some("VIEW"));
1342 CatalogItemType::MaterializedView
1343 }
1344 Some("CONTINUAL") => {
1345 assert_eq!(tokens.next(), Some("TASK"));
1346 CatalogItemType::ContinualTask
1347 }
1348 Some("INDEX") => CatalogItemType::Index,
1349 Some("TYPE") => CatalogItemType::Type,
1350 Some("FUNCTION") => CatalogItemType::Func,
1351 Some("SECRET") => CatalogItemType::Secret,
1352 Some("CONNECTION") => CatalogItemType::Connection,
1353 _ => panic!("unexpected create sql: {}", create_sql),
1354 }
1355}
1356
1357#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)]
1358pub struct CommentKey {
1359 pub(crate) object_id: CommentObjectId,
1360 pub(crate) sub_component: Option<usize>,
1361}
1362
1363#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Arbitrary)]
1364pub struct CommentValue {
1365 pub(crate) comment: String,
1366}
1367
1368#[derive(Clone, PartialOrd, PartialEq, Eq, Ord, Hash, Debug)]
1369pub struct RoleKey {
1370 pub(crate) id: RoleId,
1371}
1372
1373#[derive(Clone, PartialOrd, PartialEq, Eq, Ord, Debug)]
1374pub struct RoleValue {
1375 pub(crate) name: String,
1376 pub(crate) attributes: RoleAttributes,
1377 pub(crate) membership: RoleMembership,
1378 pub(crate) vars: RoleVars,
1379 pub(crate) oid: u32,
1380}
1381
1382#[derive(Clone, PartialOrd, PartialEq, Eq, Ord, Hash, Debug)]
1383pub struct NetworkPolicyKey {
1384 pub(crate) id: NetworkPolicyId,
1385}
1386
1387#[derive(Clone, PartialOrd, PartialEq, Eq, Ord, Debug)]
1388pub struct NetworkPolicyValue {
1389 pub(crate) name: String,
1390 pub(crate) rules: Vec<NetworkPolicyRule>,
1391 pub(crate) owner_id: RoleId,
1392 pub(crate) privileges: Vec<MzAclItem>,
1393 pub(crate) oid: u32,
1394}
1395
1396#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1397pub struct ConfigKey {
1398 pub(crate) key: String,
1399}
1400
1401#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1402pub struct ConfigValue {
1403 pub(crate) value: u64,
1404}
1405
1406#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1407pub struct AuditLogKey {
1408 pub(crate) event: VersionedEvent,
1409}
1410
1411#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1412pub struct StorageCollectionMetadataKey {
1413 pub(crate) id: GlobalId,
1414}
1415
1416#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1419pub struct StorageCollectionMetadataValue {
1420 pub(crate) shard: ShardId,
1421}
1422
1423#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1426pub struct UnfinalizedShardKey {
1427 pub(crate) shard: ShardId,
1428}
1429
1430#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1433pub struct TxnWalShardValue {
1434 pub(crate) shard: ShardId,
1435}
1436
1437#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1438pub struct ServerConfigurationKey {
1439 pub(crate) name: String,
1440}
1441
1442#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1443pub struct ServerConfigurationValue {
1444 pub(crate) value: String,
1445}
1446
1447#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1448pub struct DefaultPrivilegesKey {
1449 pub(crate) role_id: RoleId,
1450 pub(crate) database_id: Option<DatabaseId>,
1451 pub(crate) schema_id: Option<SchemaId>,
1452 pub(crate) object_type: ObjectType,
1453 pub(crate) grantee: RoleId,
1454}
1455
1456#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1457pub struct DefaultPrivilegesValue {
1458 pub(crate) privileges: AclMode,
1459}
1460
1461#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1462pub struct SystemPrivilegesKey {
1463 pub(crate) grantee: RoleId,
1464 pub(crate) grantor: RoleId,
1465}
1466
1467#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1468pub struct SystemPrivilegesValue {
1469 pub(crate) acl_mode: AclMode,
1470}
1471
1472#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1473pub struct RoleAuthKey {
1474 pub(crate) role_id: RoleId,
1478}
1479
1480#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
1481pub struct RoleAuthValue {
1482 pub(crate) password_hash: Option<String>,
1483 pub(crate) updated_at: u64,
1484}
1485
1486#[cfg(test)]
1487mod test {
1488 use mz_proto::{ProtoType, RustType};
1489 use proptest::prelude::*;
1490
1491 use super::{
1492 DatabaseKey, DatabaseValue, FenceToken, ItemKey, ItemValue, SchemaKey, SchemaValue,
1493 };
1494 use crate::durable::Epoch;
1495
1496 proptest! {
1497 #[mz_ore::test]
1498 #[cfg_attr(miri, ignore)] fn proptest_database_key_roundtrip(key: DatabaseKey) {
1500 let proto = key.into_proto();
1501 let round = proto.into_rust().expect("to roundtrip");
1502
1503 prop_assert_eq!(key, round);
1504 }
1505
1506 #[mz_ore::test]
1507 #[cfg_attr(miri, ignore)] fn proptest_database_value_roundtrip(value: DatabaseValue) {
1509 let proto = value.into_proto();
1510 let round = proto.into_rust().expect("to roundtrip");
1511
1512 prop_assert_eq!(value, round);
1513 }
1514
1515 #[mz_ore::test]
1516 #[cfg_attr(miri, ignore)] fn proptest_schema_key_roundtrip(key: SchemaKey) {
1518 let proto = key.into_proto();
1519 let round = proto.into_rust().expect("to roundtrip");
1520
1521 prop_assert_eq!(key, round);
1522 }
1523
1524 #[mz_ore::test]
1525 #[cfg_attr(miri, ignore)] fn proptest_schema_value_roundtrip(value: SchemaValue) {
1527 let proto = value.into_proto();
1528 let round = proto.into_rust().expect("to roundtrip");
1529
1530 prop_assert_eq!(value, round);
1531 }
1532
1533 #[mz_ore::test]
1534 #[cfg_attr(miri, ignore)] fn proptest_item_key_roundtrip(key: ItemKey) {
1536 let proto = key.into_proto();
1537 let round = proto.into_rust().expect("to roundtrip");
1538
1539 prop_assert_eq!(key, round);
1540 }
1541
1542 #[mz_ore::test]
1543 #[cfg_attr(miri, ignore)] fn proptest_item_value_roundtrip(value: ItemValue) {
1545 let proto = value.into_proto();
1546 let round = proto.into_rust().expect("to roundtrip");
1547
1548 prop_assert_eq!(value, round);
1549 }
1550 }
1551
1552 #[mz_ore::test]
1553 fn test_fence_token_order() {
1554 let ft1 = FenceToken {
1555 deploy_generation: 10,
1556 epoch: Epoch::new(20).expect("non-zero"),
1557 };
1558 let ft2 = FenceToken {
1559 deploy_generation: 10,
1560 epoch: Epoch::new(19).expect("non-zero"),
1561 };
1562
1563 assert!(ft1 > ft2);
1564
1565 let ft3 = FenceToken {
1566 deploy_generation: 11,
1567 epoch: Epoch::new(10).expect("non-zero"),
1568 };
1569
1570 assert!(ft3 > ft1);
1571
1572 let ft4 = FenceToken {
1573 deploy_generation: 11,
1574 epoch: Epoch::new(30).expect("non-zero"),
1575 };
1576
1577 assert!(ft4 > ft1);
1578 }
1579}