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