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(Debug, Clone, Eq, Serialize, Deserialize, PartialEq, Ord, PartialOrd, Arbitrary)]
532pub struct RoleAttributes {
533 pub inherit: bool,
535 pub superuser: Option<bool>,
537 pub login: Option<bool>,
539 _private: (),
541}
542
543impl RoleAttributesRaw {
544 pub const fn new() -> RoleAttributesRaw {
546 RoleAttributesRaw {
547 inherit: true,
548 password: None,
549 scram_iterations: None,
550 superuser: None,
551 login: None,
552 _private: (),
553 }
554 }
555
556 pub const fn with_all(mut self) -> RoleAttributesRaw {
558 self.inherit = true;
559 self.superuser = Some(true);
560 self.login = Some(true);
561 self
562 }
563}
564
565impl RoleAttributes {
566 pub const fn new() -> RoleAttributes {
568 RoleAttributes {
569 inherit: true,
570 superuser: None,
571 login: None,
572 _private: (),
573 }
574 }
575
576 pub const fn with_all(mut self) -> RoleAttributes {
578 self.inherit = true;
579 self.superuser = Some(true);
580 self.login = Some(true);
581 self
582 }
583
584 pub const fn is_inherit(&self) -> bool {
586 self.inherit
587 }
588}
589
590impl From<RoleAttributesRaw> for RoleAttributes {
591 fn from(
592 RoleAttributesRaw {
593 inherit,
594 superuser,
595 login,
596 ..
597 }: RoleAttributesRaw,
598 ) -> RoleAttributes {
599 RoleAttributes {
600 inherit,
601 superuser,
602 login,
603 _private: (),
604 }
605 }
606}
607
608impl From<RoleAttributes> for RoleAttributesRaw {
609 fn from(
610 RoleAttributes {
611 inherit,
612 superuser,
613 login,
614 ..
615 }: RoleAttributes,
616 ) -> RoleAttributesRaw {
617 RoleAttributesRaw {
618 inherit,
619 password: None,
620 scram_iterations: None,
621 superuser,
622 login,
623 _private: (),
624 }
625 }
626}
627
628impl From<PlannedRoleAttributes> for RoleAttributesRaw {
629 fn from(
630 PlannedRoleAttributes {
631 inherit,
632 password,
633 scram_iterations,
634 superuser,
635 login,
636 ..
637 }: PlannedRoleAttributes,
638 ) -> RoleAttributesRaw {
639 let default_attributes = RoleAttributesRaw::new();
640 RoleAttributesRaw {
641 inherit: inherit.unwrap_or(default_attributes.inherit),
642 password,
643 scram_iterations,
644 superuser,
645 login,
646 _private: (),
647 }
648 }
649}
650
651#[derive(Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
653pub struct RoleVars {
654 pub map: BTreeMap<String, OwnedVarInput>,
656}
657
658pub trait CatalogRole {
660 fn name(&self) -> &str;
662
663 fn id(&self) -> RoleId;
665
666 fn membership(&self) -> &BTreeMap<RoleId, RoleId>;
671
672 fn attributes(&self) -> &RoleAttributes;
674
675 fn vars(&self) -> &BTreeMap<String, OwnedVarInput>;
677}
678
679pub trait CatalogNetworkPolicy {
681 fn name(&self) -> &str;
683
684 fn id(&self) -> NetworkPolicyId;
686
687 fn owner_id(&self) -> RoleId;
689
690 fn privileges(&self) -> &PrivilegeMap;
692}
693
694pub trait CatalogCluster<'a> {
696 fn name(&self) -> &str;
698
699 fn id(&self) -> ClusterId;
701
702 fn bound_objects(&self) -> &BTreeSet<CatalogItemId>;
704
705 fn replica_ids(&self) -> &BTreeMap<String, ReplicaId>;
708
709 fn replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>>;
711
712 fn replica(&self, id: ReplicaId) -> &dyn CatalogClusterReplica<'_>;
714
715 fn owner_id(&self) -> RoleId;
717
718 fn privileges(&self) -> &PrivilegeMap;
720
721 fn is_managed(&self) -> bool;
723
724 fn managed_size(&self) -> Option<&str>;
726
727 fn schedule(&self) -> Option<&ClusterSchedule>;
729
730 fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError>;
733}
734
735pub trait CatalogClusterReplica<'a>: Debug {
737 fn name(&self) -> &str;
739
740 fn cluster_id(&self) -> ClusterId;
742
743 fn replica_id(&self) -> ReplicaId;
745
746 fn owner_id(&self) -> RoleId;
748
749 fn internal(&self) -> bool;
751}
752
753pub trait CatalogItem {
758 fn name(&self) -> &QualifiedItemName;
760
761 fn id(&self) -> CatalogItemId;
763
764 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_>;
766
767 fn oid(&self) -> u32;
769
770 fn func(&self) -> Result<&'static Func, CatalogError>;
775
776 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, CatalogError>;
781
782 fn connection(&self) -> Result<Connection<ReferencedConnection>, CatalogError>;
786
787 fn item_type(&self) -> CatalogItemType;
789
790 fn create_sql(&self) -> &str;
793
794 fn references(&self) -> &ResolvedIds;
797
798 fn uses(&self) -> BTreeSet<CatalogItemId>;
801
802 fn referenced_by(&self) -> &[CatalogItemId];
804
805 fn used_by(&self) -> &[CatalogItemId];
807
808 fn subsource_details(
811 &self,
812 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)>;
813
814 fn source_export_details(
817 &self,
818 ) -> Option<(
819 CatalogItemId,
820 &UnresolvedItemName,
821 &SourceExportDetails,
822 &SourceExportDataConfig<ReferencedConnection>,
823 )>;
824
825 fn is_progress_source(&self) -> bool;
827
828 fn progress_id(&self) -> Option<CatalogItemId>;
830
831 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)>;
834
835 fn writable_table_details(&self) -> Option<&[Expr<Aug>]>;
838
839 fn replacement_target(&self) -> Option<CatalogItemId>;
841
842 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>>;
845
846 fn owner_id(&self) -> RoleId;
848
849 fn privileges(&self) -> &PrivilegeMap;
851
852 fn cluster_id(&self) -> Option<ClusterId>;
854
855 fn at_version(&self, version: RelationVersionSelector) -> Box<dyn CatalogCollectionItem>;
858
859 fn latest_version(&self) -> Option<RelationVersion>;
861}
862
863pub trait CatalogCollectionItem: CatalogItem + Send + Sync {
866 fn desc(&self, name: &FullItemName) -> Result<Cow<'_, RelationDesc>, CatalogError>;
871
872 fn global_id(&self) -> GlobalId;
874}
875
876#[derive(Debug, Deserialize, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
878pub enum CatalogItemType {
879 Table,
881 Source,
883 Sink,
885 View,
887 MaterializedView,
889 Index,
891 Type,
893 Func,
895 Secret,
897 Connection,
899 ContinualTask,
901}
902
903impl CatalogItemType {
904 pub fn conflicts_with_type(&self) -> bool {
923 match self {
924 CatalogItemType::Table => true,
925 CatalogItemType::Source => true,
926 CatalogItemType::View => true,
927 CatalogItemType::MaterializedView => true,
928 CatalogItemType::Index => true,
929 CatalogItemType::Type => true,
930 CatalogItemType::Sink => false,
931 CatalogItemType::Func => false,
932 CatalogItemType::Secret => false,
933 CatalogItemType::Connection => false,
934 CatalogItemType::ContinualTask => true,
935 }
936 }
937}
938
939impl fmt::Display for CatalogItemType {
940 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
941 match self {
942 CatalogItemType::Table => f.write_str("table"),
943 CatalogItemType::Source => f.write_str("source"),
944 CatalogItemType::Sink => f.write_str("sink"),
945 CatalogItemType::View => f.write_str("view"),
946 CatalogItemType::MaterializedView => f.write_str("materialized view"),
947 CatalogItemType::Index => f.write_str("index"),
948 CatalogItemType::Type => f.write_str("type"),
949 CatalogItemType::Func => f.write_str("func"),
950 CatalogItemType::Secret => f.write_str("secret"),
951 CatalogItemType::Connection => f.write_str("connection"),
952 CatalogItemType::ContinualTask => f.write_str("continual task"),
953 }
954 }
955}
956
957impl From<CatalogItemType> for ObjectType {
958 fn from(value: CatalogItemType) -> Self {
959 match value {
960 CatalogItemType::Table => ObjectType::Table,
961 CatalogItemType::Source => ObjectType::Source,
962 CatalogItemType::Sink => ObjectType::Sink,
963 CatalogItemType::View => ObjectType::View,
964 CatalogItemType::MaterializedView => ObjectType::MaterializedView,
965 CatalogItemType::Index => ObjectType::Index,
966 CatalogItemType::Type => ObjectType::Type,
967 CatalogItemType::Func => ObjectType::Func,
968 CatalogItemType::Secret => ObjectType::Secret,
969 CatalogItemType::Connection => ObjectType::Connection,
970 CatalogItemType::ContinualTask => ObjectType::ContinualTask,
971 }
972 }
973}
974
975impl From<CatalogItemType> for mz_audit_log::ObjectType {
976 fn from(value: CatalogItemType) -> Self {
977 match value {
978 CatalogItemType::Table => mz_audit_log::ObjectType::Table,
979 CatalogItemType::Source => mz_audit_log::ObjectType::Source,
980 CatalogItemType::View => mz_audit_log::ObjectType::View,
981 CatalogItemType::MaterializedView => mz_audit_log::ObjectType::MaterializedView,
982 CatalogItemType::Index => mz_audit_log::ObjectType::Index,
983 CatalogItemType::Type => mz_audit_log::ObjectType::Type,
984 CatalogItemType::Sink => mz_audit_log::ObjectType::Sink,
985 CatalogItemType::Func => mz_audit_log::ObjectType::Func,
986 CatalogItemType::Secret => mz_audit_log::ObjectType::Secret,
987 CatalogItemType::Connection => mz_audit_log::ObjectType::Connection,
988 CatalogItemType::ContinualTask => mz_audit_log::ObjectType::ContinualTask,
989 }
990 }
991}
992
993#[derive(Clone, Debug, Eq, PartialEq)]
995pub struct CatalogTypeDetails<T: TypeReference> {
996 pub array_id: Option<CatalogItemId>,
998 pub typ: CatalogType<T>,
1000 pub pg_metadata: Option<CatalogTypePgMetadata>,
1002}
1003
1004#[derive(Clone, Debug, Eq, PartialEq)]
1006pub struct CatalogTypePgMetadata {
1007 pub typinput_oid: u32,
1009 pub typreceive_oid: u32,
1011}
1012
1013pub trait TypeReference {
1015 type Reference: Clone + Debug + Eq + PartialEq;
1017}
1018
1019#[derive(Clone, Debug, Eq, PartialEq)]
1021pub struct NameReference;
1022
1023impl TypeReference for NameReference {
1024 type Reference = &'static str;
1025}
1026
1027#[derive(Clone, Debug, Eq, PartialEq)]
1029pub struct IdReference;
1030
1031impl TypeReference for IdReference {
1032 type Reference = CatalogItemId;
1033}
1034
1035#[allow(missing_docs)]
1041#[derive(Clone, Debug, Eq, PartialEq)]
1042pub enum CatalogType<T: TypeReference> {
1043 AclItem,
1044 Array {
1045 element_reference: T::Reference,
1046 },
1047 Bool,
1048 Bytes,
1049 Char,
1050 Date,
1051 Float32,
1052 Float64,
1053 Int16,
1054 Int32,
1055 Int64,
1056 UInt16,
1057 UInt32,
1058 UInt64,
1059 MzTimestamp,
1060 Interval,
1061 Jsonb,
1062 List {
1063 element_reference: T::Reference,
1064 element_modifiers: Vec<i64>,
1065 },
1066 Map {
1067 key_reference: T::Reference,
1068 key_modifiers: Vec<i64>,
1069 value_reference: T::Reference,
1070 value_modifiers: Vec<i64>,
1071 },
1072 Numeric,
1073 Oid,
1074 PgLegacyChar,
1075 PgLegacyName,
1076 Pseudo,
1077 Range {
1078 element_reference: T::Reference,
1079 },
1080 Record {
1081 fields: Vec<CatalogRecordField<T>>,
1082 },
1083 RegClass,
1084 RegProc,
1085 RegType,
1086 String,
1087 Time,
1088 Timestamp,
1089 TimestampTz,
1090 Uuid,
1091 VarChar,
1092 Int2Vector,
1093 MzAclItem,
1094}
1095
1096impl CatalogType<IdReference> {
1097 pub fn desc(&self, catalog: &dyn SessionCatalog) -> Result<Option<RelationDesc>, PlanError> {
1100 match &self {
1101 CatalogType::Record { fields } => {
1102 let mut desc = RelationDesc::builder();
1103 for f in fields {
1104 let name = f.name.clone();
1105 let ty = query::scalar_type_from_catalog(
1106 catalog,
1107 f.type_reference,
1108 &f.type_modifiers,
1109 )?;
1110 let ty = ty.nullable(true);
1113 desc = desc.with_column(name, ty);
1114 }
1115 Ok(Some(desc.finish()))
1116 }
1117 _ => Ok(None),
1118 }
1119 }
1120}
1121
1122#[derive(Clone, Debug, Eq, PartialEq)]
1124pub struct CatalogRecordField<T: TypeReference> {
1125 pub name: ColumnName,
1127 pub type_reference: T::Reference,
1129 pub type_modifiers: Vec<i64>,
1131}
1132
1133#[derive(Clone, Debug, Eq, PartialEq)]
1134pub enum TypeCategory {
1142 Array,
1144 BitString,
1146 Boolean,
1148 Composite,
1150 DateTime,
1152 Enum,
1154 Geometric,
1156 List,
1158 NetworkAddress,
1160 Numeric,
1162 Pseudo,
1164 Range,
1166 String,
1168 Timespan,
1170 UserDefined,
1172 Unknown,
1174}
1175
1176impl fmt::Display for TypeCategory {
1177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1178 f.write_str(match self {
1179 TypeCategory::Array => "array",
1180 TypeCategory::BitString => "bit-string",
1181 TypeCategory::Boolean => "boolean",
1182 TypeCategory::Composite => "composite",
1183 TypeCategory::DateTime => "date-time",
1184 TypeCategory::Enum => "enum",
1185 TypeCategory::Geometric => "geometric",
1186 TypeCategory::List => "list",
1187 TypeCategory::NetworkAddress => "network-address",
1188 TypeCategory::Numeric => "numeric",
1189 TypeCategory::Pseudo => "pseudo",
1190 TypeCategory::Range => "range",
1191 TypeCategory::String => "string",
1192 TypeCategory::Timespan => "timespan",
1193 TypeCategory::UserDefined => "user-defined",
1194 TypeCategory::Unknown => "unknown",
1195 })
1196 }
1197}
1198
1199#[derive(Debug, Clone, PartialEq)]
1223pub struct EnvironmentId {
1224 cloud_provider: CloudProvider,
1225 cloud_provider_region: String,
1226 organization_id: Uuid,
1227 ordinal: u64,
1228}
1229
1230impl EnvironmentId {
1231 pub fn for_tests() -> EnvironmentId {
1233 EnvironmentId {
1234 cloud_provider: CloudProvider::Local,
1235 cloud_provider_region: "az1".into(),
1236 organization_id: Uuid::new_v4(),
1237 ordinal: 0,
1238 }
1239 }
1240
1241 pub fn cloud_provider(&self) -> &CloudProvider {
1243 &self.cloud_provider
1244 }
1245
1246 pub fn cloud_provider_region(&self) -> &str {
1248 &self.cloud_provider_region
1249 }
1250
1251 pub fn region(&self) -> String {
1256 format!("{}/{}", self.cloud_provider, self.cloud_provider_region)
1257 }
1258
1259 pub fn organization_id(&self) -> Uuid {
1261 self.organization_id
1262 }
1263
1264 pub fn ordinal(&self) -> u64 {
1266 self.ordinal
1267 }
1268}
1269
1270impl fmt::Display for EnvironmentId {
1276 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1277 write!(
1278 f,
1279 "{}-{}-{}-{}",
1280 self.cloud_provider, self.cloud_provider_region, self.organization_id, self.ordinal
1281 )
1282 }
1283}
1284
1285impl FromStr for EnvironmentId {
1286 type Err = InvalidEnvironmentIdError;
1287
1288 fn from_str(s: &str) -> Result<EnvironmentId, InvalidEnvironmentIdError> {
1289 static MATCHER: LazyLock<Regex> = LazyLock::new(|| {
1290 Regex::new(
1291 "^(?P<cloud_provider>[[:alnum:]]+)-\
1292 (?P<cloud_provider_region>[[:alnum:]\\-]+)-\
1293 (?P<organization_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-\
1294 (?P<ordinal>\\d{1,8})$"
1295 ).unwrap()
1296 });
1297 let captures = MATCHER.captures(s).ok_or(InvalidEnvironmentIdError)?;
1298 Ok(EnvironmentId {
1299 cloud_provider: CloudProvider::from_str(&captures["cloud_provider"])?,
1300 cloud_provider_region: captures["cloud_provider_region"].into(),
1301 organization_id: captures["organization_id"]
1302 .parse()
1303 .map_err(|_| InvalidEnvironmentIdError)?,
1304 ordinal: captures["ordinal"]
1305 .parse()
1306 .map_err(|_| InvalidEnvironmentIdError)?,
1307 })
1308 }
1309}
1310
1311#[derive(Debug, Clone, PartialEq)]
1313pub struct InvalidEnvironmentIdError;
1314
1315impl fmt::Display for InvalidEnvironmentIdError {
1316 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1317 f.write_str("invalid environment ID")
1318 }
1319}
1320
1321impl Error for InvalidEnvironmentIdError {}
1322
1323impl From<InvalidCloudProviderError> for InvalidEnvironmentIdError {
1324 fn from(_: InvalidCloudProviderError) -> Self {
1325 InvalidEnvironmentIdError
1326 }
1327}
1328
1329#[derive(Clone, Debug, Eq, PartialEq)]
1331pub enum CatalogError {
1332 UnknownDatabase(String),
1334 DatabaseAlreadyExists(String),
1336 UnknownSchema(String),
1338 SchemaAlreadyExists(String),
1340 UnknownRole(String),
1342 RoleAlreadyExists(String),
1344 NetworkPolicyAlreadyExists(String),
1346 UnknownCluster(String),
1348 UnexpectedBuiltinCluster(String),
1350 UnexpectedBuiltinClusterType(String),
1352 ClusterAlreadyExists(String),
1354 UnknownClusterReplica(String),
1356 UnknownClusterReplicaSize(String),
1358 DuplicateReplica(String, String),
1360 UnknownItem(String),
1362 ItemAlreadyExists(CatalogItemId, String),
1364 UnknownFunction {
1366 name: String,
1368 alternative: Option<String>,
1370 },
1371 UnknownType {
1373 name: String,
1375 },
1376 UnknownConnection(String),
1378 UnknownNetworkPolicy(String),
1380 UnexpectedType {
1382 name: String,
1384 actual_type: CatalogItemType,
1386 expected_type: CatalogItemType,
1388 },
1389 InvalidDependency {
1391 name: String,
1393 typ: CatalogItemType,
1395 },
1396 IdExhaustion,
1398 OidExhaustion,
1400 TimelineAlreadyExists(String),
1402 IdAllocatorAlreadyExists(String),
1404 ConfigAlreadyExists(String),
1406 FailedBuiltinSchemaMigration(String),
1408 StorageCollectionMetadataAlreadyExists(GlobalId),
1410}
1411
1412impl fmt::Display for CatalogError {
1413 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1414 match self {
1415 Self::UnknownDatabase(name) => write!(f, "unknown database '{}'", name),
1416 Self::DatabaseAlreadyExists(name) => write!(f, "database '{name}' already exists"),
1417 Self::UnknownFunction { name, .. } => write!(f, "function \"{}\" does not exist", name),
1418 Self::UnknownType { name, .. } => write!(f, "type \"{}\" does not exist", name),
1419 Self::UnknownConnection(name) => write!(f, "connection \"{}\" does not exist", name),
1420 Self::UnknownSchema(name) => write!(f, "unknown schema '{}'", name),
1421 Self::SchemaAlreadyExists(name) => write!(f, "schema '{name}' already exists"),
1422 Self::UnknownRole(name) => write!(f, "unknown role '{}'", name),
1423 Self::RoleAlreadyExists(name) => write!(f, "role '{name}' already exists"),
1424 Self::NetworkPolicyAlreadyExists(name) => {
1425 write!(f, "network policy '{name}' already exists")
1426 }
1427 Self::UnknownCluster(name) => write!(f, "unknown cluster '{}'", name),
1428 Self::UnknownNetworkPolicy(name) => write!(f, "unknown network policy '{}'", name),
1429 Self::UnexpectedBuiltinCluster(name) => {
1430 write!(f, "Unexpected builtin cluster '{}'", name)
1431 }
1432 Self::UnexpectedBuiltinClusterType(name) => {
1433 write!(f, "Unexpected builtin cluster type'{}'", name)
1434 }
1435 Self::ClusterAlreadyExists(name) => write!(f, "cluster '{name}' already exists"),
1436 Self::UnknownClusterReplica(name) => {
1437 write!(f, "unknown cluster replica '{}'", name)
1438 }
1439 Self::UnknownClusterReplicaSize(name) => {
1440 write!(f, "unknown cluster replica size '{}'", name)
1441 }
1442 Self::DuplicateReplica(replica_name, cluster_name) => write!(
1443 f,
1444 "cannot create multiple replicas named '{replica_name}' on cluster '{cluster_name}'"
1445 ),
1446 Self::UnknownItem(name) => write!(f, "unknown catalog item '{}'", name),
1447 Self::ItemAlreadyExists(_gid, name) => {
1448 write!(f, "catalog item '{name}' already exists")
1449 }
1450 Self::UnexpectedType {
1451 name,
1452 actual_type,
1453 expected_type,
1454 } => {
1455 write!(f, "\"{name}\" is a {actual_type} not a {expected_type}")
1456 }
1457 Self::InvalidDependency { name, typ } => write!(
1458 f,
1459 "catalog item '{}' is {} {} and so cannot be depended upon",
1460 name,
1461 if matches!(typ, CatalogItemType::Index) {
1462 "an"
1463 } else {
1464 "a"
1465 },
1466 typ,
1467 ),
1468 Self::IdExhaustion => write!(f, "id counter overflows i64"),
1469 Self::OidExhaustion => write!(f, "oid counter overflows u32"),
1470 Self::TimelineAlreadyExists(name) => write!(f, "timeline '{name}' already exists"),
1471 Self::IdAllocatorAlreadyExists(name) => {
1472 write!(f, "ID allocator '{name}' already exists")
1473 }
1474 Self::ConfigAlreadyExists(key) => write!(f, "config '{key}' already exists"),
1475 Self::FailedBuiltinSchemaMigration(objects) => {
1476 write!(f, "failed to migrate schema of builtin objects: {objects}")
1477 }
1478 Self::StorageCollectionMetadataAlreadyExists(key) => {
1479 write!(f, "storage metadata for '{key}' already exists")
1480 }
1481 }
1482 }
1483}
1484
1485impl CatalogError {
1486 pub fn hint(&self) -> Option<String> {
1488 match self {
1489 CatalogError::UnknownFunction { alternative, .. } => {
1490 match alternative {
1491 None => Some("No function matches the given name and argument types. You might need to add explicit type casts.".into()),
1492 Some(alt) => Some(format!("Try using {alt}")),
1493 }
1494 }
1495 _ => None,
1496 }
1497 }
1498}
1499
1500impl Error for CatalogError {}
1501
1502#[allow(missing_docs)]
1504#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Deserialize, Serialize)]
1505pub enum ObjectType {
1507 Table,
1508 View,
1509 MaterializedView,
1510 Source,
1511 Sink,
1512 Index,
1513 Type,
1514 Role,
1515 Cluster,
1516 ClusterReplica,
1517 Secret,
1518 Connection,
1519 Database,
1520 Schema,
1521 Func,
1522 ContinualTask,
1523 NetworkPolicy,
1524}
1525
1526impl ObjectType {
1527 pub fn is_relation(&self) -> bool {
1529 match self {
1530 ObjectType::Table
1531 | ObjectType::View
1532 | ObjectType::MaterializedView
1533 | ObjectType::Source
1534 | ObjectType::ContinualTask => true,
1535 ObjectType::Sink
1536 | ObjectType::Index
1537 | ObjectType::Type
1538 | ObjectType::Secret
1539 | ObjectType::Connection
1540 | ObjectType::Func
1541 | ObjectType::Database
1542 | ObjectType::Schema
1543 | ObjectType::Cluster
1544 | ObjectType::ClusterReplica
1545 | ObjectType::Role
1546 | ObjectType::NetworkPolicy => false,
1547 }
1548 }
1549}
1550
1551impl From<mz_sql_parser::ast::ObjectType> for ObjectType {
1552 fn from(value: mz_sql_parser::ast::ObjectType) -> Self {
1553 match value {
1554 mz_sql_parser::ast::ObjectType::Table => ObjectType::Table,
1555 mz_sql_parser::ast::ObjectType::View => ObjectType::View,
1556 mz_sql_parser::ast::ObjectType::MaterializedView => ObjectType::MaterializedView,
1557 mz_sql_parser::ast::ObjectType::Source => ObjectType::Source,
1558 mz_sql_parser::ast::ObjectType::Subsource => ObjectType::Source,
1559 mz_sql_parser::ast::ObjectType::Sink => ObjectType::Sink,
1560 mz_sql_parser::ast::ObjectType::Index => ObjectType::Index,
1561 mz_sql_parser::ast::ObjectType::Type => ObjectType::Type,
1562 mz_sql_parser::ast::ObjectType::Role => ObjectType::Role,
1563 mz_sql_parser::ast::ObjectType::Cluster => ObjectType::Cluster,
1564 mz_sql_parser::ast::ObjectType::ClusterReplica => ObjectType::ClusterReplica,
1565 mz_sql_parser::ast::ObjectType::Secret => ObjectType::Secret,
1566 mz_sql_parser::ast::ObjectType::Connection => ObjectType::Connection,
1567 mz_sql_parser::ast::ObjectType::Database => ObjectType::Database,
1568 mz_sql_parser::ast::ObjectType::Schema => ObjectType::Schema,
1569 mz_sql_parser::ast::ObjectType::Func => ObjectType::Func,
1570 mz_sql_parser::ast::ObjectType::ContinualTask => ObjectType::ContinualTask,
1571 mz_sql_parser::ast::ObjectType::NetworkPolicy => ObjectType::NetworkPolicy,
1572 }
1573 }
1574}
1575
1576impl From<CommentObjectId> for ObjectType {
1577 fn from(value: CommentObjectId) -> ObjectType {
1578 match value {
1579 CommentObjectId::Table(_) => ObjectType::Table,
1580 CommentObjectId::View(_) => ObjectType::View,
1581 CommentObjectId::MaterializedView(_) => ObjectType::MaterializedView,
1582 CommentObjectId::Source(_) => ObjectType::Source,
1583 CommentObjectId::Sink(_) => ObjectType::Sink,
1584 CommentObjectId::Index(_) => ObjectType::Index,
1585 CommentObjectId::Func(_) => ObjectType::Func,
1586 CommentObjectId::Connection(_) => ObjectType::Connection,
1587 CommentObjectId::Type(_) => ObjectType::Type,
1588 CommentObjectId::Secret(_) => ObjectType::Secret,
1589 CommentObjectId::Role(_) => ObjectType::Role,
1590 CommentObjectId::Database(_) => ObjectType::Database,
1591 CommentObjectId::Schema(_) => ObjectType::Schema,
1592 CommentObjectId::Cluster(_) => ObjectType::Cluster,
1593 CommentObjectId::ClusterReplica(_) => ObjectType::ClusterReplica,
1594 CommentObjectId::ContinualTask(_) => ObjectType::ContinualTask,
1595 CommentObjectId::NetworkPolicy(_) => ObjectType::NetworkPolicy,
1596 }
1597 }
1598}
1599
1600impl Display for ObjectType {
1601 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1602 f.write_str(match self {
1603 ObjectType::Table => "TABLE",
1604 ObjectType::View => "VIEW",
1605 ObjectType::MaterializedView => "MATERIALIZED VIEW",
1606 ObjectType::Source => "SOURCE",
1607 ObjectType::Sink => "SINK",
1608 ObjectType::Index => "INDEX",
1609 ObjectType::Type => "TYPE",
1610 ObjectType::Role => "ROLE",
1611 ObjectType::Cluster => "CLUSTER",
1612 ObjectType::ClusterReplica => "CLUSTER REPLICA",
1613 ObjectType::Secret => "SECRET",
1614 ObjectType::Connection => "CONNECTION",
1615 ObjectType::Database => "DATABASE",
1616 ObjectType::Schema => "SCHEMA",
1617 ObjectType::Func => "FUNCTION",
1618 ObjectType::ContinualTask => "CONTINUAL TASK",
1619 ObjectType::NetworkPolicy => "NETWORK POLICY",
1620 })
1621 }
1622}
1623
1624#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Deserialize, Serialize)]
1625pub enum SystemObjectType {
1627 Object(ObjectType),
1629 System,
1631}
1632
1633impl SystemObjectType {
1634 pub fn is_relation(&self) -> bool {
1636 match self {
1637 SystemObjectType::Object(object_type) => object_type.is_relation(),
1638 SystemObjectType::System => false,
1639 }
1640 }
1641}
1642
1643impl Display for SystemObjectType {
1644 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1645 match self {
1646 SystemObjectType::Object(object_type) => std::fmt::Display::fmt(&object_type, f),
1647 SystemObjectType::System => f.write_str("SYSTEM"),
1648 }
1649 }
1650}
1651
1652#[derive(Debug, Clone, PartialEq, Eq)]
1654pub enum ErrorMessageObjectDescription {
1655 Object {
1657 object_type: ObjectType,
1659 object_name: Option<String>,
1661 },
1662 System,
1664}
1665
1666impl ErrorMessageObjectDescription {
1667 pub fn from_id(
1669 object_id: &ObjectId,
1670 catalog: &dyn SessionCatalog,
1671 ) -> ErrorMessageObjectDescription {
1672 let object_name = match object_id {
1673 ObjectId::Cluster(cluster_id) => catalog.get_cluster(*cluster_id).name().to_string(),
1674 ObjectId::ClusterReplica((cluster_id, replica_id)) => catalog
1675 .get_cluster_replica(*cluster_id, *replica_id)
1676 .name()
1677 .to_string(),
1678 ObjectId::Database(database_id) => catalog.get_database(database_id).name().to_string(),
1679 ObjectId::Schema((database_spec, schema_spec)) => {
1680 let name = catalog.get_schema(database_spec, schema_spec).name();
1681 catalog.resolve_full_schema_name(name).to_string()
1682 }
1683 ObjectId::Role(role_id) => catalog.get_role(role_id).name().to_string(),
1684 ObjectId::Item(id) => {
1685 let name = catalog.get_item(id).name();
1686 catalog.resolve_full_name(name).to_string()
1687 }
1688 ObjectId::NetworkPolicy(network_policy_id) => catalog
1689 .get_network_policy(network_policy_id)
1690 .name()
1691 .to_string(),
1692 };
1693 ErrorMessageObjectDescription::Object {
1694 object_type: catalog.get_object_type(object_id),
1695 object_name: Some(object_name),
1696 }
1697 }
1698
1699 pub fn from_sys_id(
1701 object_id: &SystemObjectId,
1702 catalog: &dyn SessionCatalog,
1703 ) -> ErrorMessageObjectDescription {
1704 match object_id {
1705 SystemObjectId::Object(object_id) => {
1706 ErrorMessageObjectDescription::from_id(object_id, catalog)
1707 }
1708 SystemObjectId::System => ErrorMessageObjectDescription::System,
1709 }
1710 }
1711
1712 pub fn from_object_type(object_type: SystemObjectType) -> ErrorMessageObjectDescription {
1714 match object_type {
1715 SystemObjectType::Object(object_type) => ErrorMessageObjectDescription::Object {
1716 object_type,
1717 object_name: None,
1718 },
1719 SystemObjectType::System => ErrorMessageObjectDescription::System,
1720 }
1721 }
1722}
1723
1724impl Display for ErrorMessageObjectDescription {
1725 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1726 match self {
1727 ErrorMessageObjectDescription::Object {
1728 object_type,
1729 object_name,
1730 } => {
1731 let object_name = object_name
1732 .as_ref()
1733 .map(|object_name| format!(" {}", object_name.quoted()))
1734 .unwrap_or_else(|| "".to_string());
1735 write!(f, "{object_type}{object_name}")
1736 }
1737 ErrorMessageObjectDescription::System => f.write_str("SYSTEM"),
1738 }
1739 }
1740}
1741
1742#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
1743#[serde(into = "BTreeMap<String, RoleId>")]
1746#[serde(try_from = "BTreeMap<String, RoleId>")]
1747pub struct RoleMembership {
1749 pub map: BTreeMap<RoleId, RoleId>,
1755}
1756
1757impl RoleMembership {
1758 pub fn new() -> RoleMembership {
1760 RoleMembership {
1761 map: BTreeMap::new(),
1762 }
1763 }
1764}
1765
1766impl From<RoleMembership> for BTreeMap<String, RoleId> {
1767 fn from(value: RoleMembership) -> Self {
1768 value
1769 .map
1770 .into_iter()
1771 .map(|(k, v)| (k.to_string(), v))
1772 .collect()
1773 }
1774}
1775
1776impl TryFrom<BTreeMap<String, RoleId>> for RoleMembership {
1777 type Error = anyhow::Error;
1778
1779 fn try_from(value: BTreeMap<String, RoleId>) -> Result<Self, Self::Error> {
1780 Ok(RoleMembership {
1781 map: value
1782 .into_iter()
1783 .map(|(k, v)| Ok((RoleId::from_str(&k)?, v)))
1784 .collect::<Result<_, anyhow::Error>>()?,
1785 })
1786 }
1787}
1788
1789#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1791pub struct DefaultPrivilegeObject {
1792 pub role_id: RoleId,
1794 pub database_id: Option<DatabaseId>,
1796 pub schema_id: Option<SchemaId>,
1798 pub object_type: ObjectType,
1800}
1801
1802impl DefaultPrivilegeObject {
1803 pub fn new(
1805 role_id: RoleId,
1806 database_id: Option<DatabaseId>,
1807 schema_id: Option<SchemaId>,
1808 object_type: ObjectType,
1809 ) -> DefaultPrivilegeObject {
1810 DefaultPrivilegeObject {
1811 role_id,
1812 database_id,
1813 schema_id,
1814 object_type,
1815 }
1816 }
1817}
1818
1819impl std::fmt::Display for DefaultPrivilegeObject {
1820 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1821 write!(f, "{self:?}")
1823 }
1824}
1825
1826#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1828pub struct DefaultPrivilegeAclItem {
1829 pub grantee: RoleId,
1831 pub acl_mode: AclMode,
1833}
1834
1835impl DefaultPrivilegeAclItem {
1836 pub fn new(grantee: RoleId, acl_mode: AclMode) -> DefaultPrivilegeAclItem {
1838 DefaultPrivilegeAclItem { grantee, acl_mode }
1839 }
1840
1841 pub fn mz_acl_item(self, grantor: RoleId) -> MzAclItem {
1843 MzAclItem {
1844 grantee: self.grantee,
1845 grantor,
1846 acl_mode: self.acl_mode,
1847 }
1848 }
1849}
1850
1851#[derive(Debug, Clone)]
1857pub struct BuiltinsConfig {
1858 pub include_continual_tasks: bool,
1860}
1861
1862#[cfg(test)]
1863mod tests {
1864 use super::{CloudProvider, EnvironmentId, InvalidEnvironmentIdError};
1865
1866 #[mz_ore::test]
1867 fn test_environment_id() {
1868 for (input, expected) in [
1869 (
1870 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1871 Ok(EnvironmentId {
1872 cloud_provider: CloudProvider::Local,
1873 cloud_provider_region: "az1".into(),
1874 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1875 ordinal: 452,
1876 }),
1877 ),
1878 (
1879 "aws-us-east-1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1880 Ok(EnvironmentId {
1881 cloud_provider: CloudProvider::Aws,
1882 cloud_provider_region: "us-east-1".into(),
1883 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1884 ordinal: 0,
1885 }),
1886 ),
1887 (
1888 "gcp-us-central1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1889 Ok(EnvironmentId {
1890 cloud_provider: CloudProvider::Gcp,
1891 cloud_provider_region: "us-central1".into(),
1892 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1893 ordinal: 0,
1894 }),
1895 ),
1896 (
1897 "azure-australiaeast-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1898 Ok(EnvironmentId {
1899 cloud_provider: CloudProvider::Azure,
1900 cloud_provider_region: "australiaeast".into(),
1901 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1902 ordinal: 0,
1903 }),
1904 ),
1905 (
1906 "generic-moon-station-11-darkside-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1907 Ok(EnvironmentId {
1908 cloud_provider: CloudProvider::Generic,
1909 cloud_provider_region: "moon-station-11-darkside".into(),
1910 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1911 ordinal: 0,
1912 }),
1913 ),
1914 ("", Err(InvalidEnvironmentIdError)),
1915 (
1916 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-123456789",
1917 Err(InvalidEnvironmentIdError),
1918 ),
1919 (
1920 "local-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1921 Err(InvalidEnvironmentIdError),
1922 ),
1923 (
1924 "local-az1-1497a3b7-a455-4fc48752-b44a94b5f90a-452",
1925 Err(InvalidEnvironmentIdError),
1926 ),
1927 ] {
1928 let actual = input.parse();
1929 assert_eq!(expected, actual, "input = {}", input);
1930 if let Ok(actual) = actual {
1931 assert_eq!(input, actual.to_string(), "input = {}", input);
1932 }
1933 }
1934 }
1935}