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 try_to_plan(&self) -> Result<CreateClusterPlan, PlanError>;
780}
781
782pub trait CatalogClusterReplica<'a>: Debug {
784 fn name(&self) -> &str;
786
787 fn cluster_id(&self) -> ClusterId;
789
790 fn replica_id(&self) -> ReplicaId;
792
793 fn owner_id(&self) -> RoleId;
795
796 fn internal(&self) -> bool;
798}
799
800pub trait CatalogItem {
805 fn name(&self) -> &QualifiedItemName;
807
808 fn id(&self) -> CatalogItemId;
810
811 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_>;
813
814 fn oid(&self) -> u32;
816
817 fn func(&self) -> Result<&'static Func, CatalogError>;
822
823 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, CatalogError>;
828
829 fn connection(&self) -> Result<Connection<ReferencedConnection>, CatalogError>;
833
834 fn item_type(&self) -> CatalogItemType;
836
837 fn create_sql(&self) -> &str;
840
841 fn references(&self) -> &ResolvedIds;
844
845 fn uses(&self) -> BTreeSet<CatalogItemId>;
848
849 fn referenced_by(&self) -> &[CatalogItemId];
851
852 fn used_by(&self) -> &[CatalogItemId];
854
855 fn subsource_details(
858 &self,
859 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)>;
860
861 fn source_export_details(
864 &self,
865 ) -> Option<(
866 CatalogItemId,
867 &UnresolvedItemName,
868 &SourceExportDetails,
869 &SourceExportDataConfig<ReferencedConnection>,
870 )>;
871
872 fn is_progress_source(&self) -> bool;
874
875 fn progress_id(&self) -> Option<CatalogItemId>;
877
878 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)>;
881
882 fn writable_table_details(&self) -> Option<&[Expr<Aug>]>;
885
886 fn replacement_target(&self) -> Option<CatalogItemId>;
888
889 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>>;
892
893 fn owner_id(&self) -> RoleId;
895
896 fn privileges(&self) -> &PrivilegeMap;
898
899 fn cluster_id(&self) -> Option<ClusterId>;
901
902 fn at_version(&self, version: RelationVersionSelector) -> Box<dyn CatalogCollectionItem>;
905
906 fn latest_version(&self) -> Option<RelationVersion>;
908}
909
910pub trait CatalogCollectionItem: CatalogItem + Send + Sync {
913 fn relation_desc(&self) -> Option<Cow<'_, RelationDesc>>;
918
919 fn global_id(&self) -> GlobalId;
921}
922
923#[derive(
925 Debug,
926 Deserialize,
927 Clone,
928 Copy,
929 Eq,
930 Hash,
931 Ord,
932 PartialEq,
933 PartialOrd,
934 Serialize
935)]
936pub enum CatalogItemType {
937 Table,
939 Source,
941 Sink,
943 View,
945 MaterializedView,
947 Index,
949 Type,
951 Func,
953 Secret,
955 Connection,
957}
958
959impl CatalogItemType {
960 pub fn conflicts_with_type(&self) -> bool {
979 match self {
980 CatalogItemType::Table => true,
981 CatalogItemType::Source => true,
982 CatalogItemType::View => true,
983 CatalogItemType::MaterializedView => true,
984 CatalogItemType::Index => true,
985 CatalogItemType::Type => true,
986 CatalogItemType::Sink => false,
987 CatalogItemType::Func => false,
988 CatalogItemType::Secret => false,
989 CatalogItemType::Connection => false,
990 }
991 }
992}
993
994impl fmt::Display for CatalogItemType {
995 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
996 match self {
997 CatalogItemType::Table => f.write_str("table"),
998 CatalogItemType::Source => f.write_str("source"),
999 CatalogItemType::Sink => f.write_str("sink"),
1000 CatalogItemType::View => f.write_str("view"),
1001 CatalogItemType::MaterializedView => f.write_str("materialized view"),
1002 CatalogItemType::Index => f.write_str("index"),
1003 CatalogItemType::Type => f.write_str("type"),
1004 CatalogItemType::Func => f.write_str("func"),
1005 CatalogItemType::Secret => f.write_str("secret"),
1006 CatalogItemType::Connection => f.write_str("connection"),
1007 }
1008 }
1009}
1010
1011impl From<CatalogItemType> for ObjectType {
1012 fn from(value: CatalogItemType) -> Self {
1013 match value {
1014 CatalogItemType::Table => ObjectType::Table,
1015 CatalogItemType::Source => ObjectType::Source,
1016 CatalogItemType::Sink => ObjectType::Sink,
1017 CatalogItemType::View => ObjectType::View,
1018 CatalogItemType::MaterializedView => ObjectType::MaterializedView,
1019 CatalogItemType::Index => ObjectType::Index,
1020 CatalogItemType::Type => ObjectType::Type,
1021 CatalogItemType::Func => ObjectType::Func,
1022 CatalogItemType::Secret => ObjectType::Secret,
1023 CatalogItemType::Connection => ObjectType::Connection,
1024 }
1025 }
1026}
1027
1028impl From<CatalogItemType> for mz_audit_log::ObjectType {
1029 fn from(value: CatalogItemType) -> Self {
1030 match value {
1031 CatalogItemType::Table => mz_audit_log::ObjectType::Table,
1032 CatalogItemType::Source => mz_audit_log::ObjectType::Source,
1033 CatalogItemType::View => mz_audit_log::ObjectType::View,
1034 CatalogItemType::MaterializedView => mz_audit_log::ObjectType::MaterializedView,
1035 CatalogItemType::Index => mz_audit_log::ObjectType::Index,
1036 CatalogItemType::Type => mz_audit_log::ObjectType::Type,
1037 CatalogItemType::Sink => mz_audit_log::ObjectType::Sink,
1038 CatalogItemType::Func => mz_audit_log::ObjectType::Func,
1039 CatalogItemType::Secret => mz_audit_log::ObjectType::Secret,
1040 CatalogItemType::Connection => mz_audit_log::ObjectType::Connection,
1041 }
1042 }
1043}
1044
1045#[derive(Clone, Debug, Eq, PartialEq)]
1047pub struct CatalogTypeDetails<T: TypeReference> {
1048 pub array_id: Option<CatalogItemId>,
1050 pub typ: CatalogType<T>,
1052 pub pg_metadata: Option<CatalogTypePgMetadata>,
1054}
1055
1056#[derive(Clone, Debug, Eq, PartialEq)]
1058pub struct CatalogTypePgMetadata {
1059 pub typinput_oid: u32,
1061 pub typreceive_oid: u32,
1063}
1064
1065pub trait TypeReference {
1067 type Reference: Clone + Debug + Eq + PartialEq;
1069}
1070
1071#[derive(Clone, Debug, Eq, PartialEq)]
1073pub struct NameReference;
1074
1075impl TypeReference for NameReference {
1076 type Reference = &'static str;
1077}
1078
1079#[derive(Clone, Debug, Eq, PartialEq)]
1081pub struct IdReference;
1082
1083impl TypeReference for IdReference {
1084 type Reference = CatalogItemId;
1085}
1086
1087#[allow(missing_docs)]
1093#[derive(Clone, Debug, Eq, PartialEq)]
1094pub enum CatalogType<T: TypeReference> {
1095 AclItem,
1096 Array {
1097 element_reference: T::Reference,
1098 },
1099 Bool,
1100 Bytes,
1101 Char,
1102 Date,
1103 Float32,
1104 Float64,
1105 Int16,
1106 Int32,
1107 Int64,
1108 UInt16,
1109 UInt32,
1110 UInt64,
1111 MzTimestamp,
1112 Interval,
1113 Jsonb,
1114 List {
1115 element_reference: T::Reference,
1116 element_modifiers: Vec<i64>,
1117 },
1118 Map {
1119 key_reference: T::Reference,
1120 key_modifiers: Vec<i64>,
1121 value_reference: T::Reference,
1122 value_modifiers: Vec<i64>,
1123 },
1124 Numeric,
1125 Oid,
1126 PgLegacyChar,
1127 PgLegacyName,
1128 Pseudo,
1129 Range {
1130 element_reference: T::Reference,
1131 },
1132 Record {
1133 fields: Vec<CatalogRecordField<T>>,
1134 },
1135 RegClass,
1136 RegProc,
1137 RegType,
1138 String,
1139 Time,
1140 Timestamp,
1141 TimestampTz,
1142 Uuid,
1143 VarChar,
1144 Int2Vector,
1145 MzAclItem,
1146}
1147
1148impl CatalogType<IdReference> {
1149 pub fn desc(&self, catalog: &dyn SessionCatalog) -> Result<Option<RelationDesc>, PlanError> {
1152 match &self {
1153 CatalogType::Record { fields } => {
1154 let mut desc = RelationDesc::builder();
1155 for f in fields {
1156 let name = f.name.clone();
1157 let ty = query::scalar_type_from_catalog(
1158 catalog,
1159 f.type_reference,
1160 &f.type_modifiers,
1161 )?;
1162 let ty = ty.nullable(true);
1165 desc = desc.with_column(name, ty);
1166 }
1167 Ok(Some(desc.finish()))
1168 }
1169 _ => Ok(None),
1170 }
1171 }
1172}
1173
1174#[derive(Clone, Debug, Eq, PartialEq)]
1176pub struct CatalogRecordField<T: TypeReference> {
1177 pub name: ColumnName,
1179 pub type_reference: T::Reference,
1181 pub type_modifiers: Vec<i64>,
1183}
1184
1185#[derive(Clone, Debug, Eq, PartialEq)]
1186pub enum TypeCategory {
1194 Array,
1196 BitString,
1198 Boolean,
1200 Composite,
1202 DateTime,
1204 Enum,
1206 Geometric,
1208 List,
1210 NetworkAddress,
1212 Numeric,
1214 Pseudo,
1216 Range,
1218 String,
1220 Timespan,
1222 UserDefined,
1224 Unknown,
1226}
1227
1228impl fmt::Display for TypeCategory {
1229 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1230 f.write_str(match self {
1231 TypeCategory::Array => "array",
1232 TypeCategory::BitString => "bit-string",
1233 TypeCategory::Boolean => "boolean",
1234 TypeCategory::Composite => "composite",
1235 TypeCategory::DateTime => "date-time",
1236 TypeCategory::Enum => "enum",
1237 TypeCategory::Geometric => "geometric",
1238 TypeCategory::List => "list",
1239 TypeCategory::NetworkAddress => "network-address",
1240 TypeCategory::Numeric => "numeric",
1241 TypeCategory::Pseudo => "pseudo",
1242 TypeCategory::Range => "range",
1243 TypeCategory::String => "string",
1244 TypeCategory::Timespan => "timespan",
1245 TypeCategory::UserDefined => "user-defined",
1246 TypeCategory::Unknown => "unknown",
1247 })
1248 }
1249}
1250
1251#[derive(Debug, Clone, PartialEq)]
1275pub struct EnvironmentId {
1276 cloud_provider: CloudProvider,
1277 cloud_provider_region: String,
1278 organization_id: Uuid,
1279 ordinal: u64,
1280}
1281
1282impl EnvironmentId {
1283 pub fn for_tests() -> EnvironmentId {
1285 EnvironmentId {
1286 cloud_provider: CloudProvider::Local,
1287 cloud_provider_region: "az1".into(),
1288 organization_id: Uuid::new_v4(),
1289 ordinal: 0,
1290 }
1291 }
1292
1293 pub fn cloud_provider(&self) -> &CloudProvider {
1295 &self.cloud_provider
1296 }
1297
1298 pub fn cloud_provider_region(&self) -> &str {
1300 &self.cloud_provider_region
1301 }
1302
1303 pub fn region(&self) -> String {
1308 format!("{}/{}", self.cloud_provider, self.cloud_provider_region)
1309 }
1310
1311 pub fn organization_id(&self) -> Uuid {
1313 self.organization_id
1314 }
1315
1316 pub fn ordinal(&self) -> u64 {
1318 self.ordinal
1319 }
1320}
1321
1322impl fmt::Display for EnvironmentId {
1328 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1329 write!(
1330 f,
1331 "{}-{}-{}-{}",
1332 self.cloud_provider, self.cloud_provider_region, self.organization_id, self.ordinal
1333 )
1334 }
1335}
1336
1337impl FromStr for EnvironmentId {
1338 type Err = InvalidEnvironmentIdError;
1339
1340 fn from_str(s: &str) -> Result<EnvironmentId, InvalidEnvironmentIdError> {
1341 static MATCHER: LazyLock<Regex> = LazyLock::new(|| {
1342 Regex::new(
1343 "^(?P<cloud_provider>[[:alnum:]]+)-\
1344 (?P<cloud_provider_region>[[:alnum:]\\-]+)-\
1345 (?P<organization_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-\
1346 (?P<ordinal>\\d{1,8})$"
1347 ).unwrap()
1348 });
1349 let captures = MATCHER.captures(s).ok_or(InvalidEnvironmentIdError)?;
1350 Ok(EnvironmentId {
1351 cloud_provider: CloudProvider::from_str(&captures["cloud_provider"])?,
1352 cloud_provider_region: captures["cloud_provider_region"].into(),
1353 organization_id: captures["organization_id"]
1354 .parse()
1355 .map_err(|_| InvalidEnvironmentIdError)?,
1356 ordinal: captures["ordinal"]
1357 .parse()
1358 .map_err(|_| InvalidEnvironmentIdError)?,
1359 })
1360 }
1361}
1362
1363#[derive(Debug, Clone, PartialEq)]
1365pub struct InvalidEnvironmentIdError;
1366
1367impl fmt::Display for InvalidEnvironmentIdError {
1368 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1369 f.write_str("invalid environment ID")
1370 }
1371}
1372
1373impl Error for InvalidEnvironmentIdError {}
1374
1375impl From<InvalidCloudProviderError> for InvalidEnvironmentIdError {
1376 fn from(_: InvalidCloudProviderError) -> Self {
1377 InvalidEnvironmentIdError
1378 }
1379}
1380
1381#[derive(Clone, Debug, Eq, PartialEq)]
1383pub enum CatalogError {
1384 UnknownDatabase(String),
1386 DatabaseAlreadyExists(String),
1388 UnknownSchema(String),
1390 SchemaAlreadyExists(String),
1392 UnknownRole(String),
1394 RoleAlreadyExists(String),
1396 NetworkPolicyAlreadyExists(String),
1398 UnknownCluster(String),
1400 UnexpectedBuiltinCluster(String),
1402 UnexpectedBuiltinClusterType(String),
1404 ClusterAlreadyExists(String),
1406 UnknownClusterReplica(String),
1408 UnknownClusterReplicaSize(String),
1410 DuplicateReplica(String, String),
1412 UnknownItem(String),
1414 ItemAlreadyExists(CatalogItemId, String),
1416 UnknownFunction {
1418 name: String,
1420 alternative: Option<String>,
1422 },
1423 UnknownType {
1425 name: String,
1427 },
1428 UnknownConnection(String),
1430 UnknownNetworkPolicy(String),
1432 UnexpectedType {
1434 name: String,
1436 actual_type: CatalogItemType,
1438 expected_type: CatalogItemType,
1440 },
1441 IdExhaustion,
1443 OidExhaustion,
1445 TimelineAlreadyExists(String),
1447 IdAllocatorAlreadyExists(String),
1449 ConfigAlreadyExists(String),
1451 FailedBuiltinSchemaMigration(String),
1453 StorageCollectionMetadataAlreadyExists(GlobalId),
1455}
1456
1457impl fmt::Display for CatalogError {
1458 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1459 match self {
1460 Self::UnknownDatabase(name) => write!(f, "unknown database '{}'", name),
1461 Self::DatabaseAlreadyExists(name) => write!(f, "database '{name}' already exists"),
1462 Self::UnknownFunction { name, .. } => write!(f, "function \"{}\" does not exist", name),
1463 Self::UnknownType { name, .. } => write!(f, "type \"{}\" does not exist", name),
1464 Self::UnknownConnection(name) => write!(f, "connection \"{}\" does not exist", name),
1465 Self::UnknownSchema(name) => write!(f, "unknown schema '{}'", name),
1466 Self::SchemaAlreadyExists(name) => write!(f, "schema '{name}' already exists"),
1467 Self::UnknownRole(name) => write!(f, "unknown role '{}'", name),
1468 Self::RoleAlreadyExists(name) => write!(f, "role '{name}' already exists"),
1469 Self::NetworkPolicyAlreadyExists(name) => {
1470 write!(f, "network policy '{name}' already exists")
1471 }
1472 Self::UnknownCluster(name) => write!(f, "unknown cluster '{}'", name),
1473 Self::UnknownNetworkPolicy(name) => write!(f, "unknown network policy '{}'", name),
1474 Self::UnexpectedBuiltinCluster(name) => {
1475 write!(f, "Unexpected builtin cluster '{}'", name)
1476 }
1477 Self::UnexpectedBuiltinClusterType(name) => {
1478 write!(f, "Unexpected builtin cluster type'{}'", name)
1479 }
1480 Self::ClusterAlreadyExists(name) => write!(f, "cluster '{name}' already exists"),
1481 Self::UnknownClusterReplica(name) => {
1482 write!(f, "unknown cluster replica '{}'", name)
1483 }
1484 Self::UnknownClusterReplicaSize(name) => {
1485 write!(f, "unknown cluster replica size '{}'", name)
1486 }
1487 Self::DuplicateReplica(replica_name, cluster_name) => write!(
1488 f,
1489 "cannot create multiple replicas named '{replica_name}' on cluster '{cluster_name}'"
1490 ),
1491 Self::UnknownItem(name) => write!(f, "unknown catalog item '{}'", name),
1492 Self::ItemAlreadyExists(_gid, name) => {
1493 write!(f, "catalog item '{name}' already exists")
1494 }
1495 Self::UnexpectedType {
1496 name,
1497 actual_type,
1498 expected_type,
1499 } => {
1500 write!(f, "\"{name}\" is a {actual_type} not a {expected_type}")
1501 }
1502 Self::IdExhaustion => write!(f, "id counter overflows i64"),
1503 Self::OidExhaustion => write!(f, "oid counter overflows u32"),
1504 Self::TimelineAlreadyExists(name) => write!(f, "timeline '{name}' already exists"),
1505 Self::IdAllocatorAlreadyExists(name) => {
1506 write!(f, "ID allocator '{name}' already exists")
1507 }
1508 Self::ConfigAlreadyExists(key) => write!(f, "config '{key}' already exists"),
1509 Self::FailedBuiltinSchemaMigration(objects) => {
1510 write!(f, "failed to migrate schema of builtin objects: {objects}")
1511 }
1512 Self::StorageCollectionMetadataAlreadyExists(key) => {
1513 write!(f, "storage metadata for '{key}' already exists")
1514 }
1515 }
1516 }
1517}
1518
1519impl CatalogError {
1520 pub fn hint(&self) -> Option<String> {
1522 match self {
1523 CatalogError::UnknownFunction { alternative, .. } => {
1524 match alternative {
1525 None => Some("No function matches the given name and argument types. You might need to add explicit type casts.".into()),
1526 Some(alt) => Some(format!("Try using {alt}")),
1527 }
1528 }
1529 _ => None,
1530 }
1531 }
1532}
1533
1534impl Error for CatalogError {}
1535
1536#[allow(missing_docs)]
1538#[derive(
1539 Debug,
1540 Clone,
1541 PartialOrd,
1542 Ord,
1543 PartialEq,
1544 Eq,
1545 Hash,
1546 Copy,
1547 Deserialize,
1548 Serialize
1549)]
1550pub enum ObjectType {
1552 Table,
1553 View,
1554 MaterializedView,
1555 Source,
1556 Sink,
1557 Index,
1558 Type,
1559 Role,
1560 Cluster,
1561 ClusterReplica,
1562 Secret,
1563 Connection,
1564 Database,
1565 Schema,
1566 Func,
1567 NetworkPolicy,
1568}
1569
1570impl ObjectType {
1571 pub fn is_relation(&self) -> bool {
1573 match self {
1574 ObjectType::Table
1575 | ObjectType::View
1576 | ObjectType::MaterializedView
1577 | ObjectType::Source => true,
1578 ObjectType::Sink
1579 | ObjectType::Index
1580 | ObjectType::Type
1581 | ObjectType::Secret
1582 | ObjectType::Connection
1583 | ObjectType::Func
1584 | ObjectType::Database
1585 | ObjectType::Schema
1586 | ObjectType::Cluster
1587 | ObjectType::ClusterReplica
1588 | ObjectType::Role
1589 | ObjectType::NetworkPolicy => false,
1590 }
1591 }
1592}
1593
1594impl From<mz_sql_parser::ast::ObjectType> for ObjectType {
1595 fn from(value: mz_sql_parser::ast::ObjectType) -> Self {
1596 match value {
1597 mz_sql_parser::ast::ObjectType::Table => ObjectType::Table,
1598 mz_sql_parser::ast::ObjectType::View => ObjectType::View,
1599 mz_sql_parser::ast::ObjectType::MaterializedView => ObjectType::MaterializedView,
1600 mz_sql_parser::ast::ObjectType::Source => ObjectType::Source,
1601 mz_sql_parser::ast::ObjectType::Subsource => ObjectType::Source,
1602 mz_sql_parser::ast::ObjectType::Sink => ObjectType::Sink,
1603 mz_sql_parser::ast::ObjectType::Index => ObjectType::Index,
1604 mz_sql_parser::ast::ObjectType::Type => ObjectType::Type,
1605 mz_sql_parser::ast::ObjectType::Role => ObjectType::Role,
1606 mz_sql_parser::ast::ObjectType::Cluster => ObjectType::Cluster,
1607 mz_sql_parser::ast::ObjectType::ClusterReplica => ObjectType::ClusterReplica,
1608 mz_sql_parser::ast::ObjectType::Secret => ObjectType::Secret,
1609 mz_sql_parser::ast::ObjectType::Connection => ObjectType::Connection,
1610 mz_sql_parser::ast::ObjectType::Database => ObjectType::Database,
1611 mz_sql_parser::ast::ObjectType::Schema => ObjectType::Schema,
1612 mz_sql_parser::ast::ObjectType::Func => ObjectType::Func,
1613 mz_sql_parser::ast::ObjectType::NetworkPolicy => ObjectType::NetworkPolicy,
1614 }
1615 }
1616}
1617
1618impl From<CommentObjectId> for ObjectType {
1619 fn from(value: CommentObjectId) -> ObjectType {
1620 match value {
1621 CommentObjectId::Table(_) => ObjectType::Table,
1622 CommentObjectId::View(_) => ObjectType::View,
1623 CommentObjectId::MaterializedView(_) => ObjectType::MaterializedView,
1624 CommentObjectId::Source(_) => ObjectType::Source,
1625 CommentObjectId::Sink(_) => ObjectType::Sink,
1626 CommentObjectId::Index(_) => ObjectType::Index,
1627 CommentObjectId::Func(_) => ObjectType::Func,
1628 CommentObjectId::Connection(_) => ObjectType::Connection,
1629 CommentObjectId::Type(_) => ObjectType::Type,
1630 CommentObjectId::Secret(_) => ObjectType::Secret,
1631 CommentObjectId::Role(_) => ObjectType::Role,
1632 CommentObjectId::Database(_) => ObjectType::Database,
1633 CommentObjectId::Schema(_) => ObjectType::Schema,
1634 CommentObjectId::Cluster(_) => ObjectType::Cluster,
1635 CommentObjectId::ClusterReplica(_) => ObjectType::ClusterReplica,
1636 CommentObjectId::NetworkPolicy(_) => ObjectType::NetworkPolicy,
1637 }
1638 }
1639}
1640
1641impl Display for ObjectType {
1642 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1643 f.write_str(match self {
1644 ObjectType::Table => "TABLE",
1645 ObjectType::View => "VIEW",
1646 ObjectType::MaterializedView => "MATERIALIZED VIEW",
1647 ObjectType::Source => "SOURCE",
1648 ObjectType::Sink => "SINK",
1649 ObjectType::Index => "INDEX",
1650 ObjectType::Type => "TYPE",
1651 ObjectType::Role => "ROLE",
1652 ObjectType::Cluster => "CLUSTER",
1653 ObjectType::ClusterReplica => "CLUSTER REPLICA",
1654 ObjectType::Secret => "SECRET",
1655 ObjectType::Connection => "CONNECTION",
1656 ObjectType::Database => "DATABASE",
1657 ObjectType::Schema => "SCHEMA",
1658 ObjectType::Func => "FUNCTION",
1659 ObjectType::NetworkPolicy => "NETWORK POLICY",
1660 })
1661 }
1662}
1663
1664#[derive(
1665 Debug,
1666 Clone,
1667 PartialOrd,
1668 Ord,
1669 PartialEq,
1670 Eq,
1671 Hash,
1672 Copy,
1673 Deserialize,
1674 Serialize
1675)]
1676pub enum SystemObjectType {
1678 Object(ObjectType),
1680 System,
1682}
1683
1684impl SystemObjectType {
1685 pub fn is_relation(&self) -> bool {
1687 match self {
1688 SystemObjectType::Object(object_type) => object_type.is_relation(),
1689 SystemObjectType::System => false,
1690 }
1691 }
1692}
1693
1694impl Display for SystemObjectType {
1695 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1696 match self {
1697 SystemObjectType::Object(object_type) => std::fmt::Display::fmt(&object_type, f),
1698 SystemObjectType::System => f.write_str("SYSTEM"),
1699 }
1700 }
1701}
1702
1703#[derive(Debug, Clone, PartialEq, Eq)]
1705pub enum ErrorMessageObjectDescription {
1706 Object {
1708 object_type: ObjectType,
1710 object_name: Option<String>,
1712 },
1713 System,
1715}
1716
1717impl ErrorMessageObjectDescription {
1718 pub fn from_id(
1720 object_id: &ObjectId,
1721 catalog: &dyn SessionCatalog,
1722 ) -> ErrorMessageObjectDescription {
1723 let object_name = match object_id {
1724 ObjectId::Cluster(cluster_id) => catalog.get_cluster(*cluster_id).name().to_string(),
1725 ObjectId::ClusterReplica((cluster_id, replica_id)) => catalog
1726 .get_cluster_replica(*cluster_id, *replica_id)
1727 .name()
1728 .to_string(),
1729 ObjectId::Database(database_id) => catalog.get_database(database_id).name().to_string(),
1730 ObjectId::Schema((database_spec, schema_spec)) => {
1731 let name = catalog.get_schema(database_spec, schema_spec).name();
1732 catalog.resolve_full_schema_name(name).to_string()
1733 }
1734 ObjectId::Role(role_id) => catalog.get_role(role_id).name().to_string(),
1735 ObjectId::Item(id) => {
1736 let name = catalog.get_item(id).name();
1737 catalog.resolve_full_name(name).to_string()
1738 }
1739 ObjectId::NetworkPolicy(network_policy_id) => catalog
1740 .get_network_policy(network_policy_id)
1741 .name()
1742 .to_string(),
1743 };
1744 ErrorMessageObjectDescription::Object {
1745 object_type: catalog.get_object_type(object_id),
1746 object_name: Some(object_name),
1747 }
1748 }
1749
1750 pub fn from_sys_id(
1752 object_id: &SystemObjectId,
1753 catalog: &dyn SessionCatalog,
1754 ) -> ErrorMessageObjectDescription {
1755 match object_id {
1756 SystemObjectId::Object(object_id) => {
1757 ErrorMessageObjectDescription::from_id(object_id, catalog)
1758 }
1759 SystemObjectId::System => ErrorMessageObjectDescription::System,
1760 }
1761 }
1762
1763 pub fn from_object_type(object_type: SystemObjectType) -> ErrorMessageObjectDescription {
1765 match object_type {
1766 SystemObjectType::Object(object_type) => ErrorMessageObjectDescription::Object {
1767 object_type,
1768 object_name: None,
1769 },
1770 SystemObjectType::System => ErrorMessageObjectDescription::System,
1771 }
1772 }
1773}
1774
1775impl Display for ErrorMessageObjectDescription {
1776 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1777 match self {
1778 ErrorMessageObjectDescription::Object {
1779 object_type,
1780 object_name,
1781 } => {
1782 let object_name = object_name
1783 .as_ref()
1784 .map(|object_name| format!(" {}", object_name.quoted()))
1785 .unwrap_or_else(|| "".to_string());
1786 write!(f, "{object_type}{object_name}")
1787 }
1788 ErrorMessageObjectDescription::System => f.write_str("SYSTEM"),
1789 }
1790 }
1791}
1792
1793#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
1794#[serde(into = "BTreeMap<String, RoleId>")]
1797#[serde(try_from = "BTreeMap<String, RoleId>")]
1798pub struct RoleMembership {
1800 pub map: BTreeMap<RoleId, RoleId>,
1806}
1807
1808impl RoleMembership {
1809 pub fn new() -> RoleMembership {
1811 RoleMembership {
1812 map: BTreeMap::new(),
1813 }
1814 }
1815}
1816
1817impl From<RoleMembership> for BTreeMap<String, RoleId> {
1818 fn from(value: RoleMembership) -> Self {
1819 value
1820 .map
1821 .into_iter()
1822 .map(|(k, v)| (k.to_string(), v))
1823 .collect()
1824 }
1825}
1826
1827impl TryFrom<BTreeMap<String, RoleId>> for RoleMembership {
1828 type Error = anyhow::Error;
1829
1830 fn try_from(value: BTreeMap<String, RoleId>) -> Result<Self, Self::Error> {
1831 Ok(RoleMembership {
1832 map: value
1833 .into_iter()
1834 .map(|(k, v)| Ok((RoleId::from_str(&k)?, v)))
1835 .collect::<Result<_, anyhow::Error>>()?,
1836 })
1837 }
1838}
1839
1840#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1842pub struct DefaultPrivilegeObject {
1843 pub role_id: RoleId,
1845 pub database_id: Option<DatabaseId>,
1847 pub schema_id: Option<SchemaId>,
1849 pub object_type: ObjectType,
1851}
1852
1853impl DefaultPrivilegeObject {
1854 pub fn new(
1856 role_id: RoleId,
1857 database_id: Option<DatabaseId>,
1858 schema_id: Option<SchemaId>,
1859 object_type: ObjectType,
1860 ) -> DefaultPrivilegeObject {
1861 DefaultPrivilegeObject {
1862 role_id,
1863 database_id,
1864 schema_id,
1865 object_type,
1866 }
1867 }
1868}
1869
1870impl std::fmt::Display for DefaultPrivilegeObject {
1871 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1872 write!(f, "{self:?}")
1874 }
1875}
1876
1877#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1879pub struct DefaultPrivilegeAclItem {
1880 pub grantee: RoleId,
1882 pub acl_mode: AclMode,
1884}
1885
1886impl DefaultPrivilegeAclItem {
1887 pub fn new(grantee: RoleId, acl_mode: AclMode) -> DefaultPrivilegeAclItem {
1889 DefaultPrivilegeAclItem { grantee, acl_mode }
1890 }
1891
1892 pub fn mz_acl_item(self, grantor: RoleId) -> MzAclItem {
1894 MzAclItem {
1895 grantee: self.grantee,
1896 grantor,
1897 acl_mode: self.acl_mode,
1898 }
1899 }
1900}
1901
1902#[cfg(test)]
1903mod tests {
1904 use super::{CloudProvider, EnvironmentId, InvalidEnvironmentIdError};
1905
1906 #[mz_ore::test]
1907 fn test_environment_id() {
1908 for (input, expected) in [
1909 (
1910 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1911 Ok(EnvironmentId {
1912 cloud_provider: CloudProvider::Local,
1913 cloud_provider_region: "az1".into(),
1914 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1915 ordinal: 452,
1916 }),
1917 ),
1918 (
1919 "aws-us-east-1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1920 Ok(EnvironmentId {
1921 cloud_provider: CloudProvider::Aws,
1922 cloud_provider_region: "us-east-1".into(),
1923 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1924 ordinal: 0,
1925 }),
1926 ),
1927 (
1928 "gcp-us-central1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1929 Ok(EnvironmentId {
1930 cloud_provider: CloudProvider::Gcp,
1931 cloud_provider_region: "us-central1".into(),
1932 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1933 ordinal: 0,
1934 }),
1935 ),
1936 (
1937 "azure-australiaeast-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1938 Ok(EnvironmentId {
1939 cloud_provider: CloudProvider::Azure,
1940 cloud_provider_region: "australiaeast".into(),
1941 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1942 ordinal: 0,
1943 }),
1944 ),
1945 (
1946 "generic-moon-station-11-darkside-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1947 Ok(EnvironmentId {
1948 cloud_provider: CloudProvider::Generic,
1949 cloud_provider_region: "moon-station-11-darkside".into(),
1950 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1951 ordinal: 0,
1952 }),
1953 ),
1954 ("", Err(InvalidEnvironmentIdError)),
1955 (
1956 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-123456789",
1957 Err(InvalidEnvironmentIdError),
1958 ),
1959 (
1960 "local-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1961 Err(InvalidEnvironmentIdError),
1962 ),
1963 (
1964 "local-az1-1497a3b7-a455-4fc48752-b44a94b5f90a-452",
1965 Err(InvalidEnvironmentIdError),
1966 ),
1967 ] {
1968 let actual = input.parse();
1969 assert_eq!(expected, actual, "input = {}", input);
1970 if let Ok(actual) = actual {
1971 assert_eq!(input, actual.to_string(), "input = {}", input);
1972 }
1973 }
1974 }
1975}