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 type_details(&self) -> Option<&CatalogTypeDetails<IdReference>>;
842
843 fn owner_id(&self) -> RoleId;
845
846 fn privileges(&self) -> &PrivilegeMap;
848
849 fn cluster_id(&self) -> Option<ClusterId>;
851
852 fn at_version(&self, version: RelationVersionSelector) -> Box<dyn CatalogCollectionItem>;
855
856 fn latest_version(&self) -> Option<RelationVersion>;
858}
859
860pub trait CatalogCollectionItem: CatalogItem + Send + Sync {
863 fn desc(&self, name: &FullItemName) -> Result<Cow<'_, RelationDesc>, CatalogError>;
868
869 fn global_id(&self) -> GlobalId;
871}
872
873#[derive(Debug, Deserialize, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
875pub enum CatalogItemType {
876 Table,
878 Source,
880 Sink,
882 View,
884 MaterializedView,
886 Index,
888 Type,
890 Func,
892 Secret,
894 Connection,
896 ContinualTask,
898}
899
900impl CatalogItemType {
901 pub fn conflicts_with_type(&self) -> bool {
920 match self {
921 CatalogItemType::Table => true,
922 CatalogItemType::Source => true,
923 CatalogItemType::View => true,
924 CatalogItemType::MaterializedView => true,
925 CatalogItemType::Index => true,
926 CatalogItemType::Type => true,
927 CatalogItemType::Sink => false,
928 CatalogItemType::Func => false,
929 CatalogItemType::Secret => false,
930 CatalogItemType::Connection => false,
931 CatalogItemType::ContinualTask => true,
932 }
933 }
934}
935
936impl fmt::Display for CatalogItemType {
937 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
938 match self {
939 CatalogItemType::Table => f.write_str("table"),
940 CatalogItemType::Source => f.write_str("source"),
941 CatalogItemType::Sink => f.write_str("sink"),
942 CatalogItemType::View => f.write_str("view"),
943 CatalogItemType::MaterializedView => f.write_str("materialized view"),
944 CatalogItemType::Index => f.write_str("index"),
945 CatalogItemType::Type => f.write_str("type"),
946 CatalogItemType::Func => f.write_str("func"),
947 CatalogItemType::Secret => f.write_str("secret"),
948 CatalogItemType::Connection => f.write_str("connection"),
949 CatalogItemType::ContinualTask => f.write_str("continual task"),
950 }
951 }
952}
953
954impl From<CatalogItemType> for ObjectType {
955 fn from(value: CatalogItemType) -> Self {
956 match value {
957 CatalogItemType::Table => ObjectType::Table,
958 CatalogItemType::Source => ObjectType::Source,
959 CatalogItemType::Sink => ObjectType::Sink,
960 CatalogItemType::View => ObjectType::View,
961 CatalogItemType::MaterializedView => ObjectType::MaterializedView,
962 CatalogItemType::Index => ObjectType::Index,
963 CatalogItemType::Type => ObjectType::Type,
964 CatalogItemType::Func => ObjectType::Func,
965 CatalogItemType::Secret => ObjectType::Secret,
966 CatalogItemType::Connection => ObjectType::Connection,
967 CatalogItemType::ContinualTask => ObjectType::ContinualTask,
968 }
969 }
970}
971
972impl From<CatalogItemType> for mz_audit_log::ObjectType {
973 fn from(value: CatalogItemType) -> Self {
974 match value {
975 CatalogItemType::Table => mz_audit_log::ObjectType::Table,
976 CatalogItemType::Source => mz_audit_log::ObjectType::Source,
977 CatalogItemType::View => mz_audit_log::ObjectType::View,
978 CatalogItemType::MaterializedView => mz_audit_log::ObjectType::MaterializedView,
979 CatalogItemType::Index => mz_audit_log::ObjectType::Index,
980 CatalogItemType::Type => mz_audit_log::ObjectType::Type,
981 CatalogItemType::Sink => mz_audit_log::ObjectType::Sink,
982 CatalogItemType::Func => mz_audit_log::ObjectType::Func,
983 CatalogItemType::Secret => mz_audit_log::ObjectType::Secret,
984 CatalogItemType::Connection => mz_audit_log::ObjectType::Connection,
985 CatalogItemType::ContinualTask => mz_audit_log::ObjectType::ContinualTask,
986 }
987 }
988}
989
990#[derive(Clone, Debug, Eq, PartialEq)]
992pub struct CatalogTypeDetails<T: TypeReference> {
993 pub array_id: Option<CatalogItemId>,
995 pub typ: CatalogType<T>,
997 pub pg_metadata: Option<CatalogTypePgMetadata>,
999}
1000
1001#[derive(Clone, Debug, Eq, PartialEq)]
1003pub struct CatalogTypePgMetadata {
1004 pub typinput_oid: u32,
1006 pub typreceive_oid: u32,
1008}
1009
1010pub trait TypeReference {
1012 type Reference: Clone + Debug + Eq + PartialEq;
1014}
1015
1016#[derive(Clone, Debug, Eq, PartialEq)]
1018pub struct NameReference;
1019
1020impl TypeReference for NameReference {
1021 type Reference = &'static str;
1022}
1023
1024#[derive(Clone, Debug, Eq, PartialEq)]
1026pub struct IdReference;
1027
1028impl TypeReference for IdReference {
1029 type Reference = CatalogItemId;
1030}
1031
1032#[allow(missing_docs)]
1038#[derive(Clone, Debug, Eq, PartialEq)]
1039pub enum CatalogType<T: TypeReference> {
1040 AclItem,
1041 Array {
1042 element_reference: T::Reference,
1043 },
1044 Bool,
1045 Bytes,
1046 Char,
1047 Date,
1048 Float32,
1049 Float64,
1050 Int16,
1051 Int32,
1052 Int64,
1053 UInt16,
1054 UInt32,
1055 UInt64,
1056 MzTimestamp,
1057 Interval,
1058 Jsonb,
1059 List {
1060 element_reference: T::Reference,
1061 element_modifiers: Vec<i64>,
1062 },
1063 Map {
1064 key_reference: T::Reference,
1065 key_modifiers: Vec<i64>,
1066 value_reference: T::Reference,
1067 value_modifiers: Vec<i64>,
1068 },
1069 Numeric,
1070 Oid,
1071 PgLegacyChar,
1072 PgLegacyName,
1073 Pseudo,
1074 Range {
1075 element_reference: T::Reference,
1076 },
1077 Record {
1078 fields: Vec<CatalogRecordField<T>>,
1079 },
1080 RegClass,
1081 RegProc,
1082 RegType,
1083 String,
1084 Time,
1085 Timestamp,
1086 TimestampTz,
1087 Uuid,
1088 VarChar,
1089 Int2Vector,
1090 MzAclItem,
1091}
1092
1093impl CatalogType<IdReference> {
1094 pub fn desc(&self, catalog: &dyn SessionCatalog) -> Result<Option<RelationDesc>, PlanError> {
1097 match &self {
1098 CatalogType::Record { fields } => {
1099 let mut desc = RelationDesc::builder();
1100 for f in fields {
1101 let name = f.name.clone();
1102 let ty = query::scalar_type_from_catalog(
1103 catalog,
1104 f.type_reference,
1105 &f.type_modifiers,
1106 )?;
1107 let ty = ty.nullable(true);
1110 desc = desc.with_column(name, ty);
1111 }
1112 Ok(Some(desc.finish()))
1113 }
1114 _ => Ok(None),
1115 }
1116 }
1117}
1118
1119#[derive(Clone, Debug, Eq, PartialEq)]
1121pub struct CatalogRecordField<T: TypeReference> {
1122 pub name: ColumnName,
1124 pub type_reference: T::Reference,
1126 pub type_modifiers: Vec<i64>,
1128}
1129
1130#[derive(Clone, Debug, Eq, PartialEq)]
1131pub enum TypeCategory {
1139 Array,
1141 BitString,
1143 Boolean,
1145 Composite,
1147 DateTime,
1149 Enum,
1151 Geometric,
1153 List,
1155 NetworkAddress,
1157 Numeric,
1159 Pseudo,
1161 Range,
1163 String,
1165 Timespan,
1167 UserDefined,
1169 Unknown,
1171}
1172
1173impl fmt::Display for TypeCategory {
1174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1175 f.write_str(match self {
1176 TypeCategory::Array => "array",
1177 TypeCategory::BitString => "bit-string",
1178 TypeCategory::Boolean => "boolean",
1179 TypeCategory::Composite => "composite",
1180 TypeCategory::DateTime => "date-time",
1181 TypeCategory::Enum => "enum",
1182 TypeCategory::Geometric => "geometric",
1183 TypeCategory::List => "list",
1184 TypeCategory::NetworkAddress => "network-address",
1185 TypeCategory::Numeric => "numeric",
1186 TypeCategory::Pseudo => "pseudo",
1187 TypeCategory::Range => "range",
1188 TypeCategory::String => "string",
1189 TypeCategory::Timespan => "timespan",
1190 TypeCategory::UserDefined => "user-defined",
1191 TypeCategory::Unknown => "unknown",
1192 })
1193 }
1194}
1195
1196#[derive(Debug, Clone, PartialEq)]
1220pub struct EnvironmentId {
1221 cloud_provider: CloudProvider,
1222 cloud_provider_region: String,
1223 organization_id: Uuid,
1224 ordinal: u64,
1225}
1226
1227impl EnvironmentId {
1228 pub fn for_tests() -> EnvironmentId {
1230 EnvironmentId {
1231 cloud_provider: CloudProvider::Local,
1232 cloud_provider_region: "az1".into(),
1233 organization_id: Uuid::new_v4(),
1234 ordinal: 0,
1235 }
1236 }
1237
1238 pub fn cloud_provider(&self) -> &CloudProvider {
1240 &self.cloud_provider
1241 }
1242
1243 pub fn cloud_provider_region(&self) -> &str {
1245 &self.cloud_provider_region
1246 }
1247
1248 pub fn region(&self) -> String {
1253 format!("{}/{}", self.cloud_provider, self.cloud_provider_region)
1254 }
1255
1256 pub fn organization_id(&self) -> Uuid {
1258 self.organization_id
1259 }
1260
1261 pub fn ordinal(&self) -> u64 {
1263 self.ordinal
1264 }
1265}
1266
1267impl fmt::Display for EnvironmentId {
1273 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1274 write!(
1275 f,
1276 "{}-{}-{}-{}",
1277 self.cloud_provider, self.cloud_provider_region, self.organization_id, self.ordinal
1278 )
1279 }
1280}
1281
1282impl FromStr for EnvironmentId {
1283 type Err = InvalidEnvironmentIdError;
1284
1285 fn from_str(s: &str) -> Result<EnvironmentId, InvalidEnvironmentIdError> {
1286 static MATCHER: LazyLock<Regex> = LazyLock::new(|| {
1287 Regex::new(
1288 "^(?P<cloud_provider>[[:alnum:]]+)-\
1289 (?P<cloud_provider_region>[[:alnum:]\\-]+)-\
1290 (?P<organization_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-\
1291 (?P<ordinal>\\d{1,8})$"
1292 ).unwrap()
1293 });
1294 let captures = MATCHER.captures(s).ok_or(InvalidEnvironmentIdError)?;
1295 Ok(EnvironmentId {
1296 cloud_provider: CloudProvider::from_str(&captures["cloud_provider"])?,
1297 cloud_provider_region: captures["cloud_provider_region"].into(),
1298 organization_id: captures["organization_id"]
1299 .parse()
1300 .map_err(|_| InvalidEnvironmentIdError)?,
1301 ordinal: captures["ordinal"]
1302 .parse()
1303 .map_err(|_| InvalidEnvironmentIdError)?,
1304 })
1305 }
1306}
1307
1308#[derive(Debug, Clone, PartialEq)]
1310pub struct InvalidEnvironmentIdError;
1311
1312impl fmt::Display for InvalidEnvironmentIdError {
1313 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1314 f.write_str("invalid environment ID")
1315 }
1316}
1317
1318impl Error for InvalidEnvironmentIdError {}
1319
1320impl From<InvalidCloudProviderError> for InvalidEnvironmentIdError {
1321 fn from(_: InvalidCloudProviderError) -> Self {
1322 InvalidEnvironmentIdError
1323 }
1324}
1325
1326#[derive(Clone, Debug, Eq, PartialEq)]
1328pub enum CatalogError {
1329 UnknownDatabase(String),
1331 DatabaseAlreadyExists(String),
1333 UnknownSchema(String),
1335 SchemaAlreadyExists(String),
1337 UnknownRole(String),
1339 RoleAlreadyExists(String),
1341 NetworkPolicyAlreadyExists(String),
1343 UnknownCluster(String),
1345 UnexpectedBuiltinCluster(String),
1347 UnexpectedBuiltinClusterType(String),
1349 ClusterAlreadyExists(String),
1351 UnknownClusterReplica(String),
1353 UnknownClusterReplicaSize(String),
1355 DuplicateReplica(String, String),
1357 UnknownItem(String),
1359 ItemAlreadyExists(CatalogItemId, String),
1361 UnknownFunction {
1363 name: String,
1365 alternative: Option<String>,
1367 },
1368 UnknownType {
1370 name: String,
1372 },
1373 UnknownConnection(String),
1375 UnknownNetworkPolicy(String),
1377 UnexpectedType {
1379 name: String,
1381 actual_type: CatalogItemType,
1383 expected_type: CatalogItemType,
1385 },
1386 InvalidDependency {
1388 name: String,
1390 typ: CatalogItemType,
1392 },
1393 IdExhaustion,
1395 OidExhaustion,
1397 TimelineAlreadyExists(String),
1399 IdAllocatorAlreadyExists(String),
1401 ConfigAlreadyExists(String),
1403 FailedBuiltinSchemaMigration(String),
1405 StorageCollectionMetadataAlreadyExists(GlobalId),
1407}
1408
1409impl fmt::Display for CatalogError {
1410 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1411 match self {
1412 Self::UnknownDatabase(name) => write!(f, "unknown database '{}'", name),
1413 Self::DatabaseAlreadyExists(name) => write!(f, "database '{name}' already exists"),
1414 Self::UnknownFunction { name, .. } => write!(f, "function \"{}\" does not exist", name),
1415 Self::UnknownType { name, .. } => write!(f, "type \"{}\" does not exist", name),
1416 Self::UnknownConnection(name) => write!(f, "connection \"{}\" does not exist", name),
1417 Self::UnknownSchema(name) => write!(f, "unknown schema '{}'", name),
1418 Self::SchemaAlreadyExists(name) => write!(f, "schema '{name}' already exists"),
1419 Self::UnknownRole(name) => write!(f, "unknown role '{}'", name),
1420 Self::RoleAlreadyExists(name) => write!(f, "role '{name}' already exists"),
1421 Self::NetworkPolicyAlreadyExists(name) => {
1422 write!(f, "network policy '{name}' already exists")
1423 }
1424 Self::UnknownCluster(name) => write!(f, "unknown cluster '{}'", name),
1425 Self::UnknownNetworkPolicy(name) => write!(f, "unknown network policy '{}'", name),
1426 Self::UnexpectedBuiltinCluster(name) => {
1427 write!(f, "Unexpected builtin cluster '{}'", name)
1428 }
1429 Self::UnexpectedBuiltinClusterType(name) => {
1430 write!(f, "Unexpected builtin cluster type'{}'", name)
1431 }
1432 Self::ClusterAlreadyExists(name) => write!(f, "cluster '{name}' already exists"),
1433 Self::UnknownClusterReplica(name) => {
1434 write!(f, "unknown cluster replica '{}'", name)
1435 }
1436 Self::UnknownClusterReplicaSize(name) => {
1437 write!(f, "unknown cluster replica size '{}'", name)
1438 }
1439 Self::DuplicateReplica(replica_name, cluster_name) => write!(
1440 f,
1441 "cannot create multiple replicas named '{replica_name}' on cluster '{cluster_name}'"
1442 ),
1443 Self::UnknownItem(name) => write!(f, "unknown catalog item '{}'", name),
1444 Self::ItemAlreadyExists(_gid, name) => {
1445 write!(f, "catalog item '{name}' already exists")
1446 }
1447 Self::UnexpectedType {
1448 name,
1449 actual_type,
1450 expected_type,
1451 } => {
1452 write!(f, "\"{name}\" is a {actual_type} not a {expected_type}")
1453 }
1454 Self::InvalidDependency { name, typ } => write!(
1455 f,
1456 "catalog item '{}' is {} {} and so cannot be depended upon",
1457 name,
1458 if matches!(typ, CatalogItemType::Index) {
1459 "an"
1460 } else {
1461 "a"
1462 },
1463 typ,
1464 ),
1465 Self::IdExhaustion => write!(f, "id counter overflows i64"),
1466 Self::OidExhaustion => write!(f, "oid counter overflows u32"),
1467 Self::TimelineAlreadyExists(name) => write!(f, "timeline '{name}' already exists"),
1468 Self::IdAllocatorAlreadyExists(name) => {
1469 write!(f, "ID allocator '{name}' already exists")
1470 }
1471 Self::ConfigAlreadyExists(key) => write!(f, "config '{key}' already exists"),
1472 Self::FailedBuiltinSchemaMigration(objects) => {
1473 write!(f, "failed to migrate schema of builtin objects: {objects}")
1474 }
1475 Self::StorageCollectionMetadataAlreadyExists(key) => {
1476 write!(f, "storage metadata for '{key}' already exists")
1477 }
1478 }
1479 }
1480}
1481
1482impl CatalogError {
1483 pub fn hint(&self) -> Option<String> {
1485 match self {
1486 CatalogError::UnknownFunction { alternative, .. } => {
1487 match alternative {
1488 None => Some("No function matches the given name and argument types. You might need to add explicit type casts.".into()),
1489 Some(alt) => Some(format!("Try using {alt}")),
1490 }
1491 }
1492 _ => None,
1493 }
1494 }
1495}
1496
1497impl Error for CatalogError {}
1498
1499#[allow(missing_docs)]
1501#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Deserialize, Serialize)]
1502pub enum ObjectType {
1504 Table,
1505 View,
1506 MaterializedView,
1507 Source,
1508 Sink,
1509 Index,
1510 Type,
1511 Role,
1512 Cluster,
1513 ClusterReplica,
1514 Secret,
1515 Connection,
1516 Database,
1517 Schema,
1518 Func,
1519 ContinualTask,
1520 NetworkPolicy,
1521}
1522
1523impl ObjectType {
1524 pub fn is_relation(&self) -> bool {
1526 match self {
1527 ObjectType::Table
1528 | ObjectType::View
1529 | ObjectType::MaterializedView
1530 | ObjectType::Source
1531 | ObjectType::ContinualTask => true,
1532 ObjectType::Sink
1533 | ObjectType::Index
1534 | ObjectType::Type
1535 | ObjectType::Secret
1536 | ObjectType::Connection
1537 | ObjectType::Func
1538 | ObjectType::Database
1539 | ObjectType::Schema
1540 | ObjectType::Cluster
1541 | ObjectType::ClusterReplica
1542 | ObjectType::Role
1543 | ObjectType::NetworkPolicy => false,
1544 }
1545 }
1546}
1547
1548impl From<mz_sql_parser::ast::ObjectType> for ObjectType {
1549 fn from(value: mz_sql_parser::ast::ObjectType) -> Self {
1550 match value {
1551 mz_sql_parser::ast::ObjectType::Table => ObjectType::Table,
1552 mz_sql_parser::ast::ObjectType::View => ObjectType::View,
1553 mz_sql_parser::ast::ObjectType::MaterializedView => ObjectType::MaterializedView,
1554 mz_sql_parser::ast::ObjectType::Source => ObjectType::Source,
1555 mz_sql_parser::ast::ObjectType::Subsource => ObjectType::Source,
1556 mz_sql_parser::ast::ObjectType::Sink => ObjectType::Sink,
1557 mz_sql_parser::ast::ObjectType::Index => ObjectType::Index,
1558 mz_sql_parser::ast::ObjectType::Type => ObjectType::Type,
1559 mz_sql_parser::ast::ObjectType::Role => ObjectType::Role,
1560 mz_sql_parser::ast::ObjectType::Cluster => ObjectType::Cluster,
1561 mz_sql_parser::ast::ObjectType::ClusterReplica => ObjectType::ClusterReplica,
1562 mz_sql_parser::ast::ObjectType::Secret => ObjectType::Secret,
1563 mz_sql_parser::ast::ObjectType::Connection => ObjectType::Connection,
1564 mz_sql_parser::ast::ObjectType::Database => ObjectType::Database,
1565 mz_sql_parser::ast::ObjectType::Schema => ObjectType::Schema,
1566 mz_sql_parser::ast::ObjectType::Func => ObjectType::Func,
1567 mz_sql_parser::ast::ObjectType::ContinualTask => ObjectType::ContinualTask,
1568 mz_sql_parser::ast::ObjectType::NetworkPolicy => ObjectType::NetworkPolicy,
1569 }
1570 }
1571}
1572
1573impl From<CommentObjectId> for ObjectType {
1574 fn from(value: CommentObjectId) -> ObjectType {
1575 match value {
1576 CommentObjectId::Table(_) => ObjectType::Table,
1577 CommentObjectId::View(_) => ObjectType::View,
1578 CommentObjectId::MaterializedView(_) => ObjectType::MaterializedView,
1579 CommentObjectId::Source(_) => ObjectType::Source,
1580 CommentObjectId::Sink(_) => ObjectType::Sink,
1581 CommentObjectId::Index(_) => ObjectType::Index,
1582 CommentObjectId::Func(_) => ObjectType::Func,
1583 CommentObjectId::Connection(_) => ObjectType::Connection,
1584 CommentObjectId::Type(_) => ObjectType::Type,
1585 CommentObjectId::Secret(_) => ObjectType::Secret,
1586 CommentObjectId::Role(_) => ObjectType::Role,
1587 CommentObjectId::Database(_) => ObjectType::Database,
1588 CommentObjectId::Schema(_) => ObjectType::Schema,
1589 CommentObjectId::Cluster(_) => ObjectType::Cluster,
1590 CommentObjectId::ClusterReplica(_) => ObjectType::ClusterReplica,
1591 CommentObjectId::ContinualTask(_) => ObjectType::ContinualTask,
1592 CommentObjectId::NetworkPolicy(_) => ObjectType::NetworkPolicy,
1593 }
1594 }
1595}
1596
1597impl Display for ObjectType {
1598 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1599 f.write_str(match self {
1600 ObjectType::Table => "TABLE",
1601 ObjectType::View => "VIEW",
1602 ObjectType::MaterializedView => "MATERIALIZED VIEW",
1603 ObjectType::Source => "SOURCE",
1604 ObjectType::Sink => "SINK",
1605 ObjectType::Index => "INDEX",
1606 ObjectType::Type => "TYPE",
1607 ObjectType::Role => "ROLE",
1608 ObjectType::Cluster => "CLUSTER",
1609 ObjectType::ClusterReplica => "CLUSTER REPLICA",
1610 ObjectType::Secret => "SECRET",
1611 ObjectType::Connection => "CONNECTION",
1612 ObjectType::Database => "DATABASE",
1613 ObjectType::Schema => "SCHEMA",
1614 ObjectType::Func => "FUNCTION",
1615 ObjectType::ContinualTask => "CONTINUAL TASK",
1616 ObjectType::NetworkPolicy => "NETWORK POLICY",
1617 })
1618 }
1619}
1620
1621#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Deserialize, Serialize)]
1622pub enum SystemObjectType {
1624 Object(ObjectType),
1626 System,
1628}
1629
1630impl SystemObjectType {
1631 pub fn is_relation(&self) -> bool {
1633 match self {
1634 SystemObjectType::Object(object_type) => object_type.is_relation(),
1635 SystemObjectType::System => false,
1636 }
1637 }
1638}
1639
1640impl Display for SystemObjectType {
1641 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1642 match self {
1643 SystemObjectType::Object(object_type) => std::fmt::Display::fmt(&object_type, f),
1644 SystemObjectType::System => f.write_str("SYSTEM"),
1645 }
1646 }
1647}
1648
1649#[derive(Debug, Clone, PartialEq, Eq)]
1651pub enum ErrorMessageObjectDescription {
1652 Object {
1654 object_type: ObjectType,
1656 object_name: Option<String>,
1658 },
1659 System,
1661}
1662
1663impl ErrorMessageObjectDescription {
1664 pub fn from_id(
1666 object_id: &ObjectId,
1667 catalog: &dyn SessionCatalog,
1668 ) -> ErrorMessageObjectDescription {
1669 let object_name = match object_id {
1670 ObjectId::Cluster(cluster_id) => catalog.get_cluster(*cluster_id).name().to_string(),
1671 ObjectId::ClusterReplica((cluster_id, replica_id)) => catalog
1672 .get_cluster_replica(*cluster_id, *replica_id)
1673 .name()
1674 .to_string(),
1675 ObjectId::Database(database_id) => catalog.get_database(database_id).name().to_string(),
1676 ObjectId::Schema((database_spec, schema_spec)) => {
1677 let name = catalog.get_schema(database_spec, schema_spec).name();
1678 catalog.resolve_full_schema_name(name).to_string()
1679 }
1680 ObjectId::Role(role_id) => catalog.get_role(role_id).name().to_string(),
1681 ObjectId::Item(id) => {
1682 let name = catalog.get_item(id).name();
1683 catalog.resolve_full_name(name).to_string()
1684 }
1685 ObjectId::NetworkPolicy(network_policy_id) => catalog
1686 .get_network_policy(network_policy_id)
1687 .name()
1688 .to_string(),
1689 };
1690 ErrorMessageObjectDescription::Object {
1691 object_type: catalog.get_object_type(object_id),
1692 object_name: Some(object_name),
1693 }
1694 }
1695
1696 pub fn from_sys_id(
1698 object_id: &SystemObjectId,
1699 catalog: &dyn SessionCatalog,
1700 ) -> ErrorMessageObjectDescription {
1701 match object_id {
1702 SystemObjectId::Object(object_id) => {
1703 ErrorMessageObjectDescription::from_id(object_id, catalog)
1704 }
1705 SystemObjectId::System => ErrorMessageObjectDescription::System,
1706 }
1707 }
1708
1709 pub fn from_object_type(object_type: SystemObjectType) -> ErrorMessageObjectDescription {
1711 match object_type {
1712 SystemObjectType::Object(object_type) => ErrorMessageObjectDescription::Object {
1713 object_type,
1714 object_name: None,
1715 },
1716 SystemObjectType::System => ErrorMessageObjectDescription::System,
1717 }
1718 }
1719}
1720
1721impl Display for ErrorMessageObjectDescription {
1722 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1723 match self {
1724 ErrorMessageObjectDescription::Object {
1725 object_type,
1726 object_name,
1727 } => {
1728 let object_name = object_name
1729 .as_ref()
1730 .map(|object_name| format!(" {}", object_name.quoted()))
1731 .unwrap_or_else(|| "".to_string());
1732 write!(f, "{object_type}{object_name}")
1733 }
1734 ErrorMessageObjectDescription::System => f.write_str("SYSTEM"),
1735 }
1736 }
1737}
1738
1739#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
1740#[serde(into = "BTreeMap<String, RoleId>")]
1743#[serde(try_from = "BTreeMap<String, RoleId>")]
1744pub struct RoleMembership {
1746 pub map: BTreeMap<RoleId, RoleId>,
1752}
1753
1754impl RoleMembership {
1755 pub fn new() -> RoleMembership {
1757 RoleMembership {
1758 map: BTreeMap::new(),
1759 }
1760 }
1761}
1762
1763impl From<RoleMembership> for BTreeMap<String, RoleId> {
1764 fn from(value: RoleMembership) -> Self {
1765 value
1766 .map
1767 .into_iter()
1768 .map(|(k, v)| (k.to_string(), v))
1769 .collect()
1770 }
1771}
1772
1773impl TryFrom<BTreeMap<String, RoleId>> for RoleMembership {
1774 type Error = anyhow::Error;
1775
1776 fn try_from(value: BTreeMap<String, RoleId>) -> Result<Self, Self::Error> {
1777 Ok(RoleMembership {
1778 map: value
1779 .into_iter()
1780 .map(|(k, v)| Ok((RoleId::from_str(&k)?, v)))
1781 .collect::<Result<_, anyhow::Error>>()?,
1782 })
1783 }
1784}
1785
1786#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1788pub struct DefaultPrivilegeObject {
1789 pub role_id: RoleId,
1791 pub database_id: Option<DatabaseId>,
1793 pub schema_id: Option<SchemaId>,
1795 pub object_type: ObjectType,
1797}
1798
1799impl DefaultPrivilegeObject {
1800 pub fn new(
1802 role_id: RoleId,
1803 database_id: Option<DatabaseId>,
1804 schema_id: Option<SchemaId>,
1805 object_type: ObjectType,
1806 ) -> DefaultPrivilegeObject {
1807 DefaultPrivilegeObject {
1808 role_id,
1809 database_id,
1810 schema_id,
1811 object_type,
1812 }
1813 }
1814}
1815
1816impl std::fmt::Display for DefaultPrivilegeObject {
1817 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1818 write!(f, "{self:?}")
1820 }
1821}
1822
1823#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
1825pub struct DefaultPrivilegeAclItem {
1826 pub grantee: RoleId,
1828 pub acl_mode: AclMode,
1830}
1831
1832impl DefaultPrivilegeAclItem {
1833 pub fn new(grantee: RoleId, acl_mode: AclMode) -> DefaultPrivilegeAclItem {
1835 DefaultPrivilegeAclItem { grantee, acl_mode }
1836 }
1837
1838 pub fn mz_acl_item(self, grantor: RoleId) -> MzAclItem {
1840 MzAclItem {
1841 grantee: self.grantee,
1842 grantor,
1843 acl_mode: self.acl_mode,
1844 }
1845 }
1846}
1847
1848#[derive(Debug, Clone)]
1854pub struct BuiltinsConfig {
1855 pub include_continual_tasks: bool,
1857}
1858
1859#[cfg(test)]
1860mod tests {
1861 use super::{CloudProvider, EnvironmentId, InvalidEnvironmentIdError};
1862
1863 #[mz_ore::test]
1864 fn test_environment_id() {
1865 for (input, expected) in [
1866 (
1867 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1868 Ok(EnvironmentId {
1869 cloud_provider: CloudProvider::Local,
1870 cloud_provider_region: "az1".into(),
1871 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1872 ordinal: 452,
1873 }),
1874 ),
1875 (
1876 "aws-us-east-1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1877 Ok(EnvironmentId {
1878 cloud_provider: CloudProvider::Aws,
1879 cloud_provider_region: "us-east-1".into(),
1880 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1881 ordinal: 0,
1882 }),
1883 ),
1884 (
1885 "gcp-us-central1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1886 Ok(EnvironmentId {
1887 cloud_provider: CloudProvider::Gcp,
1888 cloud_provider_region: "us-central1".into(),
1889 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1890 ordinal: 0,
1891 }),
1892 ),
1893 (
1894 "azure-australiaeast-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1895 Ok(EnvironmentId {
1896 cloud_provider: CloudProvider::Azure,
1897 cloud_provider_region: "australiaeast".into(),
1898 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1899 ordinal: 0,
1900 }),
1901 ),
1902 (
1903 "generic-moon-station-11-darkside-1497a3b7-a455-4fc4-8752-b44a94b5f90a-0",
1904 Ok(EnvironmentId {
1905 cloud_provider: CloudProvider::Generic,
1906 cloud_provider_region: "moon-station-11-darkside".into(),
1907 organization_id: "1497a3b7-a455-4fc4-8752-b44a94b5f90a".parse().unwrap(),
1908 ordinal: 0,
1909 }),
1910 ),
1911 ("", Err(InvalidEnvironmentIdError)),
1912 (
1913 "local-az1-1497a3b7-a455-4fc4-8752-b44a94b5f90a-123456789",
1914 Err(InvalidEnvironmentIdError),
1915 ),
1916 (
1917 "local-1497a3b7-a455-4fc4-8752-b44a94b5f90a-452",
1918 Err(InvalidEnvironmentIdError),
1919 ),
1920 (
1921 "local-az1-1497a3b7-a455-4fc48752-b44a94b5f90a-452",
1922 Err(InvalidEnvironmentIdError),
1923 ),
1924 ] {
1925 let actual = input.parse();
1926 assert_eq!(expected, actual, "input = {}", input);
1927 if let Ok(actual) = actual {
1928 assert_eq!(input, actual.to_string(), "input = {}", input);
1929 }
1930 }
1931 }
1932}