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 restrict_to_user_objects(&self) -> bool {
359 false
360 }
361
362 fn system_vars(&self) -> &SystemVars;
364
365 fn system_vars_mut(&mut self) -> &mut SystemVars;
372
373 fn get_owner_id(&self, id: &ObjectId) -> Option<RoleId>;
375
376 fn get_privileges(&self, id: &SystemObjectId) -> Option<&PrivilegeMap>;
378
379 fn object_dependents(&self, ids: &Vec<ObjectId>) -> Vec<ObjectId>;
385
386 fn item_dependents(&self, id: CatalogItemId) -> Vec<ObjectId>;
392
393 fn all_object_privileges(&self, object_type: SystemObjectType) -> AclMode;
395
396 fn get_object_type(&self, object_id: &ObjectId) -> ObjectType;
398
399 fn get_system_object_type(&self, id: &SystemObjectId) -> SystemObjectType;
401
402 fn minimal_qualification(&self, qualified_name: &QualifiedItemName) -> PartialItemName;
405
406 fn add_notice(&self, notice: PlanNotice);
409
410 fn get_item_comments(&self, id: &CatalogItemId) -> Option<&BTreeMap<Option<usize>, String>>;
412
413 fn is_cluster_size_cc(&self, size: &str) -> bool;
416}
417
418#[derive(Debug, Clone)]
420pub struct CatalogConfig {
421 pub start_time: DateTime<Utc>,
423 pub start_instant: Instant,
425 pub nonce: u64,
430 pub environment_id: EnvironmentId,
432 pub session_id: Uuid,
434 pub build_info: &'static BuildInfo,
436 pub now: NowFn,
439 pub connection_context: ConnectionContext,
441 pub helm_chart_version: Option<String>,
443}
444
445pub trait CatalogDatabase {
447 fn name(&self) -> &str;
449
450 fn id(&self) -> DatabaseId;
452
453 fn has_schemas(&self) -> bool;
455
456 fn schema_ids(&self) -> &BTreeMap<String, SchemaId>;
459
460 fn schemas(&self) -> Vec<&dyn CatalogSchema>;
462
463 fn owner_id(&self) -> RoleId;
465
466 fn privileges(&self) -> &PrivilegeMap;
468}
469
470pub trait CatalogSchema {
472 fn database(&self) -> &ResolvedDatabaseSpecifier;
474
475 fn name(&self) -> &QualifiedSchemaName;
477
478 fn id(&self) -> &SchemaSpecifier;
480
481 fn has_items(&self) -> bool;
483
484 fn item_ids(&self) -> Box<dyn Iterator<Item = CatalogItemId> + '_>;
486
487 fn owner_id(&self) -> RoleId;
489
490 fn privileges(&self) -> &PrivilegeMap;
492}
493
494#[derive(Debug, Clone, Eq, PartialEq, Arbitrary)]
496pub struct PasswordConfig {
497 pub password: Password,
499 pub scram_iterations: NonZeroU32,
501}
502
503#[derive(Debug, Clone, Eq, PartialEq, Arbitrary)]
505pub enum PasswordAction {
506 Set(PasswordConfig),
508 Clear,
510 NoChange,
512}
513
514#[derive(
516 Debug,
517 Copy,
518 Clone,
519 Eq,
520 PartialEq,
521 Ord,
522 PartialOrd,
523 Serialize,
524 Deserialize,
525 Arbitrary
526)]
527pub enum AutoProvisionSource {
528 Oidc,
530 Frontegg,
532 None,
534}
535
536#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Arbitrary)]
541pub struct RoleAttributesRaw {
542 pub inherit: bool,
544 pub password: Option<Password>,
546 pub scram_iterations: Option<NonZeroU32>,
548 pub superuser: Option<bool>,
550 pub login: Option<bool>,
552 pub auto_provision_source: Option<AutoProvisionSource>,
554 _private: (),
556}
557
558#[derive(
560 Debug,
561 Clone,
562 Eq,
563 Serialize,
564 Deserialize,
565 PartialEq,
566 Ord,
567 PartialOrd,
568 Arbitrary
569)]
570pub struct RoleAttributes {
571 pub inherit: bool,
573 pub superuser: Option<bool>,
575 pub login: Option<bool>,
577 pub auto_provision_source: Option<AutoProvisionSource>,
579 _private: (),
581}
582
583impl RoleAttributesRaw {
584 pub const fn new() -> RoleAttributesRaw {
586 RoleAttributesRaw {
587 inherit: true,
588 password: None,
589 scram_iterations: None,
590 superuser: None,
591 login: None,
592 auto_provision_source: None,
593 _private: (),
594 }
595 }
596
597 pub const fn with_all(mut self) -> RoleAttributesRaw {
599 self.inherit = true;
600 self.superuser = Some(true);
601 self.login = Some(true);
602 self
603 }
604}
605
606impl RoleAttributes {
607 pub const fn new() -> RoleAttributes {
609 RoleAttributes {
610 inherit: true,
611 superuser: None,
612 login: None,
613 auto_provision_source: None,
614 _private: (),
615 }
616 }
617
618 pub const fn with_all(mut self) -> RoleAttributes {
620 self.inherit = true;
621 self.superuser = Some(true);
622 self.login = Some(true);
623 self
624 }
625
626 pub const fn is_inherit(&self) -> bool {
628 self.inherit
629 }
630}
631
632impl From<RoleAttributesRaw> for RoleAttributes {
633 fn from(
634 RoleAttributesRaw {
635 inherit,
636 superuser,
637 login,
638 auto_provision_source,
639 ..
640 }: RoleAttributesRaw,
641 ) -> RoleAttributes {
642 RoleAttributes {
643 inherit,
644 superuser,
645 login,
646 auto_provision_source,
647 _private: (),
648 }
649 }
650}
651
652impl From<RoleAttributes> for RoleAttributesRaw {
653 fn from(
654 RoleAttributes {
655 inherit,
656 superuser,
657 login,
658 auto_provision_source,
659 ..
660 }: RoleAttributes,
661 ) -> RoleAttributesRaw {
662 RoleAttributesRaw {
663 inherit,
664 password: None,
665 scram_iterations: None,
666 superuser,
667 login,
668 auto_provision_source,
669 _private: (),
670 }
671 }
672}
673
674impl From<PlannedRoleAttributes> for RoleAttributesRaw {
675 fn from(
676 PlannedRoleAttributes {
677 inherit,
678 password,
679 scram_iterations,
680 superuser,
681 login,
682 ..
683 }: PlannedRoleAttributes,
684 ) -> RoleAttributesRaw {
685 let default_attributes = RoleAttributesRaw::new();
686 RoleAttributesRaw {
687 inherit: inherit.unwrap_or(default_attributes.inherit),
688 password,
689 scram_iterations,
690 superuser,
691 login,
692 auto_provision_source: None,
693 _private: (),
694 }
695 }
696}
697
698#[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
700pub struct RoleVars {
701 pub map: BTreeMap<String, OwnedVarInput>,
703}
704
705pub trait CatalogRole {
707 fn name(&self) -> &str;
709
710 fn id(&self) -> RoleId;
712
713 fn membership(&self) -> &BTreeMap<RoleId, RoleId>;
718
719 fn attributes(&self) -> &RoleAttributes;
721
722 fn vars(&self) -> &BTreeMap<String, OwnedVarInput>;
724}
725
726pub trait CatalogNetworkPolicy {
728 fn name(&self) -> &str;
730
731 fn id(&self) -> NetworkPolicyId;
733
734 fn owner_id(&self) -> RoleId;
736
737 fn privileges(&self) -> &PrivilegeMap;
739}
740
741pub trait CatalogCluster<'a> {
743 fn name(&self) -> &str;
745
746 fn id(&self) -> ClusterId;
748
749 fn bound_objects(&self) -> &BTreeSet<CatalogItemId>;
751
752 fn replica_ids(&self) -> &BTreeMap<String, ReplicaId>;
755
756 fn replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>>;
758
759 fn replica(&self, id: ReplicaId) -> &dyn CatalogClusterReplica<'_>;
761
762 fn owner_id(&self) -> RoleId;
764
765 fn privileges(&self) -> &PrivilegeMap;
767
768 fn is_managed(&self) -> bool;
770
771 fn managed_size(&self) -> Option<&str>;
773
774 fn schedule(&self) -> Option<&ClusterSchedule>;
776
777 fn replication_factor(&self) -> Option<u32>;
779
780 fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError>;
783}
784
785pub trait CatalogClusterReplica<'a>: Debug {
787 fn name(&self) -> &str;
789
790 fn cluster_id(&self) -> ClusterId;
792
793 fn replica_id(&self) -> ReplicaId;
795
796 fn owner_id(&self) -> RoleId;
798
799 fn internal(&self) -> bool;
801}
802
803pub trait CatalogItem {
808 fn name(&self) -> &QualifiedItemName;
810
811 fn id(&self) -> CatalogItemId;
813
814 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_>;
816
817 fn oid(&self) -> u32;
819
820 fn func(&self) -> Result<&'static Func, CatalogError>;
825
826 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, CatalogError>;
831
832 fn connection(&self) -> Result<Connection<ReferencedConnection>, CatalogError>;
836
837 fn item_type(&self) -> CatalogItemType;
839
840 fn create_sql(&self) -> &str;
843
844 fn references(&self) -> &ResolvedIds;
847
848 fn uses(&self) -> BTreeSet<CatalogItemId>;
851
852 fn referenced_by(&self) -> &[CatalogItemId];
854
855 fn used_by(&self) -> &[CatalogItemId];
857
858 fn subsource_details(
861 &self,
862 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)>;
863
864 fn source_export_details(
867 &self,
868 ) -> Option<(
869 CatalogItemId,
870 &UnresolvedItemName,
871 &SourceExportDetails,
872 &SourceExportDataConfig<ReferencedConnection>,
873 )>;
874
875 fn is_progress_source(&self) -> bool;
877
878 fn progress_id(&self) -> Option<CatalogItemId>;
880
881 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)>;
884
885 fn writable_table_details(&self) -> Option<&[Expr<Aug>]>;
888
889 fn replacement_target(&self) -> Option<CatalogItemId>;
891
892 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>>;
895
896 fn owner_id(&self) -> RoleId;
898
899 fn privileges(&self) -> &PrivilegeMap;
901
902 fn cluster_id(&self) -> Option<ClusterId>;
904
905 fn at_version(&self, version: RelationVersionSelector) -> Box<dyn CatalogCollectionItem>;
908
909 fn latest_version(&self) -> Option<RelationVersion>;
911}
912
913pub trait CatalogCollectionItem: CatalogItem + Send + Sync {
916 fn relation_desc(&self) -> Option<Cow<'_, RelationDesc>>;
921
922 fn global_id(&self) -> GlobalId;
924}
925
926#[derive(
928 Debug,
929 Deserialize,
930 Clone,
931 Copy,
932 Eq,
933 Hash,
934 Ord,
935 PartialEq,
936 PartialOrd,
937 Serialize
938)]
939pub enum CatalogItemType {
940 Table,
942 Source,
944 Sink,
946 View,
948 MaterializedView,
950 Index,
952 Type,
954 Func,
956 Secret,
958 Connection,
960}
961
962impl CatalogItemType {
963 pub fn conflicts_with_type(&self) -> bool {
982 match self {
983 CatalogItemType::Table => true,
984 CatalogItemType::Source => true,
985 CatalogItemType::View => true,
986 CatalogItemType::MaterializedView => true,
987 CatalogItemType::Index => true,
988 CatalogItemType::Type => true,
989 CatalogItemType::Sink => false,
990 CatalogItemType::Func => false,
991 CatalogItemType::Secret => false,
992 CatalogItemType::Connection => false,
993 }
994 }
995}
996
997impl fmt::Display for CatalogItemType {
998 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
999 match self {
1000 CatalogItemType::Table => f.write_str("table"),
1001 CatalogItemType::Source => f.write_str("source"),
1002 CatalogItemType::Sink => f.write_str("sink"),
1003 CatalogItemType::View => f.write_str("view"),
1004 CatalogItemType::MaterializedView => f.write_str("materialized view"),
1005 CatalogItemType::Index => f.write_str("index"),
1006 CatalogItemType::Type => f.write_str("type"),
1007 CatalogItemType::Func => f.write_str("func"),
1008 CatalogItemType::Secret => f.write_str("secret"),
1009 CatalogItemType::Connection => f.write_str("connection"),
1010 }
1011 }
1012}
1013
1014impl From<CatalogItemType> for ObjectType {
1015 fn from(value: CatalogItemType) -> Self {
1016 match value {
1017 CatalogItemType::Table => ObjectType::Table,
1018 CatalogItemType::Source => ObjectType::Source,
1019 CatalogItemType::Sink => ObjectType::Sink,
1020 CatalogItemType::View => ObjectType::View,
1021 CatalogItemType::MaterializedView => ObjectType::MaterializedView,
1022 CatalogItemType::Index => ObjectType::Index,
1023 CatalogItemType::Type => ObjectType::Type,
1024 CatalogItemType::Func => ObjectType::Func,
1025 CatalogItemType::Secret => ObjectType::Secret,
1026 CatalogItemType::Connection => ObjectType::Connection,
1027 }
1028 }
1029}
1030
1031impl From<CatalogItemType> for mz_audit_log::ObjectType {
1032 fn from(value: CatalogItemType) -> Self {
1033 match value {
1034 CatalogItemType::Table => mz_audit_log::ObjectType::Table,
1035 CatalogItemType::Source => mz_audit_log::ObjectType::Source,
1036 CatalogItemType::View => mz_audit_log::ObjectType::View,
1037 CatalogItemType::MaterializedView => mz_audit_log::ObjectType::MaterializedView,
1038 CatalogItemType::Index => mz_audit_log::ObjectType::Index,
1039 CatalogItemType::Type => mz_audit_log::ObjectType::Type,
1040 CatalogItemType::Sink => mz_audit_log::ObjectType::Sink,
1041 CatalogItemType::Func => mz_audit_log::ObjectType::Func,
1042 CatalogItemType::Secret => mz_audit_log::ObjectType::Secret,
1043 CatalogItemType::Connection => mz_audit_log::ObjectType::Connection,
1044 }
1045 }
1046}
1047
1048#[derive(Clone, Debug, Eq, PartialEq)]
1050pub struct CatalogTypeDetails<T: TypeReference> {
1051 pub array_id: Option<CatalogItemId>,
1053 pub typ: CatalogType<T>,
1055 pub pg_metadata: Option<CatalogTypePgMetadata>,
1057}
1058
1059#[derive(Clone, Debug, Eq, PartialEq)]
1061pub struct CatalogTypePgMetadata {
1062 pub typinput_oid: u32,
1064 pub typreceive_oid: u32,
1066}
1067
1068pub trait TypeReference {
1070 type Reference: Clone + Debug + Eq + PartialEq;
1072}
1073
1074#[derive(Clone, Debug, Eq, PartialEq)]
1076pub struct NameReference;
1077
1078impl TypeReference for NameReference {
1079 type Reference = &'static str;
1080}
1081
1082#[derive(Clone, Debug, Eq, PartialEq)]
1084pub struct IdReference;
1085
1086impl TypeReference for IdReference {
1087 type Reference = CatalogItemId;
1088}
1089
1090#[allow(missing_docs)]
1096#[derive(Clone, Debug, Eq, PartialEq)]
1097pub enum CatalogType<T: TypeReference> {
1098 AclItem,
1099 Array {
1100 element_reference: T::Reference,
1101 },
1102 Bool,
1103 Bytes,
1104 Char,
1105 Date,
1106 Float32,
1107 Float64,
1108 Int16,
1109 Int32,
1110 Int64,
1111 UInt16,
1112 UInt32,
1113 UInt64,
1114 MzTimestamp,
1115 Interval,
1116 Jsonb,
1117 List {
1118 element_reference: T::Reference,
1119 element_modifiers: Vec<i64>,
1120 },
1121 Map {
1122 key_reference: T::Reference,
1123 key_modifiers: Vec<i64>,
1124 value_reference: T::Reference,
1125 value_modifiers: Vec<i64>,
1126 },
1127 Numeric,
1128 Oid,
1129 PgLegacyChar,
1130 PgLegacyName,
1131 Pseudo,
1132 Range {
1133 element_reference: T::Reference,
1134 },
1135 Record {
1136 fields: Vec<CatalogRecordField<T>>,
1137 },
1138 RegClass,
1139 RegProc,
1140 RegType,
1141 String,
1142 Time,
1143 Timestamp,
1144 TimestampTz,
1145 Uuid,
1146 VarChar,
1147 Int2Vector,
1148 MzAclItem,
1149}
1150
1151impl CatalogType<IdReference> {
1152 pub fn desc(&self, catalog: &dyn SessionCatalog) -> Result<Option<RelationDesc>, PlanError> {
1155 match &self {
1156 CatalogType::Record { fields } => {
1157 let mut desc = RelationDesc::builder();
1158 let mut budget = query::TypeResolutionBudget::for_root(catalog);
1163 for f in fields {
1164 let name = f.name.clone();
1165 let ty = budget.resolve_child(catalog, f.type_reference, &f.type_modifiers)?;
1166 let ty = ty.nullable(true);
1169 desc = desc.with_column(name, ty);
1170 }
1171 Ok(Some(desc.finish()))
1172 }
1173 _ => Ok(None),
1174 }
1175 }
1176}
1177
1178#[derive(Clone, Debug, Eq, PartialEq)]
1180pub struct CatalogRecordField<T: TypeReference> {
1181 pub name: ColumnName,
1183 pub type_reference: T::Reference,
1185 pub type_modifiers: Vec<i64>,
1187}
1188
1189#[derive(Clone, Debug, Eq, PartialEq)]
1190pub enum TypeCategory {
1198 Array,
1200 BitString,
1202 Boolean,
1204 Composite,
1206 DateTime,
1208 Enum,
1210 Geometric,
1212 List,
1214 NetworkAddress,
1216 Numeric,
1218 Pseudo,
1220 Range,
1222 String,
1224 Timespan,
1226 UserDefined,
1228 Unknown,
1230}
1231
1232impl fmt::Display for TypeCategory {
1233 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1234 f.write_str(match self {
1235 TypeCategory::Array => "array",
1236 TypeCategory::BitString => "bit-string",
1237 TypeCategory::Boolean => "boolean",
1238 TypeCategory::Composite => "composite",
1239 TypeCategory::DateTime => "date-time",
1240 TypeCategory::Enum => "enum",
1241 TypeCategory::Geometric => "geometric",
1242 TypeCategory::List => "list",
1243 TypeCategory::NetworkAddress => "network-address",
1244 TypeCategory::Numeric => "numeric",
1245 TypeCategory::Pseudo => "pseudo",
1246 TypeCategory::Range => "range",
1247 TypeCategory::String => "string",
1248 TypeCategory::Timespan => "timespan",
1249 TypeCategory::UserDefined => "user-defined",
1250 TypeCategory::Unknown => "unknown",
1251 })
1252 }
1253}
1254
1255#[derive(Debug, Clone, PartialEq)]
1279pub struct EnvironmentId {
1280 cloud_provider: CloudProvider,
1281 cloud_provider_region: String,
1282 organization_id: Uuid,
1283 ordinal: u64,
1284}
1285
1286impl EnvironmentId {
1287 pub fn for_tests() -> EnvironmentId {
1289 EnvironmentId {
1290 cloud_provider: CloudProvider::Local,
1291 cloud_provider_region: "az1".into(),
1292 organization_id: Uuid::new_v4(),
1293 ordinal: 0,
1294 }
1295 }
1296
1297 pub fn cloud_provider(&self) -> &CloudProvider {
1299 &self.cloud_provider
1300 }
1301
1302 pub fn cloud_provider_region(&self) -> &str {
1304 &self.cloud_provider_region
1305 }
1306
1307 pub fn region(&self) -> String {
1312 format!("{}/{}", self.cloud_provider, self.cloud_provider_region)
1313 }
1314
1315 pub fn organization_id(&self) -> Uuid {
1317 self.organization_id
1318 }
1319
1320 pub fn ordinal(&self) -> u64 {
1322 self.ordinal
1323 }
1324}
1325
1326impl fmt::Display for EnvironmentId {
1332 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1333 write!(
1334 f,
1335 "{}-{}-{}-{}",
1336 self.cloud_provider, self.cloud_provider_region, self.organization_id, self.ordinal
1337 )
1338 }
1339}
1340
1341impl FromStr for EnvironmentId {
1342 type Err = InvalidEnvironmentIdError;
1343
1344 fn from_str(s: &str) -> Result<EnvironmentId, InvalidEnvironmentIdError> {
1345 static MATCHER: LazyLock<Regex> = LazyLock::new(|| {
1346 Regex::new(
1347 "^(?P<cloud_provider>[[:alnum:]]+)-\
1348 (?P<cloud_provider_region>[[:alnum:]\\-]+)-\
1349 (?P<organization_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-\
1350 (?P<ordinal>\\d{1,8})$"
1351 ).unwrap()
1352 });
1353 let captures = MATCHER.captures(s).ok_or(InvalidEnvironmentIdError)?;
1354 Ok(EnvironmentId {
1355 cloud_provider: CloudProvider::from_str(&captures["cloud_provider"])?,
1356 cloud_provider_region: captures["cloud_provider_region"].into(),
1357 organization_id: captures["organization_id"]
1358 .parse()
1359 .map_err(|_| InvalidEnvironmentIdError)?,
1360 ordinal: captures["ordinal"]
1361 .parse()
1362 .map_err(|_| InvalidEnvironmentIdError)?,
1363 })
1364 }
1365}
1366
1367#[derive(Debug, Clone, PartialEq)]
1369pub struct InvalidEnvironmentIdError;
1370
1371impl fmt::Display for InvalidEnvironmentIdError {
1372 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1373 f.write_str("invalid environment ID")
1374 }
1375}
1376
1377impl Error for InvalidEnvironmentIdError {}
1378
1379impl From<InvalidCloudProviderError> for InvalidEnvironmentIdError {
1380 fn from(_: InvalidCloudProviderError) -> Self {
1381 InvalidEnvironmentIdError
1382 }
1383}
1384
1385#[derive(Clone, Debug, Eq, PartialEq)]
1387pub enum CatalogError {
1388 UnknownDatabase(String),
1390 DatabaseAlreadyExists(String),
1392 UnknownSchema(String),
1394 SchemaAlreadyExists(String),
1396 UnknownRole(String),
1398 RoleAlreadyExists(String),
1400 NetworkPolicyAlreadyExists(String),
1402 UnknownCluster(String),
1404 UnexpectedBuiltinCluster(String),
1406 UnexpectedBuiltinClusterType(String),
1408 ClusterAlreadyExists(String),
1410 UnknownClusterReplica(String),
1412 UnknownClusterReplicaSize(String),
1414 DuplicateReplica(String, String),
1416 UnknownItem(String),
1418 ItemAlreadyExists(CatalogItemId, String),
1420 UnknownFunction {
1422 name: String,
1424 alternative: Option<String>,
1426 },
1427 UnknownType {
1429 name: String,
1431 },
1432 UnknownConnection(String),
1434 UnknownNetworkPolicy(String),
1436 UnexpectedType {
1438 name: String,
1440 actual_type: CatalogItemType,
1442 expected_type: CatalogItemType,
1444 },
1445 IdExhaustion,
1447 OidExhaustion,
1449 TimelineAlreadyExists(String),
1451 IdAllocatorAlreadyExists(String),
1453 ConfigAlreadyExists(String),
1455 FailedBuiltinSchemaMigration(String),
1457 StorageCollectionMetadataAlreadyExists(GlobalId),
1459}
1460
1461impl fmt::Display for CatalogError {
1462 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1463 match self {
1464 Self::UnknownDatabase(name) => write!(f, "unknown database '{}'", name),
1465 Self::DatabaseAlreadyExists(name) => write!(f, "database '{name}' already exists"),
1466 Self::UnknownFunction { name, .. } => write!(f, "function \"{}\" does not exist", name),
1467 Self::UnknownType { name, .. } => write!(f, "type \"{}\" does not exist", name),
1468 Self::UnknownConnection(name) => write!(f, "connection \"{}\" does not exist", name),
1469 Self::UnknownSchema(name) => write!(f, "unknown schema '{}'", name),
1470 Self::SchemaAlreadyExists(name) => write!(f, "schema '{name}' already exists"),
1471 Self::UnknownRole(name) => write!(f, "unknown role '{}'", name),
1472 Self::RoleAlreadyExists(name) => write!(f, "role '{name}' already exists"),
1473 Self::NetworkPolicyAlreadyExists(name) => {
1474 write!(f, "network policy '{name}' already exists")
1475 }
1476 Self::UnknownCluster(name) => write!(f, "unknown cluster '{}'", name),
1477 Self::UnknownNetworkPolicy(name) => write!(f, "unknown network policy '{}'", name),
1478 Self::UnexpectedBuiltinCluster(name) => {
1479 write!(f, "Unexpected builtin cluster '{}'", name)
1480 }
1481 Self::UnexpectedBuiltinClusterType(name) => {
1482 write!(f, "Unexpected builtin cluster type'{}'", name)
1483 }
1484 Self::ClusterAlreadyExists(name) => write!(f, "cluster '{name}' already exists"),
1485 Self::UnknownClusterReplica(name) => {
1486 write!(f, "unknown cluster replica '{}'", name)
1487 }
1488 Self::UnknownClusterReplicaSize(name) => {
1489 write!(f, "unknown cluster replica size '{}'", name)
1490 }
1491 Self::DuplicateReplica(replica_name, cluster_name) => write!(
1492 f,
1493 "cannot create multiple replicas named '{replica_name}' on cluster '{cluster_name}'"
1494 ),
1495 Self::UnknownItem(name) => write!(f, "unknown catalog item '{}'", name),
1496 Self::ItemAlreadyExists(_gid, name) => {
1497 write!(f, "catalog item '{name}' already exists")
1498 }
1499 Self::UnexpectedType {
1500 name,
1501 actual_type,
1502 expected_type,
1503 } => {
1504 write!(f, "\"{name}\" is a {actual_type} not a {expected_type}")
1505 }
1506 Self::IdExhaustion => write!(f, "id counter overflows i64"),
1507 Self::OidExhaustion => write!(f, "oid counter overflows u32"),
1508 Self::TimelineAlreadyExists(name) => write!(f, "timeline '{name}' already exists"),
1509 Self::IdAllocatorAlreadyExists(name) => {
1510 write!(f, "ID allocator '{name}' already exists")
1511 }
1512 Self::ConfigAlreadyExists(key) => write!(f, "config '{key}' already exists"),
1513 Self::FailedBuiltinSchemaMigration(objects) => {
1514 write!(f, "failed to migrate schema of builtin objects: {objects}")
1515 }
1516 Self::StorageCollectionMetadataAlreadyExists(key) => {
1517 write!(f, "storage metadata for '{key}' already exists")
1518 }
1519 }
1520 }
1521}
1522
1523impl CatalogError {
1524 pub fn hint(&self) -> Option<String> {
1526 match self {
1527 CatalogError::UnknownFunction { alternative, .. } => {
1528 match alternative {
1529 None => Some("No function matches the given name and argument types. You might need to add explicit type casts.".into()),
1530 Some(alt) => Some(format!("Try using {alt}")),
1531 }
1532 }
1533 _ => None,
1534 }
1535 }
1536}
1537
1538impl Error for CatalogError {}
1539
1540#[allow(missing_docs)]
1542#[derive(
1543 Debug,
1544 Clone,
1545 PartialOrd,
1546 Ord,
1547 PartialEq,
1548 Eq,
1549 Hash,
1550 Copy,
1551 Deserialize,
1552 Serialize
1553)]
1554pub enum ObjectType {
1556 Table,
1557 View,
1558 MaterializedView,
1559 Source,
1560 Sink,
1561 Index,
1562 Type,
1563 Role,
1564 Cluster,
1565 ClusterReplica,
1566 Secret,
1567 Connection,
1568 Database,
1569 Schema,
1570 Func,
1571 NetworkPolicy,
1572}
1573
1574impl ObjectType {
1575 pub fn is_relation(&self) -> bool {
1577 match self {
1578 ObjectType::Table
1579 | ObjectType::View
1580 | ObjectType::MaterializedView
1581 | ObjectType::Source => true,
1582 ObjectType::Sink
1583 | ObjectType::Index
1584 | ObjectType::Type
1585 | ObjectType::Secret
1586 | ObjectType::Connection
1587 | ObjectType::Func
1588 | ObjectType::Database
1589 | ObjectType::Schema
1590 | ObjectType::Cluster
1591 | ObjectType::ClusterReplica
1592 | ObjectType::Role
1593 | ObjectType::NetworkPolicy => false,
1594 }
1595 }
1596}
1597
1598impl From<mz_sql_parser::ast::ObjectType> for ObjectType {
1599 fn from(value: mz_sql_parser::ast::ObjectType) -> Self {
1600 match value {
1601 mz_sql_parser::ast::ObjectType::Table => ObjectType::Table,
1602 mz_sql_parser::ast::ObjectType::View => ObjectType::View,
1603 mz_sql_parser::ast::ObjectType::MaterializedView => ObjectType::MaterializedView,
1604 mz_sql_parser::ast::ObjectType::Source => ObjectType::Source,
1605 mz_sql_parser::ast::ObjectType::Subsource => ObjectType::Source,
1606 mz_sql_parser::ast::ObjectType::Sink => ObjectType::Sink,
1607 mz_sql_parser::ast::ObjectType::Index => ObjectType::Index,
1608 mz_sql_parser::ast::ObjectType::Type => ObjectType::Type,
1609 mz_sql_parser::ast::ObjectType::Role => ObjectType::Role,
1610 mz_sql_parser::ast::ObjectType::Cluster => ObjectType::Cluster,
1611 mz_sql_parser::ast::ObjectType::ClusterReplica => ObjectType::ClusterReplica,
1612 mz_sql_parser::ast::ObjectType::Secret => ObjectType::Secret,
1613 mz_sql_parser::ast::ObjectType::Connection => ObjectType::Connection,
1614 mz_sql_parser::ast::ObjectType::Database => ObjectType::Database,
1615 mz_sql_parser::ast::ObjectType::Schema => ObjectType::Schema,
1616 mz_sql_parser::ast::ObjectType::Func => ObjectType::Func,
1617 mz_sql_parser::ast::ObjectType::NetworkPolicy => ObjectType::NetworkPolicy,
1618 }
1619 }
1620}
1621
1622impl From<CommentObjectId> for ObjectType {
1623 fn from(value: CommentObjectId) -> ObjectType {
1624 match value {
1625 CommentObjectId::Table(_) => ObjectType::Table,
1626 CommentObjectId::View(_) => ObjectType::View,
1627 CommentObjectId::MaterializedView(_) => ObjectType::MaterializedView,
1628 CommentObjectId::Source(_) => ObjectType::Source,
1629 CommentObjectId::Sink(_) => ObjectType::Sink,
1630 CommentObjectId::Index(_) => ObjectType::Index,
1631 CommentObjectId::Func(_) => ObjectType::Func,
1632 CommentObjectId::Connection(_) => ObjectType::Connection,
1633 CommentObjectId::Type(_) => ObjectType::Type,
1634 CommentObjectId::Secret(_) => ObjectType::Secret,
1635 CommentObjectId::Role(_) => ObjectType::Role,
1636 CommentObjectId::Database(_) => ObjectType::Database,
1637 CommentObjectId::Schema(_) => ObjectType::Schema,
1638 CommentObjectId::Cluster(_) => ObjectType::Cluster,
1639 CommentObjectId::ClusterReplica(_) => ObjectType::ClusterReplica,
1640 CommentObjectId::NetworkPolicy(_) => ObjectType::NetworkPolicy,
1641 }
1642 }
1643}
1644
1645impl Display for ObjectType {
1646 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1647 f.write_str(match self {
1648 ObjectType::Table => "TABLE",
1649 ObjectType::View => "VIEW",
1650 ObjectType::MaterializedView => "MATERIALIZED VIEW",
1651 ObjectType::Source => "SOURCE",
1652 ObjectType::Sink => "SINK",
1653 ObjectType::Index => "INDEX",
1654 ObjectType::Type => "TYPE",
1655 ObjectType::Role => "ROLE",
1656 ObjectType::Cluster => "CLUSTER",
1657 ObjectType::ClusterReplica => "CLUSTER REPLICA",
1658 ObjectType::Secret => "SECRET",
1659 ObjectType::Connection => "CONNECTION",
1660 ObjectType::Database => "DATABASE",
1661 ObjectType::Schema => "SCHEMA",
1662 ObjectType::Func => "FUNCTION",
1663 ObjectType::NetworkPolicy => "NETWORK POLICY",
1664 })
1665 }
1666}
1667
1668#[derive(
1669 Debug,
1670 Clone,
1671 PartialOrd,
1672 Ord,
1673 PartialEq,
1674 Eq,
1675 Hash,
1676 Copy,
1677 Deserialize,
1678 Serialize
1679)]
1680pub enum SystemObjectType {
1682 Object(ObjectType),
1684 System,
1686}
1687
1688impl SystemObjectType {
1689 pub fn is_relation(&self) -> bool {
1691 match self {
1692 SystemObjectType::Object(object_type) => object_type.is_relation(),
1693 SystemObjectType::System => false,
1694 }
1695 }
1696}
1697
1698impl Display for SystemObjectType {
1699 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1700 match self {
1701 SystemObjectType::Object(object_type) => std::fmt::Display::fmt(&object_type, f),
1702 SystemObjectType::System => f.write_str("SYSTEM"),
1703 }
1704 }
1705}
1706
1707#[derive(Debug, Clone, PartialEq, Eq)]
1709pub enum ErrorMessageObjectDescription {
1710 Object {
1712 object_type: ObjectType,
1714 object_name: Option<String>,
1716 },
1717 System,
1719}
1720
1721impl ErrorMessageObjectDescription {
1722 pub fn from_id(
1724 object_id: &ObjectId,
1725 catalog: &dyn SessionCatalog,
1726 ) -> ErrorMessageObjectDescription {
1727 let object_name = match object_id {
1728 ObjectId::Cluster(cluster_id) => catalog.get_cluster(*cluster_id).name().to_string(),
1729 ObjectId::ClusterReplica((cluster_id, replica_id)) => catalog
1730 .get_cluster_replica(*cluster_id, *replica_id)
1731 .name()
1732 .to_string(),
1733 ObjectId::Database(database_id) => catalog.get_database(database_id).name().to_string(),
1734 ObjectId::Schema((database_spec, schema_spec)) => {
1735 let name = catalog.get_schema(database_spec, schema_spec).name();
1736 catalog.resolve_full_schema_name(name).to_string()
1737 }
1738 ObjectId::Role(role_id) => catalog.get_role(role_id).name().to_string(),
1739 ObjectId::Item(id) => {
1740 let name = catalog.get_item(id).name();
1741 catalog.resolve_full_name(name).to_string()
1742 }
1743 ObjectId::NetworkPolicy(network_policy_id) => catalog
1744 .get_network_policy(network_policy_id)
1745 .name()
1746 .to_string(),
1747 };
1748 ErrorMessageObjectDescription::Object {
1749 object_type: catalog.get_object_type(object_id),
1750 object_name: Some(object_name),
1751 }
1752 }
1753
1754 pub fn from_sys_id(
1756 object_id: &SystemObjectId,
1757 catalog: &dyn SessionCatalog,
1758 ) -> ErrorMessageObjectDescription {
1759 match object_id {
1760 SystemObjectId::Object(object_id) => {
1761 ErrorMessageObjectDescription::from_id(object_id, catalog)
1762 }
1763 SystemObjectId::System => ErrorMessageObjectDescription::System,
1764 }
1765 }
1766
1767 pub fn from_object_type(object_type: SystemObjectType) -> ErrorMessageObjectDescription {
1769 match object_type {
1770 SystemObjectType::Object(object_type) => ErrorMessageObjectDescription::Object {
1771 object_type,
1772 object_name: None,
1773 },
1774 SystemObjectType::System => ErrorMessageObjectDescription::System,
1775 }
1776 }
1777}
1778
1779impl Display for ErrorMessageObjectDescription {
1780 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1781 match self {
1782 ErrorMessageObjectDescription::Object {
1783 object_type,
1784 object_name,
1785 } => {
1786 let object_name = object_name
1787 .as_ref()
1788 .map(|object_name| format!(" {}", object_name.quoted()))
1789 .unwrap_or_else(|| "".to_string());
1790 write!(f, "{object_type}{object_name}")
1791 }
1792 ErrorMessageObjectDescription::System => f.write_str("SYSTEM"),
1793 }
1794 }
1795}
1796
1797#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
1798#[serde(into = "BTreeMap<String, RoleId>")]
1801#[serde(try_from = "BTreeMap<String, RoleId>")]
1802pub struct RoleMembership {
1804 pub map: BTreeMap<RoleId, RoleId>,
1810}
1811
1812impl RoleMembership {
1813 pub fn new() -> RoleMembership {
1815 RoleMembership {
1816 map: BTreeMap::new(),
1817 }
1818 }
1819}
1820
1821impl From<RoleMembership> for BTreeMap<String, RoleId> {
1822 fn from(value: RoleMembership) -> Self {
1823 value
1824 .map
1825 .into_iter()
1826 .map(|(k, v)| (k.to_string(), v))
1827 .collect()
1828 }
1829}
1830
1831impl TryFrom<BTreeMap<String, RoleId>> for RoleMembership {
1832 type Error = anyhow::Error;
1833
1834 fn try_from(value: BTreeMap<String, RoleId>) -> Result<Self, Self::Error> {
1835 Ok(RoleMembership {
1836 map: value
1837 .into_iter()
1838 .map(|(k, v)| Ok((RoleId::from_str(&k)?, v)))
1839 .collect::<Result<_, anyhow::Error>>()?,
1840 })
1841 }
1842}
1843
1844#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1846pub struct DefaultPrivilegeObject {
1847 pub role_id: RoleId,
1849 pub database_id: Option<DatabaseId>,
1851 pub schema_id: Option<SchemaId>,
1853 pub object_type: ObjectType,
1855}
1856
1857impl DefaultPrivilegeObject {
1858 pub fn new(
1860 role_id: RoleId,
1861 database_id: Option<DatabaseId>,
1862 schema_id: Option<SchemaId>,
1863 object_type: ObjectType,
1864 ) -> DefaultPrivilegeObject {
1865 DefaultPrivilegeObject {
1866 role_id,
1867 database_id,
1868 schema_id,
1869 object_type,
1870 }
1871 }
1872}
1873
1874impl std::fmt::Display for DefaultPrivilegeObject {
1875 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1876 write!(f, "{self:?}")
1878 }
1879}
1880
1881#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1883pub struct DefaultPrivilegeAclItem {
1884 pub grantee: RoleId,
1886 pub acl_mode: AclMode,
1888}
1889
1890impl DefaultPrivilegeAclItem {
1891 pub fn new(grantee: RoleId, acl_mode: AclMode) -> DefaultPrivilegeAclItem {
1893 DefaultPrivilegeAclItem { grantee, acl_mode }
1894 }
1895
1896 pub fn mz_acl_item(self, grantor: RoleId) -> MzAclItem {
1898 MzAclItem {
1899 grantee: self.grantee,
1900 grantor,
1901 acl_mode: self.acl_mode,
1902 }
1903 }
1904}
1905
1906#[cfg(test)]
1907mod tests {
1908 use super::{CloudProvider, EnvironmentId, InvalidEnvironmentIdError};
1909
1910 #[mz_ore::test]
1911 fn test_environment_id() {
1912 for (input, expected) in [
1913 (
1914 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1915 Ok(EnvironmentId {
1916 cloud_provider: CloudProvider::Local,
1917 cloud_provider_region: "az1".into(),
1918 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1919 ordinal: 452,
1920 }),
1921 ),
1922 (
1923 "aws-us-east-1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1924 Ok(EnvironmentId {
1925 cloud_provider: CloudProvider::Aws,
1926 cloud_provider_region: "us-east-1".into(),
1927 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1928 ordinal: 0,
1929 }),
1930 ),
1931 (
1932 "gcp-us-central1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1933 Ok(EnvironmentId {
1934 cloud_provider: CloudProvider::Gcp,
1935 cloud_provider_region: "us-central1".into(),
1936 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1937 ordinal: 0,
1938 }),
1939 ),
1940 (
1941 "azure-australiaeast-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1942 Ok(EnvironmentId {
1943 cloud_provider: CloudProvider::Azure,
1944 cloud_provider_region: "australiaeast".into(),
1945 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1946 ordinal: 0,
1947 }),
1948 ),
1949 (
1950 "generic-moon-station-11-darkside-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1951 Ok(EnvironmentId {
1952 cloud_provider: CloudProvider::Generic,
1953 cloud_provider_region: "moon-station-11-darkside".into(),
1954 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1955 ordinal: 0,
1956 }),
1957 ),
1958 ("", Err(InvalidEnvironmentIdError)),
1959 (
1960 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-123456789",
1961 Err(InvalidEnvironmentIdError),
1962 ),
1963 (
1964 "local-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1965 Err(InvalidEnvironmentIdError),
1966 ),
1967 (
1968 "local-az1-1497a3b7-a455-4fc48752-b44a94b5f90a-452",
1969 Err(InvalidEnvironmentIdError),
1970 ),
1971 ] {
1972 let actual = input.parse();
1973 assert_eq!(expected, actual, "input = {}", input);
1974 if let Ok(actual) = actual {
1975 assert_eq!(input, actual.to_string(), "input = {}", input);
1976 }
1977 }
1978 }
1979}