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::{Duration, 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 timestamp_interval: Duration,
430 pub now: NowFn,
433 pub connection_context: ConnectionContext,
435 pub builtins_cfg: BuiltinsConfig,
437 pub helm_chart_version: Option<String>,
439}
440
441pub trait CatalogDatabase {
443 fn name(&self) -> &str;
445
446 fn id(&self) -> DatabaseId;
448
449 fn has_schemas(&self) -> bool;
451
452 fn schema_ids(&self) -> &BTreeMap<String, SchemaId>;
455
456 fn schemas(&self) -> Vec<&dyn CatalogSchema>;
458
459 fn owner_id(&self) -> RoleId;
461
462 fn privileges(&self) -> &PrivilegeMap;
464}
465
466pub trait CatalogSchema {
468 fn database(&self) -> &ResolvedDatabaseSpecifier;
470
471 fn name(&self) -> &QualifiedSchemaName;
473
474 fn id(&self) -> &SchemaSpecifier;
476
477 fn has_items(&self) -> bool;
479
480 fn item_ids(&self) -> Box<dyn Iterator<Item = CatalogItemId> + '_>;
482
483 fn owner_id(&self) -> RoleId;
485
486 fn privileges(&self) -> &PrivilegeMap;
488}
489
490#[derive(Debug, Clone, Eq, PartialEq, Arbitrary)]
492pub struct PasswordConfig {
493 pub password: Password,
495 pub scram_iterations: NonZeroU32,
497}
498
499#[derive(Debug, Clone, Eq, PartialEq, Arbitrary)]
501pub enum PasswordAction {
502 Set(PasswordConfig),
504 Clear,
506 NoChange,
508}
509
510#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Arbitrary)]
515pub struct RoleAttributesRaw {
516 pub inherit: bool,
518 pub password: Option<Password>,
520 pub scram_iterations: Option<NonZeroU32>,
522 pub superuser: Option<bool>,
524 pub login: Option<bool>,
526 _private: (),
528}
529
530#[derive(
532 Debug,
533 Clone,
534 Eq,
535 Serialize,
536 Deserialize,
537 PartialEq,
538 Ord,
539 PartialOrd,
540 Arbitrary
541)]
542pub struct RoleAttributes {
543 pub inherit: bool,
545 pub superuser: Option<bool>,
547 pub login: Option<bool>,
549 _private: (),
551}
552
553impl RoleAttributesRaw {
554 pub const fn new() -> RoleAttributesRaw {
556 RoleAttributesRaw {
557 inherit: true,
558 password: None,
559 scram_iterations: None,
560 superuser: None,
561 login: None,
562 _private: (),
563 }
564 }
565
566 pub const fn with_all(mut self) -> RoleAttributesRaw {
568 self.inherit = true;
569 self.superuser = Some(true);
570 self.login = Some(true);
571 self
572 }
573}
574
575impl RoleAttributes {
576 pub const fn new() -> RoleAttributes {
578 RoleAttributes {
579 inherit: true,
580 superuser: None,
581 login: None,
582 _private: (),
583 }
584 }
585
586 pub const fn with_all(mut self) -> RoleAttributes {
588 self.inherit = true;
589 self.superuser = Some(true);
590 self.login = Some(true);
591 self
592 }
593
594 pub const fn is_inherit(&self) -> bool {
596 self.inherit
597 }
598}
599
600impl From<RoleAttributesRaw> for RoleAttributes {
601 fn from(
602 RoleAttributesRaw {
603 inherit,
604 superuser,
605 login,
606 ..
607 }: RoleAttributesRaw,
608 ) -> RoleAttributes {
609 RoleAttributes {
610 inherit,
611 superuser,
612 login,
613 _private: (),
614 }
615 }
616}
617
618impl From<RoleAttributes> for RoleAttributesRaw {
619 fn from(
620 RoleAttributes {
621 inherit,
622 superuser,
623 login,
624 ..
625 }: RoleAttributes,
626 ) -> RoleAttributesRaw {
627 RoleAttributesRaw {
628 inherit,
629 password: None,
630 scram_iterations: None,
631 superuser,
632 login,
633 _private: (),
634 }
635 }
636}
637
638impl From<PlannedRoleAttributes> for RoleAttributesRaw {
639 fn from(
640 PlannedRoleAttributes {
641 inherit,
642 password,
643 scram_iterations,
644 superuser,
645 login,
646 ..
647 }: PlannedRoleAttributes,
648 ) -> RoleAttributesRaw {
649 let default_attributes = RoleAttributesRaw::new();
650 RoleAttributesRaw {
651 inherit: inherit.unwrap_or(default_attributes.inherit),
652 password,
653 scram_iterations,
654 superuser,
655 login,
656 _private: (),
657 }
658 }
659}
660
661#[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
663pub struct RoleVars {
664 pub map: BTreeMap<String, OwnedVarInput>,
666}
667
668pub trait CatalogRole {
670 fn name(&self) -> &str;
672
673 fn id(&self) -> RoleId;
675
676 fn membership(&self) -> &BTreeMap<RoleId, RoleId>;
681
682 fn attributes(&self) -> &RoleAttributes;
684
685 fn vars(&self) -> &BTreeMap<String, OwnedVarInput>;
687}
688
689pub trait CatalogNetworkPolicy {
691 fn name(&self) -> &str;
693
694 fn id(&self) -> NetworkPolicyId;
696
697 fn owner_id(&self) -> RoleId;
699
700 fn privileges(&self) -> &PrivilegeMap;
702}
703
704pub trait CatalogCluster<'a> {
706 fn name(&self) -> &str;
708
709 fn id(&self) -> ClusterId;
711
712 fn bound_objects(&self) -> &BTreeSet<CatalogItemId>;
714
715 fn replica_ids(&self) -> &BTreeMap<String, ReplicaId>;
718
719 fn replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>>;
721
722 fn replica(&self, id: ReplicaId) -> &dyn CatalogClusterReplica<'_>;
724
725 fn owner_id(&self) -> RoleId;
727
728 fn privileges(&self) -> &PrivilegeMap;
730
731 fn is_managed(&self) -> bool;
733
734 fn managed_size(&self) -> Option<&str>;
736
737 fn schedule(&self) -> Option<&ClusterSchedule>;
739
740 fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError>;
743}
744
745pub trait CatalogClusterReplica<'a>: Debug {
747 fn name(&self) -> &str;
749
750 fn cluster_id(&self) -> ClusterId;
752
753 fn replica_id(&self) -> ReplicaId;
755
756 fn owner_id(&self) -> RoleId;
758
759 fn internal(&self) -> bool;
761}
762
763pub trait CatalogItem {
768 fn name(&self) -> &QualifiedItemName;
770
771 fn id(&self) -> CatalogItemId;
773
774 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_>;
776
777 fn oid(&self) -> u32;
779
780 fn func(&self) -> Result<&'static Func, CatalogError>;
785
786 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, CatalogError>;
791
792 fn connection(&self) -> Result<Connection<ReferencedConnection>, CatalogError>;
796
797 fn item_type(&self) -> CatalogItemType;
799
800 fn create_sql(&self) -> &str;
803
804 fn references(&self) -> &ResolvedIds;
807
808 fn uses(&self) -> BTreeSet<CatalogItemId>;
811
812 fn referenced_by(&self) -> &[CatalogItemId];
814
815 fn used_by(&self) -> &[CatalogItemId];
817
818 fn subsource_details(
821 &self,
822 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)>;
823
824 fn source_export_details(
827 &self,
828 ) -> Option<(
829 CatalogItemId,
830 &UnresolvedItemName,
831 &SourceExportDetails,
832 &SourceExportDataConfig<ReferencedConnection>,
833 )>;
834
835 fn is_progress_source(&self) -> bool;
837
838 fn progress_id(&self) -> Option<CatalogItemId>;
840
841 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)>;
844
845 fn writable_table_details(&self) -> Option<&[Expr<Aug>]>;
848
849 fn replacement_target(&self) -> Option<CatalogItemId>;
851
852 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>>;
855
856 fn owner_id(&self) -> RoleId;
858
859 fn privileges(&self) -> &PrivilegeMap;
861
862 fn cluster_id(&self) -> Option<ClusterId>;
864
865 fn at_version(&self, version: RelationVersionSelector) -> Box<dyn CatalogCollectionItem>;
868
869 fn latest_version(&self) -> Option<RelationVersion>;
871}
872
873pub trait CatalogCollectionItem: CatalogItem + Send + Sync {
876 fn relation_desc(&self) -> Option<Cow<'_, RelationDesc>>;
881
882 fn global_id(&self) -> GlobalId;
884}
885
886#[derive(
888 Debug,
889 Deserialize,
890 Clone,
891 Copy,
892 Eq,
893 Hash,
894 Ord,
895 PartialEq,
896 PartialOrd,
897 Serialize
898)]
899pub enum CatalogItemType {
900 Table,
902 Source,
904 Sink,
906 View,
908 MaterializedView,
910 Index,
912 Type,
914 Func,
916 Secret,
918 Connection,
920 ContinualTask,
922}
923
924impl CatalogItemType {
925 pub fn conflicts_with_type(&self) -> bool {
944 match self {
945 CatalogItemType::Table => true,
946 CatalogItemType::Source => true,
947 CatalogItemType::View => true,
948 CatalogItemType::MaterializedView => true,
949 CatalogItemType::Index => true,
950 CatalogItemType::Type => true,
951 CatalogItemType::Sink => false,
952 CatalogItemType::Func => false,
953 CatalogItemType::Secret => false,
954 CatalogItemType::Connection => false,
955 CatalogItemType::ContinualTask => true,
956 }
957 }
958}
959
960impl fmt::Display for CatalogItemType {
961 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
962 match self {
963 CatalogItemType::Table => f.write_str("table"),
964 CatalogItemType::Source => f.write_str("source"),
965 CatalogItemType::Sink => f.write_str("sink"),
966 CatalogItemType::View => f.write_str("view"),
967 CatalogItemType::MaterializedView => f.write_str("materialized view"),
968 CatalogItemType::Index => f.write_str("index"),
969 CatalogItemType::Type => f.write_str("type"),
970 CatalogItemType::Func => f.write_str("func"),
971 CatalogItemType::Secret => f.write_str("secret"),
972 CatalogItemType::Connection => f.write_str("connection"),
973 CatalogItemType::ContinualTask => f.write_str("continual task"),
974 }
975 }
976}
977
978impl From<CatalogItemType> for ObjectType {
979 fn from(value: CatalogItemType) -> Self {
980 match value {
981 CatalogItemType::Table => ObjectType::Table,
982 CatalogItemType::Source => ObjectType::Source,
983 CatalogItemType::Sink => ObjectType::Sink,
984 CatalogItemType::View => ObjectType::View,
985 CatalogItemType::MaterializedView => ObjectType::MaterializedView,
986 CatalogItemType::Index => ObjectType::Index,
987 CatalogItemType::Type => ObjectType::Type,
988 CatalogItemType::Func => ObjectType::Func,
989 CatalogItemType::Secret => ObjectType::Secret,
990 CatalogItemType::Connection => ObjectType::Connection,
991 CatalogItemType::ContinualTask => ObjectType::ContinualTask,
992 }
993 }
994}
995
996impl From<CatalogItemType> for mz_audit_log::ObjectType {
997 fn from(value: CatalogItemType) -> Self {
998 match value {
999 CatalogItemType::Table => mz_audit_log::ObjectType::Table,
1000 CatalogItemType::Source => mz_audit_log::ObjectType::Source,
1001 CatalogItemType::View => mz_audit_log::ObjectType::View,
1002 CatalogItemType::MaterializedView => mz_audit_log::ObjectType::MaterializedView,
1003 CatalogItemType::Index => mz_audit_log::ObjectType::Index,
1004 CatalogItemType::Type => mz_audit_log::ObjectType::Type,
1005 CatalogItemType::Sink => mz_audit_log::ObjectType::Sink,
1006 CatalogItemType::Func => mz_audit_log::ObjectType::Func,
1007 CatalogItemType::Secret => mz_audit_log::ObjectType::Secret,
1008 CatalogItemType::Connection => mz_audit_log::ObjectType::Connection,
1009 CatalogItemType::ContinualTask => mz_audit_log::ObjectType::ContinualTask,
1010 }
1011 }
1012}
1013
1014#[derive(Clone, Debug, Eq, PartialEq)]
1016pub struct CatalogTypeDetails<T: TypeReference> {
1017 pub array_id: Option<CatalogItemId>,
1019 pub typ: CatalogType<T>,
1021 pub pg_metadata: Option<CatalogTypePgMetadata>,
1023}
1024
1025#[derive(Clone, Debug, Eq, PartialEq)]
1027pub struct CatalogTypePgMetadata {
1028 pub typinput_oid: u32,
1030 pub typreceive_oid: u32,
1032}
1033
1034pub trait TypeReference {
1036 type Reference: Clone + Debug + Eq + PartialEq;
1038}
1039
1040#[derive(Clone, Debug, Eq, PartialEq)]
1042pub struct NameReference;
1043
1044impl TypeReference for NameReference {
1045 type Reference = &'static str;
1046}
1047
1048#[derive(Clone, Debug, Eq, PartialEq)]
1050pub struct IdReference;
1051
1052impl TypeReference for IdReference {
1053 type Reference = CatalogItemId;
1054}
1055
1056#[allow(missing_docs)]
1062#[derive(Clone, Debug, Eq, PartialEq)]
1063pub enum CatalogType<T: TypeReference> {
1064 AclItem,
1065 Array {
1066 element_reference: T::Reference,
1067 },
1068 Bool,
1069 Bytes,
1070 Char,
1071 Date,
1072 Float32,
1073 Float64,
1074 Int16,
1075 Int32,
1076 Int64,
1077 UInt16,
1078 UInt32,
1079 UInt64,
1080 MzTimestamp,
1081 Interval,
1082 Jsonb,
1083 List {
1084 element_reference: T::Reference,
1085 element_modifiers: Vec<i64>,
1086 },
1087 Map {
1088 key_reference: T::Reference,
1089 key_modifiers: Vec<i64>,
1090 value_reference: T::Reference,
1091 value_modifiers: Vec<i64>,
1092 },
1093 Numeric,
1094 Oid,
1095 PgLegacyChar,
1096 PgLegacyName,
1097 Pseudo,
1098 Range {
1099 element_reference: T::Reference,
1100 },
1101 Record {
1102 fields: Vec<CatalogRecordField<T>>,
1103 },
1104 RegClass,
1105 RegProc,
1106 RegType,
1107 String,
1108 Time,
1109 Timestamp,
1110 TimestampTz,
1111 Uuid,
1112 VarChar,
1113 Int2Vector,
1114 MzAclItem,
1115}
1116
1117impl CatalogType<IdReference> {
1118 pub fn desc(&self, catalog: &dyn SessionCatalog) -> Result<Option<RelationDesc>, PlanError> {
1121 match &self {
1122 CatalogType::Record { fields } => {
1123 let mut desc = RelationDesc::builder();
1124 for f in fields {
1125 let name = f.name.clone();
1126 let ty = query::scalar_type_from_catalog(
1127 catalog,
1128 f.type_reference,
1129 &f.type_modifiers,
1130 )?;
1131 let ty = ty.nullable(true);
1134 desc = desc.with_column(name, ty);
1135 }
1136 Ok(Some(desc.finish()))
1137 }
1138 _ => Ok(None),
1139 }
1140 }
1141}
1142
1143#[derive(Clone, Debug, Eq, PartialEq)]
1145pub struct CatalogRecordField<T: TypeReference> {
1146 pub name: ColumnName,
1148 pub type_reference: T::Reference,
1150 pub type_modifiers: Vec<i64>,
1152}
1153
1154#[derive(Clone, Debug, Eq, PartialEq)]
1155pub enum TypeCategory {
1163 Array,
1165 BitString,
1167 Boolean,
1169 Composite,
1171 DateTime,
1173 Enum,
1175 Geometric,
1177 List,
1179 NetworkAddress,
1181 Numeric,
1183 Pseudo,
1185 Range,
1187 String,
1189 Timespan,
1191 UserDefined,
1193 Unknown,
1195}
1196
1197impl fmt::Display for TypeCategory {
1198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1199 f.write_str(match self {
1200 TypeCategory::Array => "array",
1201 TypeCategory::BitString => "bit-string",
1202 TypeCategory::Boolean => "boolean",
1203 TypeCategory::Composite => "composite",
1204 TypeCategory::DateTime => "date-time",
1205 TypeCategory::Enum => "enum",
1206 TypeCategory::Geometric => "geometric",
1207 TypeCategory::List => "list",
1208 TypeCategory::NetworkAddress => "network-address",
1209 TypeCategory::Numeric => "numeric",
1210 TypeCategory::Pseudo => "pseudo",
1211 TypeCategory::Range => "range",
1212 TypeCategory::String => "string",
1213 TypeCategory::Timespan => "timespan",
1214 TypeCategory::UserDefined => "user-defined",
1215 TypeCategory::Unknown => "unknown",
1216 })
1217 }
1218}
1219
1220#[derive(Debug, Clone, PartialEq)]
1244pub struct EnvironmentId {
1245 cloud_provider: CloudProvider,
1246 cloud_provider_region: String,
1247 organization_id: Uuid,
1248 ordinal: u64,
1249}
1250
1251impl EnvironmentId {
1252 pub fn for_tests() -> EnvironmentId {
1254 EnvironmentId {
1255 cloud_provider: CloudProvider::Local,
1256 cloud_provider_region: "az1".into(),
1257 organization_id: Uuid::new_v4(),
1258 ordinal: 0,
1259 }
1260 }
1261
1262 pub fn cloud_provider(&self) -> &CloudProvider {
1264 &self.cloud_provider
1265 }
1266
1267 pub fn cloud_provider_region(&self) -> &str {
1269 &self.cloud_provider_region
1270 }
1271
1272 pub fn region(&self) -> String {
1277 format!("{}/{}", self.cloud_provider, self.cloud_provider_region)
1278 }
1279
1280 pub fn organization_id(&self) -> Uuid {
1282 self.organization_id
1283 }
1284
1285 pub fn ordinal(&self) -> u64 {
1287 self.ordinal
1288 }
1289}
1290
1291impl fmt::Display for EnvironmentId {
1297 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1298 write!(
1299 f,
1300 "{}-{}-{}-{}",
1301 self.cloud_provider, self.cloud_provider_region, self.organization_id, self.ordinal
1302 )
1303 }
1304}
1305
1306impl FromStr for EnvironmentId {
1307 type Err = InvalidEnvironmentIdError;
1308
1309 fn from_str(s: &str) -> Result<EnvironmentId, InvalidEnvironmentIdError> {
1310 static MATCHER: LazyLock<Regex> = LazyLock::new(|| {
1311 Regex::new(
1312 "^(?P<cloud_provider>[[:alnum:]]+)-\
1313 (?P<cloud_provider_region>[[:alnum:]\\-]+)-\
1314 (?P<organization_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-\
1315 (?P<ordinal>\\d{1,8})$"
1316 ).unwrap()
1317 });
1318 let captures = MATCHER.captures(s).ok_or(InvalidEnvironmentIdError)?;
1319 Ok(EnvironmentId {
1320 cloud_provider: CloudProvider::from_str(&captures["cloud_provider"])?,
1321 cloud_provider_region: captures["cloud_provider_region"].into(),
1322 organization_id: captures["organization_id"]
1323 .parse()
1324 .map_err(|_| InvalidEnvironmentIdError)?,
1325 ordinal: captures["ordinal"]
1326 .parse()
1327 .map_err(|_| InvalidEnvironmentIdError)?,
1328 })
1329 }
1330}
1331
1332#[derive(Debug, Clone, PartialEq)]
1334pub struct InvalidEnvironmentIdError;
1335
1336impl fmt::Display for InvalidEnvironmentIdError {
1337 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1338 f.write_str("invalid environment ID")
1339 }
1340}
1341
1342impl Error for InvalidEnvironmentIdError {}
1343
1344impl From<InvalidCloudProviderError> for InvalidEnvironmentIdError {
1345 fn from(_: InvalidCloudProviderError) -> Self {
1346 InvalidEnvironmentIdError
1347 }
1348}
1349
1350#[derive(Clone, Debug, Eq, PartialEq)]
1352pub enum CatalogError {
1353 UnknownDatabase(String),
1355 DatabaseAlreadyExists(String),
1357 UnknownSchema(String),
1359 SchemaAlreadyExists(String),
1361 UnknownRole(String),
1363 RoleAlreadyExists(String),
1365 NetworkPolicyAlreadyExists(String),
1367 UnknownCluster(String),
1369 UnexpectedBuiltinCluster(String),
1371 UnexpectedBuiltinClusterType(String),
1373 ClusterAlreadyExists(String),
1375 UnknownClusterReplica(String),
1377 UnknownClusterReplicaSize(String),
1379 DuplicateReplica(String, String),
1381 UnknownItem(String),
1383 ItemAlreadyExists(CatalogItemId, String),
1385 UnknownFunction {
1387 name: String,
1389 alternative: Option<String>,
1391 },
1392 UnknownType {
1394 name: String,
1396 },
1397 UnknownConnection(String),
1399 UnknownNetworkPolicy(String),
1401 UnexpectedType {
1403 name: String,
1405 actual_type: CatalogItemType,
1407 expected_type: CatalogItemType,
1409 },
1410 IdExhaustion,
1412 OidExhaustion,
1414 TimelineAlreadyExists(String),
1416 IdAllocatorAlreadyExists(String),
1418 ConfigAlreadyExists(String),
1420 FailedBuiltinSchemaMigration(String),
1422 StorageCollectionMetadataAlreadyExists(GlobalId),
1424}
1425
1426impl fmt::Display for CatalogError {
1427 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1428 match self {
1429 Self::UnknownDatabase(name) => write!(f, "unknown database '{}'", name),
1430 Self::DatabaseAlreadyExists(name) => write!(f, "database '{name}' already exists"),
1431 Self::UnknownFunction { name, .. } => write!(f, "function \"{}\" does not exist", name),
1432 Self::UnknownType { name, .. } => write!(f, "type \"{}\" does not exist", name),
1433 Self::UnknownConnection(name) => write!(f, "connection \"{}\" does not exist", name),
1434 Self::UnknownSchema(name) => write!(f, "unknown schema '{}'", name),
1435 Self::SchemaAlreadyExists(name) => write!(f, "schema '{name}' already exists"),
1436 Self::UnknownRole(name) => write!(f, "unknown role '{}'", name),
1437 Self::RoleAlreadyExists(name) => write!(f, "role '{name}' already exists"),
1438 Self::NetworkPolicyAlreadyExists(name) => {
1439 write!(f, "network policy '{name}' already exists")
1440 }
1441 Self::UnknownCluster(name) => write!(f, "unknown cluster '{}'", name),
1442 Self::UnknownNetworkPolicy(name) => write!(f, "unknown network policy '{}'", name),
1443 Self::UnexpectedBuiltinCluster(name) => {
1444 write!(f, "Unexpected builtin cluster '{}'", name)
1445 }
1446 Self::UnexpectedBuiltinClusterType(name) => {
1447 write!(f, "Unexpected builtin cluster type'{}'", name)
1448 }
1449 Self::ClusterAlreadyExists(name) => write!(f, "cluster '{name}' already exists"),
1450 Self::UnknownClusterReplica(name) => {
1451 write!(f, "unknown cluster replica '{}'", name)
1452 }
1453 Self::UnknownClusterReplicaSize(name) => {
1454 write!(f, "unknown cluster replica size '{}'", name)
1455 }
1456 Self::DuplicateReplica(replica_name, cluster_name) => write!(
1457 f,
1458 "cannot create multiple replicas named '{replica_name}' on cluster '{cluster_name}'"
1459 ),
1460 Self::UnknownItem(name) => write!(f, "unknown catalog item '{}'", name),
1461 Self::ItemAlreadyExists(_gid, name) => {
1462 write!(f, "catalog item '{name}' already exists")
1463 }
1464 Self::UnexpectedType {
1465 name,
1466 actual_type,
1467 expected_type,
1468 } => {
1469 write!(f, "\"{name}\" is a {actual_type} not a {expected_type}")
1470 }
1471 Self::IdExhaustion => write!(f, "id counter overflows i64"),
1472 Self::OidExhaustion => write!(f, "oid counter overflows u32"),
1473 Self::TimelineAlreadyExists(name) => write!(f, "timeline '{name}' already exists"),
1474 Self::IdAllocatorAlreadyExists(name) => {
1475 write!(f, "ID allocator '{name}' already exists")
1476 }
1477 Self::ConfigAlreadyExists(key) => write!(f, "config '{key}' already exists"),
1478 Self::FailedBuiltinSchemaMigration(objects) => {
1479 write!(f, "failed to migrate schema of builtin objects: {objects}")
1480 }
1481 Self::StorageCollectionMetadataAlreadyExists(key) => {
1482 write!(f, "storage metadata for '{key}' already exists")
1483 }
1484 }
1485 }
1486}
1487
1488impl CatalogError {
1489 pub fn hint(&self) -> Option<String> {
1491 match self {
1492 CatalogError::UnknownFunction { alternative, .. } => {
1493 match alternative {
1494 None => Some("No function matches the given name and argument types. You might need to add explicit type casts.".into()),
1495 Some(alt) => Some(format!("Try using {alt}")),
1496 }
1497 }
1498 _ => None,
1499 }
1500 }
1501}
1502
1503impl Error for CatalogError {}
1504
1505#[allow(missing_docs)]
1507#[derive(
1508 Debug,
1509 Clone,
1510 PartialOrd,
1511 Ord,
1512 PartialEq,
1513 Eq,
1514 Hash,
1515 Copy,
1516 Deserialize,
1517 Serialize
1518)]
1519pub enum ObjectType {
1521 Table,
1522 View,
1523 MaterializedView,
1524 Source,
1525 Sink,
1526 Index,
1527 Type,
1528 Role,
1529 Cluster,
1530 ClusterReplica,
1531 Secret,
1532 Connection,
1533 Database,
1534 Schema,
1535 Func,
1536 ContinualTask,
1537 NetworkPolicy,
1538}
1539
1540impl ObjectType {
1541 pub fn is_relation(&self) -> bool {
1543 match self {
1544 ObjectType::Table
1545 | ObjectType::View
1546 | ObjectType::MaterializedView
1547 | ObjectType::Source
1548 | ObjectType::ContinualTask => true,
1549 ObjectType::Sink
1550 | ObjectType::Index
1551 | ObjectType::Type
1552 | ObjectType::Secret
1553 | ObjectType::Connection
1554 | ObjectType::Func
1555 | ObjectType::Database
1556 | ObjectType::Schema
1557 | ObjectType::Cluster
1558 | ObjectType::ClusterReplica
1559 | ObjectType::Role
1560 | ObjectType::NetworkPolicy => false,
1561 }
1562 }
1563}
1564
1565impl From<mz_sql_parser::ast::ObjectType> for ObjectType {
1566 fn from(value: mz_sql_parser::ast::ObjectType) -> Self {
1567 match value {
1568 mz_sql_parser::ast::ObjectType::Table => ObjectType::Table,
1569 mz_sql_parser::ast::ObjectType::View => ObjectType::View,
1570 mz_sql_parser::ast::ObjectType::MaterializedView => ObjectType::MaterializedView,
1571 mz_sql_parser::ast::ObjectType::Source => ObjectType::Source,
1572 mz_sql_parser::ast::ObjectType::Subsource => ObjectType::Source,
1573 mz_sql_parser::ast::ObjectType::Sink => ObjectType::Sink,
1574 mz_sql_parser::ast::ObjectType::Index => ObjectType::Index,
1575 mz_sql_parser::ast::ObjectType::Type => ObjectType::Type,
1576 mz_sql_parser::ast::ObjectType::Role => ObjectType::Role,
1577 mz_sql_parser::ast::ObjectType::Cluster => ObjectType::Cluster,
1578 mz_sql_parser::ast::ObjectType::ClusterReplica => ObjectType::ClusterReplica,
1579 mz_sql_parser::ast::ObjectType::Secret => ObjectType::Secret,
1580 mz_sql_parser::ast::ObjectType::Connection => ObjectType::Connection,
1581 mz_sql_parser::ast::ObjectType::Database => ObjectType::Database,
1582 mz_sql_parser::ast::ObjectType::Schema => ObjectType::Schema,
1583 mz_sql_parser::ast::ObjectType::Func => ObjectType::Func,
1584 mz_sql_parser::ast::ObjectType::ContinualTask => ObjectType::ContinualTask,
1585 mz_sql_parser::ast::ObjectType::NetworkPolicy => ObjectType::NetworkPolicy,
1586 }
1587 }
1588}
1589
1590impl From<CommentObjectId> for ObjectType {
1591 fn from(value: CommentObjectId) -> ObjectType {
1592 match value {
1593 CommentObjectId::Table(_) => ObjectType::Table,
1594 CommentObjectId::View(_) => ObjectType::View,
1595 CommentObjectId::MaterializedView(_) => ObjectType::MaterializedView,
1596 CommentObjectId::Source(_) => ObjectType::Source,
1597 CommentObjectId::Sink(_) => ObjectType::Sink,
1598 CommentObjectId::Index(_) => ObjectType::Index,
1599 CommentObjectId::Func(_) => ObjectType::Func,
1600 CommentObjectId::Connection(_) => ObjectType::Connection,
1601 CommentObjectId::Type(_) => ObjectType::Type,
1602 CommentObjectId::Secret(_) => ObjectType::Secret,
1603 CommentObjectId::Role(_) => ObjectType::Role,
1604 CommentObjectId::Database(_) => ObjectType::Database,
1605 CommentObjectId::Schema(_) => ObjectType::Schema,
1606 CommentObjectId::Cluster(_) => ObjectType::Cluster,
1607 CommentObjectId::ClusterReplica(_) => ObjectType::ClusterReplica,
1608 CommentObjectId::ContinualTask(_) => ObjectType::ContinualTask,
1609 CommentObjectId::NetworkPolicy(_) => ObjectType::NetworkPolicy,
1610 }
1611 }
1612}
1613
1614impl Display for ObjectType {
1615 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1616 f.write_str(match self {
1617 ObjectType::Table => "TABLE",
1618 ObjectType::View => "VIEW",
1619 ObjectType::MaterializedView => "MATERIALIZED VIEW",
1620 ObjectType::Source => "SOURCE",
1621 ObjectType::Sink => "SINK",
1622 ObjectType::Index => "INDEX",
1623 ObjectType::Type => "TYPE",
1624 ObjectType::Role => "ROLE",
1625 ObjectType::Cluster => "CLUSTER",
1626 ObjectType::ClusterReplica => "CLUSTER REPLICA",
1627 ObjectType::Secret => "SECRET",
1628 ObjectType::Connection => "CONNECTION",
1629 ObjectType::Database => "DATABASE",
1630 ObjectType::Schema => "SCHEMA",
1631 ObjectType::Func => "FUNCTION",
1632 ObjectType::ContinualTask => "CONTINUAL TASK",
1633 ObjectType::NetworkPolicy => "NETWORK POLICY",
1634 })
1635 }
1636}
1637
1638#[derive(
1639 Debug,
1640 Clone,
1641 PartialOrd,
1642 Ord,
1643 PartialEq,
1644 Eq,
1645 Hash,
1646 Copy,
1647 Deserialize,
1648 Serialize
1649)]
1650pub enum SystemObjectType {
1652 Object(ObjectType),
1654 System,
1656}
1657
1658impl SystemObjectType {
1659 pub fn is_relation(&self) -> bool {
1661 match self {
1662 SystemObjectType::Object(object_type) => object_type.is_relation(),
1663 SystemObjectType::System => false,
1664 }
1665 }
1666}
1667
1668impl Display for SystemObjectType {
1669 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1670 match self {
1671 SystemObjectType::Object(object_type) => std::fmt::Display::fmt(&object_type, f),
1672 SystemObjectType::System => f.write_str("SYSTEM"),
1673 }
1674 }
1675}
1676
1677#[derive(Debug, Clone, PartialEq, Eq)]
1679pub enum ErrorMessageObjectDescription {
1680 Object {
1682 object_type: ObjectType,
1684 object_name: Option<String>,
1686 },
1687 System,
1689}
1690
1691impl ErrorMessageObjectDescription {
1692 pub fn from_id(
1694 object_id: &ObjectId,
1695 catalog: &dyn SessionCatalog,
1696 ) -> ErrorMessageObjectDescription {
1697 let object_name = match object_id {
1698 ObjectId::Cluster(cluster_id) => catalog.get_cluster(*cluster_id).name().to_string(),
1699 ObjectId::ClusterReplica((cluster_id, replica_id)) => catalog
1700 .get_cluster_replica(*cluster_id, *replica_id)
1701 .name()
1702 .to_string(),
1703 ObjectId::Database(database_id) => catalog.get_database(database_id).name().to_string(),
1704 ObjectId::Schema((database_spec, schema_spec)) => {
1705 let name = catalog.get_schema(database_spec, schema_spec).name();
1706 catalog.resolve_full_schema_name(name).to_string()
1707 }
1708 ObjectId::Role(role_id) => catalog.get_role(role_id).name().to_string(),
1709 ObjectId::Item(id) => {
1710 let name = catalog.get_item(id).name();
1711 catalog.resolve_full_name(name).to_string()
1712 }
1713 ObjectId::NetworkPolicy(network_policy_id) => catalog
1714 .get_network_policy(network_policy_id)
1715 .name()
1716 .to_string(),
1717 };
1718 ErrorMessageObjectDescription::Object {
1719 object_type: catalog.get_object_type(object_id),
1720 object_name: Some(object_name),
1721 }
1722 }
1723
1724 pub fn from_sys_id(
1726 object_id: &SystemObjectId,
1727 catalog: &dyn SessionCatalog,
1728 ) -> ErrorMessageObjectDescription {
1729 match object_id {
1730 SystemObjectId::Object(object_id) => {
1731 ErrorMessageObjectDescription::from_id(object_id, catalog)
1732 }
1733 SystemObjectId::System => ErrorMessageObjectDescription::System,
1734 }
1735 }
1736
1737 pub fn from_object_type(object_type: SystemObjectType) -> ErrorMessageObjectDescription {
1739 match object_type {
1740 SystemObjectType::Object(object_type) => ErrorMessageObjectDescription::Object {
1741 object_type,
1742 object_name: None,
1743 },
1744 SystemObjectType::System => ErrorMessageObjectDescription::System,
1745 }
1746 }
1747}
1748
1749impl Display for ErrorMessageObjectDescription {
1750 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1751 match self {
1752 ErrorMessageObjectDescription::Object {
1753 object_type,
1754 object_name,
1755 } => {
1756 let object_name = object_name
1757 .as_ref()
1758 .map(|object_name| format!(" {}", object_name.quoted()))
1759 .unwrap_or_else(|| "".to_string());
1760 write!(f, "{object_type}{object_name}")
1761 }
1762 ErrorMessageObjectDescription::System => f.write_str("SYSTEM"),
1763 }
1764 }
1765}
1766
1767#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
1768#[serde(into = "BTreeMap<String, RoleId>")]
1771#[serde(try_from = "BTreeMap<String, RoleId>")]
1772pub struct RoleMembership {
1774 pub map: BTreeMap<RoleId, RoleId>,
1780}
1781
1782impl RoleMembership {
1783 pub fn new() -> RoleMembership {
1785 RoleMembership {
1786 map: BTreeMap::new(),
1787 }
1788 }
1789}
1790
1791impl From<RoleMembership> for BTreeMap<String, RoleId> {
1792 fn from(value: RoleMembership) -> Self {
1793 value
1794 .map
1795 .into_iter()
1796 .map(|(k, v)| (k.to_string(), v))
1797 .collect()
1798 }
1799}
1800
1801impl TryFrom<BTreeMap<String, RoleId>> for RoleMembership {
1802 type Error = anyhow::Error;
1803
1804 fn try_from(value: BTreeMap<String, RoleId>) -> Result<Self, Self::Error> {
1805 Ok(RoleMembership {
1806 map: value
1807 .into_iter()
1808 .map(|(k, v)| Ok((RoleId::from_str(&k)?, v)))
1809 .collect::<Result<_, anyhow::Error>>()?,
1810 })
1811 }
1812}
1813
1814#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1816pub struct DefaultPrivilegeObject {
1817 pub role_id: RoleId,
1819 pub database_id: Option<DatabaseId>,
1821 pub schema_id: Option<SchemaId>,
1823 pub object_type: ObjectType,
1825}
1826
1827impl DefaultPrivilegeObject {
1828 pub fn new(
1830 role_id: RoleId,
1831 database_id: Option<DatabaseId>,
1832 schema_id: Option<SchemaId>,
1833 object_type: ObjectType,
1834 ) -> DefaultPrivilegeObject {
1835 DefaultPrivilegeObject {
1836 role_id,
1837 database_id,
1838 schema_id,
1839 object_type,
1840 }
1841 }
1842}
1843
1844impl std::fmt::Display for DefaultPrivilegeObject {
1845 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1846 write!(f, "{self:?}")
1848 }
1849}
1850
1851#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1853pub struct DefaultPrivilegeAclItem {
1854 pub grantee: RoleId,
1856 pub acl_mode: AclMode,
1858}
1859
1860impl DefaultPrivilegeAclItem {
1861 pub fn new(grantee: RoleId, acl_mode: AclMode) -> DefaultPrivilegeAclItem {
1863 DefaultPrivilegeAclItem { grantee, acl_mode }
1864 }
1865
1866 pub fn mz_acl_item(self, grantor: RoleId) -> MzAclItem {
1868 MzAclItem {
1869 grantee: self.grantee,
1870 grantor,
1871 acl_mode: self.acl_mode,
1872 }
1873 }
1874}
1875
1876#[derive(Debug, Clone)]
1882pub struct BuiltinsConfig {
1883 pub include_continual_tasks: bool,
1885}
1886
1887#[cfg(test)]
1888mod tests {
1889 use super::{CloudProvider, EnvironmentId, InvalidEnvironmentIdError};
1890
1891 #[mz_ore::test]
1892 fn test_environment_id() {
1893 for (input, expected) in [
1894 (
1895 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1896 Ok(EnvironmentId {
1897 cloud_provider: CloudProvider::Local,
1898 cloud_provider_region: "az1".into(),
1899 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1900 ordinal: 452,
1901 }),
1902 ),
1903 (
1904 "aws-us-east-1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1905 Ok(EnvironmentId {
1906 cloud_provider: CloudProvider::Aws,
1907 cloud_provider_region: "us-east-1".into(),
1908 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1909 ordinal: 0,
1910 }),
1911 ),
1912 (
1913 "gcp-us-central1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1914 Ok(EnvironmentId {
1915 cloud_provider: CloudProvider::Gcp,
1916 cloud_provider_region: "us-central1".into(),
1917 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1918 ordinal: 0,
1919 }),
1920 ),
1921 (
1922 "azure-australiaeast-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1923 Ok(EnvironmentId {
1924 cloud_provider: CloudProvider::Azure,
1925 cloud_provider_region: "australiaeast".into(),
1926 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1927 ordinal: 0,
1928 }),
1929 ),
1930 (
1931 "generic-moon-station-11-darkside-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1932 Ok(EnvironmentId {
1933 cloud_provider: CloudProvider::Generic,
1934 cloud_provider_region: "moon-station-11-darkside".into(),
1935 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1936 ordinal: 0,
1937 }),
1938 ),
1939 ("", Err(InvalidEnvironmentIdError)),
1940 (
1941 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-123456789",
1942 Err(InvalidEnvironmentIdError),
1943 ),
1944 (
1945 "local-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1946 Err(InvalidEnvironmentIdError),
1947 ),
1948 (
1949 "local-az1-1497a3b7-a455-4fc48752-b44a94b5f90a-452",
1950 Err(InvalidEnvironmentIdError),
1951 ),
1952 ] {
1953 let actual = input.parse();
1954 assert_eq!(expected, actual, "input = {}", input);
1955 if let Ok(actual) = actual {
1956 assert_eq!(input, actual.to_string(), "input = {}", input);
1957 }
1958 }
1959 }
1960}