1#![warn(missing_docs)]
11
12use std::borrow::Cow;
15use std::collections::{BTreeMap, BTreeSet};
16use std::error::Error;
17use std::fmt;
18use std::fmt::{Debug, Display, Formatter};
19use std::num::NonZeroU32;
20use std::str::FromStr;
21use std::sync::LazyLock;
22use std::time::Instant;
23
24use chrono::{DateTime, Utc};
25use mz_auth::password::Password;
26use mz_build_info::BuildInfo;
27use mz_cloud_provider::{CloudProvider, InvalidCloudProviderError};
28use mz_controller_types::{ClusterId, ReplicaId};
29use mz_expr::MirScalarExpr;
30use mz_ore::now::{EpochMillis, NowFn};
31use mz_ore::str::StrExt;
32use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem, PrivilegeMap};
33use mz_repr::explain::ExprHumanizer;
34use mz_repr::network_policy_id::NetworkPolicyId;
35use mz_repr::role_id::RoleId;
36use mz_repr::{
37 CatalogItemId, ColumnName, GlobalId, RelationDesc, RelationVersion, RelationVersionSelector,
38};
39use mz_sql_parser::ast::{Expr, QualifiedReplica, UnresolvedItemName};
40use mz_storage_types::connections::inline::{ConnectionResolver, ReferencedConnection};
41use mz_storage_types::connections::{Connection, ConnectionContext};
42use mz_storage_types::sources::{SourceDesc, SourceExportDataConfig, SourceExportDetails};
43use proptest_derive::Arbitrary;
44use regex::Regex;
45use serde::{Deserialize, Serialize};
46use uuid::Uuid;
47
48use crate::func::Func;
49use crate::names::{
50 Aug, CommentObjectId, DatabaseId, FullItemName, FullSchemaName, ObjectId, PartialItemName,
51 QualifiedItemName, QualifiedSchemaName, ResolvedDatabaseSpecifier, ResolvedIds, SchemaId,
52 SchemaSpecifier, SystemObjectId,
53};
54use crate::plan::statement::StatementDesc;
55use crate::plan::statement::ddl::PlannedRoleAttributes;
56use crate::plan::{ClusterSchedule, CreateClusterPlan, PlanError, PlanNotice, query};
57use crate::session::vars::{OwnedVarInput, SystemVars};
58
59pub trait SessionCatalog: fmt::Debug + ExprHumanizer + Send + Sync + ConnectionResolver {
88 fn active_role_id(&self) -> &RoleId;
90
91 fn active_database_name(&self) -> Option<&str> {
93 self.active_database()
94 .map(|id| self.get_database(id))
95 .map(|db| db.name())
96 }
97
98 fn active_database(&self) -> Option<&DatabaseId>;
100
101 fn active_cluster(&self) -> &str;
103
104 fn search_path(&self) -> &[(ResolvedDatabaseSpecifier, SchemaSpecifier)];
106
107 fn get_prepared_statement_desc(&self, name: &str) -> Option<&StatementDesc>;
110
111 fn get_portal_desc_unverified(&self, portal_name: &str) -> Option<&StatementDesc>;
115
116 fn resolve_database(&self, database_name: &str) -> Result<&dyn CatalogDatabase, CatalogError>;
121
122 fn get_database(&self, id: &DatabaseId) -> &dyn CatalogDatabase;
126
127 fn get_databases(&self) -> Vec<&dyn CatalogDatabase>;
129
130 fn resolve_schema(
135 &self,
136 database_name: Option<&str>,
137 schema_name: &str,
138 ) -> Result<&dyn CatalogSchema, CatalogError>;
139
140 fn resolve_schema_in_database(
145 &self,
146 database_spec: &ResolvedDatabaseSpecifier,
147 schema_name: &str,
148 ) -> Result<&dyn CatalogSchema, CatalogError>;
149
150 fn get_schema(
154 &self,
155 database_spec: &ResolvedDatabaseSpecifier,
156 schema_spec: &SchemaSpecifier,
157 ) -> &dyn CatalogSchema;
158
159 fn get_schemas(&self) -> Vec<&dyn CatalogSchema>;
161
162 fn get_mz_internal_schema_id(&self) -> SchemaId;
164
165 fn get_mz_unsafe_schema_id(&self) -> SchemaId;
167
168 fn is_system_schema_specifier(&self, schema: SchemaSpecifier) -> bool;
170
171 fn resolve_role(&self, role_name: &str) -> Result<&dyn CatalogRole, CatalogError>;
173
174 fn resolve_network_policy(
176 &self,
177 network_policy_name: &str,
178 ) -> Result<&dyn CatalogNetworkPolicy, CatalogError>;
179
180 fn try_get_role(&self, id: &RoleId) -> Option<&dyn CatalogRole>;
182
183 fn get_role(&self, id: &RoleId) -> &dyn CatalogRole;
187
188 fn get_roles(&self) -> Vec<&dyn CatalogRole>;
190
191 fn mz_system_role_id(&self) -> RoleId;
193
194 fn collect_role_membership(&self, id: &RoleId) -> BTreeSet<RoleId>;
196
197 fn get_network_policy(&self, id: &NetworkPolicyId) -> &dyn CatalogNetworkPolicy;
202
203 fn get_network_policies(&self) -> Vec<&dyn CatalogNetworkPolicy>;
205
206 fn resolve_cluster<'a, 'b>(
209 &'a self,
210 cluster_name: Option<&'b str>,
211 ) -> Result<&'a dyn CatalogCluster<'a>, CatalogError>;
212
213 fn resolve_cluster_replica<'a, 'b>(
215 &'a self,
216 cluster_replica_name: &'b QualifiedReplica,
217 ) -> Result<&'a dyn CatalogClusterReplica<'a>, CatalogError>;
218
219 fn resolve_item(&self, item_name: &PartialItemName) -> Result<&dyn CatalogItem, CatalogError>;
234
235 fn resolve_function(
238 &self,
239 item_name: &PartialItemName,
240 ) -> Result<&dyn CatalogItem, CatalogError>;
241
242 fn resolve_type(&self, item_name: &PartialItemName) -> Result<&dyn CatalogItem, CatalogError>;
245
246 fn resolve_item_or_type(
248 &self,
249 name: &PartialItemName,
250 ) -> Result<&dyn CatalogItem, CatalogError> {
251 if let Ok(ty) = self.resolve_type(name) {
252 return Ok(ty);
253 }
254 self.resolve_item(name)
255 }
256
257 fn get_system_type(&self, name: &str) -> &dyn CatalogItem;
263
264 fn try_get_item(&self, id: &CatalogItemId) -> Option<&dyn CatalogItem>;
266
267 fn try_get_item_by_global_id<'a>(
272 &'a self,
273 id: &GlobalId,
274 ) -> Option<Box<dyn CatalogCollectionItem + 'a>>;
275
276 fn get_item(&self, id: &CatalogItemId) -> &dyn CatalogItem;
280
281 fn get_item_by_global_id<'a>(&'a self, id: &GlobalId) -> Box<dyn CatalogCollectionItem + 'a>;
287
288 fn get_items(&self) -> Vec<&dyn CatalogItem>;
290
291 fn get_item_by_name(&self, name: &QualifiedItemName) -> Option<&dyn CatalogItem>;
293
294 fn get_type_by_name(&self, name: &QualifiedItemName) -> Option<&dyn CatalogItem>;
296
297 fn get_cluster(&self, id: ClusterId) -> &dyn CatalogCluster<'_>;
299
300 fn get_clusters(&self) -> Vec<&dyn CatalogCluster<'_>>;
302
303 fn get_cluster_replica(
305 &self,
306 cluster_id: ClusterId,
307 replica_id: ReplicaId,
308 ) -> &dyn CatalogClusterReplica<'_>;
309
310 fn get_cluster_replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>>;
312
313 fn get_system_privileges(&self) -> &PrivilegeMap;
315
316 fn get_default_privileges(
318 &self,
319 ) -> Vec<(&DefaultPrivilegeObject, Vec<&DefaultPrivilegeAclItem>)>;
320
321 fn find_available_name(&self, name: QualifiedItemName) -> QualifiedItemName;
325
326 fn resolve_full_name(&self, name: &QualifiedItemName) -> FullItemName;
328
329 fn resolve_full_schema_name(&self, name: &QualifiedSchemaName) -> FullSchemaName;
332
333 fn resolve_item_id(&self, global_id: &GlobalId) -> CatalogItemId;
335
336 fn resolve_global_id(
338 &self,
339 item_id: &CatalogItemId,
340 version: RelationVersionSelector,
341 ) -> GlobalId;
342
343 fn config(&self) -> &CatalogConfig;
345
346 fn now(&self) -> EpochMillis;
350
351 fn aws_privatelink_availability_zones(&self) -> Option<BTreeSet<String>>;
353
354 fn system_vars(&self) -> &SystemVars;
356
357 fn system_vars_mut(&mut self) -> &mut SystemVars;
364
365 fn get_owner_id(&self, id: &ObjectId) -> Option<RoleId>;
367
368 fn get_privileges(&self, id: &SystemObjectId) -> Option<&PrivilegeMap>;
370
371 fn object_dependents(&self, ids: &Vec<ObjectId>) -> Vec<ObjectId>;
377
378 fn item_dependents(&self, id: CatalogItemId) -> Vec<ObjectId>;
384
385 fn all_object_privileges(&self, object_type: SystemObjectType) -> AclMode;
387
388 fn get_object_type(&self, object_id: &ObjectId) -> ObjectType;
390
391 fn get_system_object_type(&self, id: &SystemObjectId) -> SystemObjectType;
393
394 fn minimal_qualification(&self, qualified_name: &QualifiedItemName) -> PartialItemName;
397
398 fn add_notice(&self, notice: PlanNotice);
401
402 fn get_item_comments(&self, id: &CatalogItemId) -> Option<&BTreeMap<Option<usize>, String>>;
404
405 fn is_cluster_size_cc(&self, size: &str) -> bool;
408}
409
410#[derive(Debug, Clone)]
412pub struct CatalogConfig {
413 pub start_time: DateTime<Utc>,
415 pub start_instant: Instant,
417 pub nonce: u64,
422 pub environment_id: EnvironmentId,
424 pub session_id: Uuid,
426 pub build_info: &'static BuildInfo,
428 pub now: NowFn,
431 pub connection_context: ConnectionContext,
433 pub helm_chart_version: Option<String>,
435}
436
437pub trait CatalogDatabase {
439 fn name(&self) -> &str;
441
442 fn id(&self) -> DatabaseId;
444
445 fn has_schemas(&self) -> bool;
447
448 fn schema_ids(&self) -> &BTreeMap<String, SchemaId>;
451
452 fn schemas(&self) -> Vec<&dyn CatalogSchema>;
454
455 fn owner_id(&self) -> RoleId;
457
458 fn privileges(&self) -> &PrivilegeMap;
460}
461
462pub trait CatalogSchema {
464 fn database(&self) -> &ResolvedDatabaseSpecifier;
466
467 fn name(&self) -> &QualifiedSchemaName;
469
470 fn id(&self) -> &SchemaSpecifier;
472
473 fn has_items(&self) -> bool;
475
476 fn item_ids(&self) -> Box<dyn Iterator<Item = CatalogItemId> + '_>;
478
479 fn owner_id(&self) -> RoleId;
481
482 fn privileges(&self) -> &PrivilegeMap;
484}
485
486#[derive(Debug, Clone, Eq, PartialEq, Arbitrary)]
488pub struct PasswordConfig {
489 pub password: Password,
491 pub scram_iterations: NonZeroU32,
493}
494
495#[derive(Debug, Clone, Eq, PartialEq, Arbitrary)]
497pub enum PasswordAction {
498 Set(PasswordConfig),
500 Clear,
502 NoChange,
504}
505
506#[derive(
508 Debug,
509 Copy,
510 Clone,
511 Eq,
512 PartialEq,
513 Ord,
514 PartialOrd,
515 Serialize,
516 Deserialize,
517 Arbitrary
518)]
519pub enum AutoProvisionSource {
520 Oidc,
522 Frontegg,
524 None,
526}
527
528#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Arbitrary)]
533pub struct RoleAttributesRaw {
534 pub inherit: bool,
536 pub password: Option<Password>,
538 pub scram_iterations: Option<NonZeroU32>,
540 pub superuser: Option<bool>,
542 pub login: Option<bool>,
544 pub auto_provision_source: Option<AutoProvisionSource>,
546 _private: (),
548}
549
550#[derive(
552 Debug,
553 Clone,
554 Eq,
555 Serialize,
556 Deserialize,
557 PartialEq,
558 Ord,
559 PartialOrd,
560 Arbitrary
561)]
562pub struct RoleAttributes {
563 pub inherit: bool,
565 pub superuser: Option<bool>,
567 pub login: Option<bool>,
569 pub auto_provision_source: Option<AutoProvisionSource>,
571 _private: (),
573}
574
575impl RoleAttributesRaw {
576 pub const fn new() -> RoleAttributesRaw {
578 RoleAttributesRaw {
579 inherit: true,
580 password: None,
581 scram_iterations: None,
582 superuser: None,
583 login: None,
584 auto_provision_source: None,
585 _private: (),
586 }
587 }
588
589 pub const fn with_all(mut self) -> RoleAttributesRaw {
591 self.inherit = true;
592 self.superuser = Some(true);
593 self.login = Some(true);
594 self
595 }
596}
597
598impl RoleAttributes {
599 pub const fn new() -> RoleAttributes {
601 RoleAttributes {
602 inherit: true,
603 superuser: None,
604 login: None,
605 auto_provision_source: None,
606 _private: (),
607 }
608 }
609
610 pub const fn with_all(mut self) -> RoleAttributes {
612 self.inherit = true;
613 self.superuser = Some(true);
614 self.login = Some(true);
615 self
616 }
617
618 pub const fn is_inherit(&self) -> bool {
620 self.inherit
621 }
622}
623
624impl From<RoleAttributesRaw> for RoleAttributes {
625 fn from(
626 RoleAttributesRaw {
627 inherit,
628 superuser,
629 login,
630 auto_provision_source,
631 ..
632 }: RoleAttributesRaw,
633 ) -> RoleAttributes {
634 RoleAttributes {
635 inherit,
636 superuser,
637 login,
638 auto_provision_source,
639 _private: (),
640 }
641 }
642}
643
644impl From<RoleAttributes> for RoleAttributesRaw {
645 fn from(
646 RoleAttributes {
647 inherit,
648 superuser,
649 login,
650 auto_provision_source,
651 ..
652 }: RoleAttributes,
653 ) -> RoleAttributesRaw {
654 RoleAttributesRaw {
655 inherit,
656 password: None,
657 scram_iterations: None,
658 superuser,
659 login,
660 auto_provision_source,
661 _private: (),
662 }
663 }
664}
665
666impl From<PlannedRoleAttributes> for RoleAttributesRaw {
667 fn from(
668 PlannedRoleAttributes {
669 inherit,
670 password,
671 scram_iterations,
672 superuser,
673 login,
674 ..
675 }: PlannedRoleAttributes,
676 ) -> RoleAttributesRaw {
677 let default_attributes = RoleAttributesRaw::new();
678 RoleAttributesRaw {
679 inherit: inherit.unwrap_or(default_attributes.inherit),
680 password,
681 scram_iterations,
682 superuser,
683 login,
684 auto_provision_source: None,
685 _private: (),
686 }
687 }
688}
689
690#[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
692pub struct RoleVars {
693 pub map: BTreeMap<String, OwnedVarInput>,
695}
696
697pub trait CatalogRole {
699 fn name(&self) -> &str;
701
702 fn id(&self) -> RoleId;
704
705 fn membership(&self) -> &BTreeMap<RoleId, RoleId>;
710
711 fn attributes(&self) -> &RoleAttributes;
713
714 fn vars(&self) -> &BTreeMap<String, OwnedVarInput>;
716}
717
718pub trait CatalogNetworkPolicy {
720 fn name(&self) -> &str;
722
723 fn id(&self) -> NetworkPolicyId;
725
726 fn owner_id(&self) -> RoleId;
728
729 fn privileges(&self) -> &PrivilegeMap;
731}
732
733pub trait CatalogCluster<'a> {
735 fn name(&self) -> &str;
737
738 fn id(&self) -> ClusterId;
740
741 fn bound_objects(&self) -> &BTreeSet<CatalogItemId>;
743
744 fn replica_ids(&self) -> &BTreeMap<String, ReplicaId>;
747
748 fn replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>>;
750
751 fn replica(&self, id: ReplicaId) -> &dyn CatalogClusterReplica<'_>;
753
754 fn owner_id(&self) -> RoleId;
756
757 fn privileges(&self) -> &PrivilegeMap;
759
760 fn is_managed(&self) -> bool;
762
763 fn managed_size(&self) -> Option<&str>;
765
766 fn schedule(&self) -> Option<&ClusterSchedule>;
768
769 fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError>;
772}
773
774pub trait CatalogClusterReplica<'a>: Debug {
776 fn name(&self) -> &str;
778
779 fn cluster_id(&self) -> ClusterId;
781
782 fn replica_id(&self) -> ReplicaId;
784
785 fn owner_id(&self) -> RoleId;
787
788 fn internal(&self) -> bool;
790}
791
792pub trait CatalogItem {
797 fn name(&self) -> &QualifiedItemName;
799
800 fn id(&self) -> CatalogItemId;
802
803 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_>;
805
806 fn oid(&self) -> u32;
808
809 fn func(&self) -> Result<&'static Func, CatalogError>;
814
815 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, CatalogError>;
820
821 fn connection(&self) -> Result<Connection<ReferencedConnection>, CatalogError>;
825
826 fn item_type(&self) -> CatalogItemType;
828
829 fn create_sql(&self) -> &str;
832
833 fn references(&self) -> &ResolvedIds;
836
837 fn uses(&self) -> BTreeSet<CatalogItemId>;
840
841 fn referenced_by(&self) -> &[CatalogItemId];
843
844 fn used_by(&self) -> &[CatalogItemId];
846
847 fn subsource_details(
850 &self,
851 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)>;
852
853 fn source_export_details(
856 &self,
857 ) -> Option<(
858 CatalogItemId,
859 &UnresolvedItemName,
860 &SourceExportDetails,
861 &SourceExportDataConfig<ReferencedConnection>,
862 )>;
863
864 fn is_progress_source(&self) -> bool;
866
867 fn progress_id(&self) -> Option<CatalogItemId>;
869
870 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)>;
873
874 fn writable_table_details(&self) -> Option<&[Expr<Aug>]>;
877
878 fn replacement_target(&self) -> Option<CatalogItemId>;
880
881 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>>;
884
885 fn owner_id(&self) -> RoleId;
887
888 fn privileges(&self) -> &PrivilegeMap;
890
891 fn cluster_id(&self) -> Option<ClusterId>;
893
894 fn at_version(&self, version: RelationVersionSelector) -> Box<dyn CatalogCollectionItem>;
897
898 fn latest_version(&self) -> Option<RelationVersion>;
900}
901
902pub trait CatalogCollectionItem: CatalogItem + Send + Sync {
905 fn relation_desc(&self) -> Option<Cow<'_, RelationDesc>>;
910
911 fn global_id(&self) -> GlobalId;
913}
914
915#[derive(
917 Debug,
918 Deserialize,
919 Clone,
920 Copy,
921 Eq,
922 Hash,
923 Ord,
924 PartialEq,
925 PartialOrd,
926 Serialize
927)]
928pub enum CatalogItemType {
929 Table,
931 Source,
933 Sink,
935 View,
937 MaterializedView,
939 Index,
941 Type,
943 Func,
945 Secret,
947 Connection,
949}
950
951impl CatalogItemType {
952 pub fn conflicts_with_type(&self) -> bool {
971 match self {
972 CatalogItemType::Table => true,
973 CatalogItemType::Source => true,
974 CatalogItemType::View => true,
975 CatalogItemType::MaterializedView => true,
976 CatalogItemType::Index => true,
977 CatalogItemType::Type => true,
978 CatalogItemType::Sink => false,
979 CatalogItemType::Func => false,
980 CatalogItemType::Secret => false,
981 CatalogItemType::Connection => false,
982 }
983 }
984}
985
986impl fmt::Display for CatalogItemType {
987 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
988 match self {
989 CatalogItemType::Table => f.write_str("table"),
990 CatalogItemType::Source => f.write_str("source"),
991 CatalogItemType::Sink => f.write_str("sink"),
992 CatalogItemType::View => f.write_str("view"),
993 CatalogItemType::MaterializedView => f.write_str("materialized view"),
994 CatalogItemType::Index => f.write_str("index"),
995 CatalogItemType::Type => f.write_str("type"),
996 CatalogItemType::Func => f.write_str("func"),
997 CatalogItemType::Secret => f.write_str("secret"),
998 CatalogItemType::Connection => f.write_str("connection"),
999 }
1000 }
1001}
1002
1003impl From<CatalogItemType> for ObjectType {
1004 fn from(value: CatalogItemType) -> Self {
1005 match value {
1006 CatalogItemType::Table => ObjectType::Table,
1007 CatalogItemType::Source => ObjectType::Source,
1008 CatalogItemType::Sink => ObjectType::Sink,
1009 CatalogItemType::View => ObjectType::View,
1010 CatalogItemType::MaterializedView => ObjectType::MaterializedView,
1011 CatalogItemType::Index => ObjectType::Index,
1012 CatalogItemType::Type => ObjectType::Type,
1013 CatalogItemType::Func => ObjectType::Func,
1014 CatalogItemType::Secret => ObjectType::Secret,
1015 CatalogItemType::Connection => ObjectType::Connection,
1016 }
1017 }
1018}
1019
1020impl From<CatalogItemType> for mz_audit_log::ObjectType {
1021 fn from(value: CatalogItemType) -> Self {
1022 match value {
1023 CatalogItemType::Table => mz_audit_log::ObjectType::Table,
1024 CatalogItemType::Source => mz_audit_log::ObjectType::Source,
1025 CatalogItemType::View => mz_audit_log::ObjectType::View,
1026 CatalogItemType::MaterializedView => mz_audit_log::ObjectType::MaterializedView,
1027 CatalogItemType::Index => mz_audit_log::ObjectType::Index,
1028 CatalogItemType::Type => mz_audit_log::ObjectType::Type,
1029 CatalogItemType::Sink => mz_audit_log::ObjectType::Sink,
1030 CatalogItemType::Func => mz_audit_log::ObjectType::Func,
1031 CatalogItemType::Secret => mz_audit_log::ObjectType::Secret,
1032 CatalogItemType::Connection => mz_audit_log::ObjectType::Connection,
1033 }
1034 }
1035}
1036
1037#[derive(Clone, Debug, Eq, PartialEq)]
1039pub struct CatalogTypeDetails<T: TypeReference> {
1040 pub array_id: Option<CatalogItemId>,
1042 pub typ: CatalogType<T>,
1044 pub pg_metadata: Option<CatalogTypePgMetadata>,
1046}
1047
1048#[derive(Clone, Debug, Eq, PartialEq)]
1050pub struct CatalogTypePgMetadata {
1051 pub typinput_oid: u32,
1053 pub typreceive_oid: u32,
1055}
1056
1057pub trait TypeReference {
1059 type Reference: Clone + Debug + Eq + PartialEq;
1061}
1062
1063#[derive(Clone, Debug, Eq, PartialEq)]
1065pub struct NameReference;
1066
1067impl TypeReference for NameReference {
1068 type Reference = &'static str;
1069}
1070
1071#[derive(Clone, Debug, Eq, PartialEq)]
1073pub struct IdReference;
1074
1075impl TypeReference for IdReference {
1076 type Reference = CatalogItemId;
1077}
1078
1079#[allow(missing_docs)]
1085#[derive(Clone, Debug, Eq, PartialEq)]
1086pub enum CatalogType<T: TypeReference> {
1087 AclItem,
1088 Array {
1089 element_reference: T::Reference,
1090 },
1091 Bool,
1092 Bytes,
1093 Char,
1094 Date,
1095 Float32,
1096 Float64,
1097 Int16,
1098 Int32,
1099 Int64,
1100 UInt16,
1101 UInt32,
1102 UInt64,
1103 MzTimestamp,
1104 Interval,
1105 Jsonb,
1106 List {
1107 element_reference: T::Reference,
1108 element_modifiers: Vec<i64>,
1109 },
1110 Map {
1111 key_reference: T::Reference,
1112 key_modifiers: Vec<i64>,
1113 value_reference: T::Reference,
1114 value_modifiers: Vec<i64>,
1115 },
1116 Numeric,
1117 Oid,
1118 PgLegacyChar,
1119 PgLegacyName,
1120 Pseudo,
1121 Range {
1122 element_reference: T::Reference,
1123 },
1124 Record {
1125 fields: Vec<CatalogRecordField<T>>,
1126 },
1127 RegClass,
1128 RegProc,
1129 RegType,
1130 String,
1131 Time,
1132 Timestamp,
1133 TimestampTz,
1134 Uuid,
1135 VarChar,
1136 Int2Vector,
1137 MzAclItem,
1138}
1139
1140impl CatalogType<IdReference> {
1141 pub fn desc(&self, catalog: &dyn SessionCatalog) -> Result<Option<RelationDesc>, PlanError> {
1144 match &self {
1145 CatalogType::Record { fields } => {
1146 let mut desc = RelationDesc::builder();
1147 for f in fields {
1148 let name = f.name.clone();
1149 let ty = query::scalar_type_from_catalog(
1150 catalog,
1151 f.type_reference,
1152 &f.type_modifiers,
1153 )?;
1154 let ty = ty.nullable(true);
1157 desc = desc.with_column(name, ty);
1158 }
1159 Ok(Some(desc.finish()))
1160 }
1161 _ => Ok(None),
1162 }
1163 }
1164}
1165
1166#[derive(Clone, Debug, Eq, PartialEq)]
1168pub struct CatalogRecordField<T: TypeReference> {
1169 pub name: ColumnName,
1171 pub type_reference: T::Reference,
1173 pub type_modifiers: Vec<i64>,
1175}
1176
1177#[derive(Clone, Debug, Eq, PartialEq)]
1178pub enum TypeCategory {
1186 Array,
1188 BitString,
1190 Boolean,
1192 Composite,
1194 DateTime,
1196 Enum,
1198 Geometric,
1200 List,
1202 NetworkAddress,
1204 Numeric,
1206 Pseudo,
1208 Range,
1210 String,
1212 Timespan,
1214 UserDefined,
1216 Unknown,
1218}
1219
1220impl fmt::Display for TypeCategory {
1221 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1222 f.write_str(match self {
1223 TypeCategory::Array => "array",
1224 TypeCategory::BitString => "bit-string",
1225 TypeCategory::Boolean => "boolean",
1226 TypeCategory::Composite => "composite",
1227 TypeCategory::DateTime => "date-time",
1228 TypeCategory::Enum => "enum",
1229 TypeCategory::Geometric => "geometric",
1230 TypeCategory::List => "list",
1231 TypeCategory::NetworkAddress => "network-address",
1232 TypeCategory::Numeric => "numeric",
1233 TypeCategory::Pseudo => "pseudo",
1234 TypeCategory::Range => "range",
1235 TypeCategory::String => "string",
1236 TypeCategory::Timespan => "timespan",
1237 TypeCategory::UserDefined => "user-defined",
1238 TypeCategory::Unknown => "unknown",
1239 })
1240 }
1241}
1242
1243#[derive(Debug, Clone, PartialEq)]
1267pub struct EnvironmentId {
1268 cloud_provider: CloudProvider,
1269 cloud_provider_region: String,
1270 organization_id: Uuid,
1271 ordinal: u64,
1272}
1273
1274impl EnvironmentId {
1275 pub fn for_tests() -> EnvironmentId {
1277 EnvironmentId {
1278 cloud_provider: CloudProvider::Local,
1279 cloud_provider_region: "az1".into(),
1280 organization_id: Uuid::new_v4(),
1281 ordinal: 0,
1282 }
1283 }
1284
1285 pub fn cloud_provider(&self) -> &CloudProvider {
1287 &self.cloud_provider
1288 }
1289
1290 pub fn cloud_provider_region(&self) -> &str {
1292 &self.cloud_provider_region
1293 }
1294
1295 pub fn region(&self) -> String {
1300 format!("{}/{}", self.cloud_provider, self.cloud_provider_region)
1301 }
1302
1303 pub fn organization_id(&self) -> Uuid {
1305 self.organization_id
1306 }
1307
1308 pub fn ordinal(&self) -> u64 {
1310 self.ordinal
1311 }
1312}
1313
1314impl fmt::Display for EnvironmentId {
1320 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1321 write!(
1322 f,
1323 "{}-{}-{}-{}",
1324 self.cloud_provider, self.cloud_provider_region, self.organization_id, self.ordinal
1325 )
1326 }
1327}
1328
1329impl FromStr for EnvironmentId {
1330 type Err = InvalidEnvironmentIdError;
1331
1332 fn from_str(s: &str) -> Result<EnvironmentId, InvalidEnvironmentIdError> {
1333 static MATCHER: LazyLock<Regex> = LazyLock::new(|| {
1334 Regex::new(
1335 "^(?P<cloud_provider>[[:alnum:]]+)-\
1336 (?P<cloud_provider_region>[[:alnum:]\\-]+)-\
1337 (?P<organization_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-\
1338 (?P<ordinal>\\d{1,8})$"
1339 ).unwrap()
1340 });
1341 let captures = MATCHER.captures(s).ok_or(InvalidEnvironmentIdError)?;
1342 Ok(EnvironmentId {
1343 cloud_provider: CloudProvider::from_str(&captures["cloud_provider"])?,
1344 cloud_provider_region: captures["cloud_provider_region"].into(),
1345 organization_id: captures["organization_id"]
1346 .parse()
1347 .map_err(|_| InvalidEnvironmentIdError)?,
1348 ordinal: captures["ordinal"]
1349 .parse()
1350 .map_err(|_| InvalidEnvironmentIdError)?,
1351 })
1352 }
1353}
1354
1355#[derive(Debug, Clone, PartialEq)]
1357pub struct InvalidEnvironmentIdError;
1358
1359impl fmt::Display for InvalidEnvironmentIdError {
1360 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1361 f.write_str("invalid environment ID")
1362 }
1363}
1364
1365impl Error for InvalidEnvironmentIdError {}
1366
1367impl From<InvalidCloudProviderError> for InvalidEnvironmentIdError {
1368 fn from(_: InvalidCloudProviderError) -> Self {
1369 InvalidEnvironmentIdError
1370 }
1371}
1372
1373#[derive(Clone, Debug, Eq, PartialEq)]
1375pub enum CatalogError {
1376 UnknownDatabase(String),
1378 DatabaseAlreadyExists(String),
1380 UnknownSchema(String),
1382 SchemaAlreadyExists(String),
1384 UnknownRole(String),
1386 RoleAlreadyExists(String),
1388 NetworkPolicyAlreadyExists(String),
1390 UnknownCluster(String),
1392 UnexpectedBuiltinCluster(String),
1394 UnexpectedBuiltinClusterType(String),
1396 ClusterAlreadyExists(String),
1398 UnknownClusterReplica(String),
1400 UnknownClusterReplicaSize(String),
1402 DuplicateReplica(String, String),
1404 UnknownItem(String),
1406 ItemAlreadyExists(CatalogItemId, String),
1408 UnknownFunction {
1410 name: String,
1412 alternative: Option<String>,
1414 },
1415 UnknownType {
1417 name: String,
1419 },
1420 UnknownConnection(String),
1422 UnknownNetworkPolicy(String),
1424 UnexpectedType {
1426 name: String,
1428 actual_type: CatalogItemType,
1430 expected_type: CatalogItemType,
1432 },
1433 IdExhaustion,
1435 OidExhaustion,
1437 TimelineAlreadyExists(String),
1439 IdAllocatorAlreadyExists(String),
1441 ConfigAlreadyExists(String),
1443 FailedBuiltinSchemaMigration(String),
1445 StorageCollectionMetadataAlreadyExists(GlobalId),
1447}
1448
1449impl fmt::Display for CatalogError {
1450 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1451 match self {
1452 Self::UnknownDatabase(name) => write!(f, "unknown database '{}'", name),
1453 Self::DatabaseAlreadyExists(name) => write!(f, "database '{name}' already exists"),
1454 Self::UnknownFunction { name, .. } => write!(f, "function \"{}\" does not exist", name),
1455 Self::UnknownType { name, .. } => write!(f, "type \"{}\" does not exist", name),
1456 Self::UnknownConnection(name) => write!(f, "connection \"{}\" does not exist", name),
1457 Self::UnknownSchema(name) => write!(f, "unknown schema '{}'", name),
1458 Self::SchemaAlreadyExists(name) => write!(f, "schema '{name}' already exists"),
1459 Self::UnknownRole(name) => write!(f, "unknown role '{}'", name),
1460 Self::RoleAlreadyExists(name) => write!(f, "role '{name}' already exists"),
1461 Self::NetworkPolicyAlreadyExists(name) => {
1462 write!(f, "network policy '{name}' already exists")
1463 }
1464 Self::UnknownCluster(name) => write!(f, "unknown cluster '{}'", name),
1465 Self::UnknownNetworkPolicy(name) => write!(f, "unknown network policy '{}'", name),
1466 Self::UnexpectedBuiltinCluster(name) => {
1467 write!(f, "Unexpected builtin cluster '{}'", name)
1468 }
1469 Self::UnexpectedBuiltinClusterType(name) => {
1470 write!(f, "Unexpected builtin cluster type'{}'", name)
1471 }
1472 Self::ClusterAlreadyExists(name) => write!(f, "cluster '{name}' already exists"),
1473 Self::UnknownClusterReplica(name) => {
1474 write!(f, "unknown cluster replica '{}'", name)
1475 }
1476 Self::UnknownClusterReplicaSize(name) => {
1477 write!(f, "unknown cluster replica size '{}'", name)
1478 }
1479 Self::DuplicateReplica(replica_name, cluster_name) => write!(
1480 f,
1481 "cannot create multiple replicas named '{replica_name}' on cluster '{cluster_name}'"
1482 ),
1483 Self::UnknownItem(name) => write!(f, "unknown catalog item '{}'", name),
1484 Self::ItemAlreadyExists(_gid, name) => {
1485 write!(f, "catalog item '{name}' already exists")
1486 }
1487 Self::UnexpectedType {
1488 name,
1489 actual_type,
1490 expected_type,
1491 } => {
1492 write!(f, "\"{name}\" is a {actual_type} not a {expected_type}")
1493 }
1494 Self::IdExhaustion => write!(f, "id counter overflows i64"),
1495 Self::OidExhaustion => write!(f, "oid counter overflows u32"),
1496 Self::TimelineAlreadyExists(name) => write!(f, "timeline '{name}' already exists"),
1497 Self::IdAllocatorAlreadyExists(name) => {
1498 write!(f, "ID allocator '{name}' already exists")
1499 }
1500 Self::ConfigAlreadyExists(key) => write!(f, "config '{key}' already exists"),
1501 Self::FailedBuiltinSchemaMigration(objects) => {
1502 write!(f, "failed to migrate schema of builtin objects: {objects}")
1503 }
1504 Self::StorageCollectionMetadataAlreadyExists(key) => {
1505 write!(f, "storage metadata for '{key}' already exists")
1506 }
1507 }
1508 }
1509}
1510
1511impl CatalogError {
1512 pub fn hint(&self) -> Option<String> {
1514 match self {
1515 CatalogError::UnknownFunction { alternative, .. } => {
1516 match alternative {
1517 None => Some("No function matches the given name and argument types. You might need to add explicit type casts.".into()),
1518 Some(alt) => Some(format!("Try using {alt}")),
1519 }
1520 }
1521 _ => None,
1522 }
1523 }
1524}
1525
1526impl Error for CatalogError {}
1527
1528#[allow(missing_docs)]
1530#[derive(
1531 Debug,
1532 Clone,
1533 PartialOrd,
1534 Ord,
1535 PartialEq,
1536 Eq,
1537 Hash,
1538 Copy,
1539 Deserialize,
1540 Serialize
1541)]
1542pub enum ObjectType {
1544 Table,
1545 View,
1546 MaterializedView,
1547 Source,
1548 Sink,
1549 Index,
1550 Type,
1551 Role,
1552 Cluster,
1553 ClusterReplica,
1554 Secret,
1555 Connection,
1556 Database,
1557 Schema,
1558 Func,
1559 NetworkPolicy,
1560}
1561
1562impl ObjectType {
1563 pub fn is_relation(&self) -> bool {
1565 match self {
1566 ObjectType::Table
1567 | ObjectType::View
1568 | ObjectType::MaterializedView
1569 | ObjectType::Source => true,
1570 ObjectType::Sink
1571 | ObjectType::Index
1572 | ObjectType::Type
1573 | ObjectType::Secret
1574 | ObjectType::Connection
1575 | ObjectType::Func
1576 | ObjectType::Database
1577 | ObjectType::Schema
1578 | ObjectType::Cluster
1579 | ObjectType::ClusterReplica
1580 | ObjectType::Role
1581 | ObjectType::NetworkPolicy => false,
1582 }
1583 }
1584}
1585
1586impl From<mz_sql_parser::ast::ObjectType> for ObjectType {
1587 fn from(value: mz_sql_parser::ast::ObjectType) -> Self {
1588 match value {
1589 mz_sql_parser::ast::ObjectType::Table => ObjectType::Table,
1590 mz_sql_parser::ast::ObjectType::View => ObjectType::View,
1591 mz_sql_parser::ast::ObjectType::MaterializedView => ObjectType::MaterializedView,
1592 mz_sql_parser::ast::ObjectType::Source => ObjectType::Source,
1593 mz_sql_parser::ast::ObjectType::Subsource => ObjectType::Source,
1594 mz_sql_parser::ast::ObjectType::Sink => ObjectType::Sink,
1595 mz_sql_parser::ast::ObjectType::Index => ObjectType::Index,
1596 mz_sql_parser::ast::ObjectType::Type => ObjectType::Type,
1597 mz_sql_parser::ast::ObjectType::Role => ObjectType::Role,
1598 mz_sql_parser::ast::ObjectType::Cluster => ObjectType::Cluster,
1599 mz_sql_parser::ast::ObjectType::ClusterReplica => ObjectType::ClusterReplica,
1600 mz_sql_parser::ast::ObjectType::Secret => ObjectType::Secret,
1601 mz_sql_parser::ast::ObjectType::Connection => ObjectType::Connection,
1602 mz_sql_parser::ast::ObjectType::Database => ObjectType::Database,
1603 mz_sql_parser::ast::ObjectType::Schema => ObjectType::Schema,
1604 mz_sql_parser::ast::ObjectType::Func => ObjectType::Func,
1605 mz_sql_parser::ast::ObjectType::NetworkPolicy => ObjectType::NetworkPolicy,
1606 }
1607 }
1608}
1609
1610impl From<CommentObjectId> for ObjectType {
1611 fn from(value: CommentObjectId) -> ObjectType {
1612 match value {
1613 CommentObjectId::Table(_) => ObjectType::Table,
1614 CommentObjectId::View(_) => ObjectType::View,
1615 CommentObjectId::MaterializedView(_) => ObjectType::MaterializedView,
1616 CommentObjectId::Source(_) => ObjectType::Source,
1617 CommentObjectId::Sink(_) => ObjectType::Sink,
1618 CommentObjectId::Index(_) => ObjectType::Index,
1619 CommentObjectId::Func(_) => ObjectType::Func,
1620 CommentObjectId::Connection(_) => ObjectType::Connection,
1621 CommentObjectId::Type(_) => ObjectType::Type,
1622 CommentObjectId::Secret(_) => ObjectType::Secret,
1623 CommentObjectId::Role(_) => ObjectType::Role,
1624 CommentObjectId::Database(_) => ObjectType::Database,
1625 CommentObjectId::Schema(_) => ObjectType::Schema,
1626 CommentObjectId::Cluster(_) => ObjectType::Cluster,
1627 CommentObjectId::ClusterReplica(_) => ObjectType::ClusterReplica,
1628 CommentObjectId::NetworkPolicy(_) => ObjectType::NetworkPolicy,
1629 }
1630 }
1631}
1632
1633impl Display for ObjectType {
1634 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1635 f.write_str(match self {
1636 ObjectType::Table => "TABLE",
1637 ObjectType::View => "VIEW",
1638 ObjectType::MaterializedView => "MATERIALIZED VIEW",
1639 ObjectType::Source => "SOURCE",
1640 ObjectType::Sink => "SINK",
1641 ObjectType::Index => "INDEX",
1642 ObjectType::Type => "TYPE",
1643 ObjectType::Role => "ROLE",
1644 ObjectType::Cluster => "CLUSTER",
1645 ObjectType::ClusterReplica => "CLUSTER REPLICA",
1646 ObjectType::Secret => "SECRET",
1647 ObjectType::Connection => "CONNECTION",
1648 ObjectType::Database => "DATABASE",
1649 ObjectType::Schema => "SCHEMA",
1650 ObjectType::Func => "FUNCTION",
1651 ObjectType::NetworkPolicy => "NETWORK POLICY",
1652 })
1653 }
1654}
1655
1656#[derive(
1657 Debug,
1658 Clone,
1659 PartialOrd,
1660 Ord,
1661 PartialEq,
1662 Eq,
1663 Hash,
1664 Copy,
1665 Deserialize,
1666 Serialize
1667)]
1668pub enum SystemObjectType {
1670 Object(ObjectType),
1672 System,
1674}
1675
1676impl SystemObjectType {
1677 pub fn is_relation(&self) -> bool {
1679 match self {
1680 SystemObjectType::Object(object_type) => object_type.is_relation(),
1681 SystemObjectType::System => false,
1682 }
1683 }
1684}
1685
1686impl Display for SystemObjectType {
1687 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1688 match self {
1689 SystemObjectType::Object(object_type) => std::fmt::Display::fmt(&object_type, f),
1690 SystemObjectType::System => f.write_str("SYSTEM"),
1691 }
1692 }
1693}
1694
1695#[derive(Debug, Clone, PartialEq, Eq)]
1697pub enum ErrorMessageObjectDescription {
1698 Object {
1700 object_type: ObjectType,
1702 object_name: Option<String>,
1704 },
1705 System,
1707}
1708
1709impl ErrorMessageObjectDescription {
1710 pub fn from_id(
1712 object_id: &ObjectId,
1713 catalog: &dyn SessionCatalog,
1714 ) -> ErrorMessageObjectDescription {
1715 let object_name = match object_id {
1716 ObjectId::Cluster(cluster_id) => catalog.get_cluster(*cluster_id).name().to_string(),
1717 ObjectId::ClusterReplica((cluster_id, replica_id)) => catalog
1718 .get_cluster_replica(*cluster_id, *replica_id)
1719 .name()
1720 .to_string(),
1721 ObjectId::Database(database_id) => catalog.get_database(database_id).name().to_string(),
1722 ObjectId::Schema((database_spec, schema_spec)) => {
1723 let name = catalog.get_schema(database_spec, schema_spec).name();
1724 catalog.resolve_full_schema_name(name).to_string()
1725 }
1726 ObjectId::Role(role_id) => catalog.get_role(role_id).name().to_string(),
1727 ObjectId::Item(id) => {
1728 let name = catalog.get_item(id).name();
1729 catalog.resolve_full_name(name).to_string()
1730 }
1731 ObjectId::NetworkPolicy(network_policy_id) => catalog
1732 .get_network_policy(network_policy_id)
1733 .name()
1734 .to_string(),
1735 };
1736 ErrorMessageObjectDescription::Object {
1737 object_type: catalog.get_object_type(object_id),
1738 object_name: Some(object_name),
1739 }
1740 }
1741
1742 pub fn from_sys_id(
1744 object_id: &SystemObjectId,
1745 catalog: &dyn SessionCatalog,
1746 ) -> ErrorMessageObjectDescription {
1747 match object_id {
1748 SystemObjectId::Object(object_id) => {
1749 ErrorMessageObjectDescription::from_id(object_id, catalog)
1750 }
1751 SystemObjectId::System => ErrorMessageObjectDescription::System,
1752 }
1753 }
1754
1755 pub fn from_object_type(object_type: SystemObjectType) -> ErrorMessageObjectDescription {
1757 match object_type {
1758 SystemObjectType::Object(object_type) => ErrorMessageObjectDescription::Object {
1759 object_type,
1760 object_name: None,
1761 },
1762 SystemObjectType::System => ErrorMessageObjectDescription::System,
1763 }
1764 }
1765}
1766
1767impl Display for ErrorMessageObjectDescription {
1768 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1769 match self {
1770 ErrorMessageObjectDescription::Object {
1771 object_type,
1772 object_name,
1773 } => {
1774 let object_name = object_name
1775 .as_ref()
1776 .map(|object_name| format!(" {}", object_name.quoted()))
1777 .unwrap_or_else(|| "".to_string());
1778 write!(f, "{object_type}{object_name}")
1779 }
1780 ErrorMessageObjectDescription::System => f.write_str("SYSTEM"),
1781 }
1782 }
1783}
1784
1785#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
1786#[serde(into = "BTreeMap<String, RoleId>")]
1789#[serde(try_from = "BTreeMap<String, RoleId>")]
1790pub struct RoleMembership {
1792 pub map: BTreeMap<RoleId, RoleId>,
1798}
1799
1800impl RoleMembership {
1801 pub fn new() -> RoleMembership {
1803 RoleMembership {
1804 map: BTreeMap::new(),
1805 }
1806 }
1807}
1808
1809impl From<RoleMembership> for BTreeMap<String, RoleId> {
1810 fn from(value: RoleMembership) -> Self {
1811 value
1812 .map
1813 .into_iter()
1814 .map(|(k, v)| (k.to_string(), v))
1815 .collect()
1816 }
1817}
1818
1819impl TryFrom<BTreeMap<String, RoleId>> for RoleMembership {
1820 type Error = anyhow::Error;
1821
1822 fn try_from(value: BTreeMap<String, RoleId>) -> Result<Self, Self::Error> {
1823 Ok(RoleMembership {
1824 map: value
1825 .into_iter()
1826 .map(|(k, v)| Ok((RoleId::from_str(&k)?, v)))
1827 .collect::<Result<_, anyhow::Error>>()?,
1828 })
1829 }
1830}
1831
1832#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1834pub struct DefaultPrivilegeObject {
1835 pub role_id: RoleId,
1837 pub database_id: Option<DatabaseId>,
1839 pub schema_id: Option<SchemaId>,
1841 pub object_type: ObjectType,
1843}
1844
1845impl DefaultPrivilegeObject {
1846 pub fn new(
1848 role_id: RoleId,
1849 database_id: Option<DatabaseId>,
1850 schema_id: Option<SchemaId>,
1851 object_type: ObjectType,
1852 ) -> DefaultPrivilegeObject {
1853 DefaultPrivilegeObject {
1854 role_id,
1855 database_id,
1856 schema_id,
1857 object_type,
1858 }
1859 }
1860}
1861
1862impl std::fmt::Display for DefaultPrivilegeObject {
1863 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1864 write!(f, "{self:?}")
1866 }
1867}
1868
1869#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1871pub struct DefaultPrivilegeAclItem {
1872 pub grantee: RoleId,
1874 pub acl_mode: AclMode,
1876}
1877
1878impl DefaultPrivilegeAclItem {
1879 pub fn new(grantee: RoleId, acl_mode: AclMode) -> DefaultPrivilegeAclItem {
1881 DefaultPrivilegeAclItem { grantee, acl_mode }
1882 }
1883
1884 pub fn mz_acl_item(self, grantor: RoleId) -> MzAclItem {
1886 MzAclItem {
1887 grantee: self.grantee,
1888 grantor,
1889 acl_mode: self.acl_mode,
1890 }
1891 }
1892}
1893
1894#[cfg(test)]
1895mod tests {
1896 use super::{CloudProvider, EnvironmentId, InvalidEnvironmentIdError};
1897
1898 #[mz_ore::test]
1899 fn test_environment_id() {
1900 for (input, expected) in [
1901 (
1902 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1903 Ok(EnvironmentId {
1904 cloud_provider: CloudProvider::Local,
1905 cloud_provider_region: "az1".into(),
1906 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1907 ordinal: 452,
1908 }),
1909 ),
1910 (
1911 "aws-us-east-1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1912 Ok(EnvironmentId {
1913 cloud_provider: CloudProvider::Aws,
1914 cloud_provider_region: "us-east-1".into(),
1915 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1916 ordinal: 0,
1917 }),
1918 ),
1919 (
1920 "gcp-us-central1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1921 Ok(EnvironmentId {
1922 cloud_provider: CloudProvider::Gcp,
1923 cloud_provider_region: "us-central1".into(),
1924 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1925 ordinal: 0,
1926 }),
1927 ),
1928 (
1929 "azure-australiaeast-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1930 Ok(EnvironmentId {
1931 cloud_provider: CloudProvider::Azure,
1932 cloud_provider_region: "australiaeast".into(),
1933 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1934 ordinal: 0,
1935 }),
1936 ),
1937 (
1938 "generic-moon-station-11-darkside-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1939 Ok(EnvironmentId {
1940 cloud_provider: CloudProvider::Generic,
1941 cloud_provider_region: "moon-station-11-darkside".into(),
1942 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1943 ordinal: 0,
1944 }),
1945 ),
1946 ("", Err(InvalidEnvironmentIdError)),
1947 (
1948 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-123456789",
1949 Err(InvalidEnvironmentIdError),
1950 ),
1951 (
1952 "local-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1953 Err(InvalidEnvironmentIdError),
1954 ),
1955 (
1956 "local-az1-1497a3b7-a455-4fc48752-b44a94b5f90a-452",
1957 Err(InvalidEnvironmentIdError),
1958 ),
1959 ] {
1960 let actual = input.parse();
1961 assert_eq!(expected, actual, "input = {}", input);
1962 if let Ok(actual) = actual {
1963 assert_eq!(input, actual.to_string(), "input = {}", input);
1964 }
1965 }
1966 }
1967}