1use std::borrow::Cow;
15use std::collections::{BTreeMap, BTreeSet};
16use std::ops::{Deref, DerefMut};
17use std::sync::{Arc, LazyLock};
18use std::time::Duration;
19
20use chrono::{DateTime, Utc};
21use mz_adapter_types::compaction::CompactionWindow;
22use mz_adapter_types::connection::ConnectionId;
23use mz_compute_client::logging::LogVariant;
24use mz_compute_types::dataflows::DataflowDescription;
25use mz_compute_types::plan::LirRelationExpr as ComputePlan;
26use mz_controller::clusters::{ClusterRole, ClusterStatus, ReplicaConfig, ReplicaLogging};
27use mz_controller_types::{ClusterId, ReplicaId};
28use mz_expr::{MirScalarExpr, OptimizedMirRelationExpr};
29use mz_ore::collections::CollectionExt;
30use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem, PrivilegeMap};
31use mz_repr::network_policy_id::NetworkPolicyId;
32use mz_repr::optimize::OptimizerFeatureOverrides;
33use mz_repr::refresh_schedule::RefreshSchedule;
34use mz_repr::role_id::RoleId;
35use mz_repr::{
36 CatalogItemId, ColumnName, Diff, GlobalId, RelationDesc, RelationVersion,
37 RelationVersionSelector, SqlColumnType, Timestamp, VersionedRelationDesc,
38};
39use mz_sql::ast::display::AstDisplay;
40use mz_sql::ast::{
41 ColumnDef, ColumnOption, ColumnOptionDef, ColumnVersioned, Expr, Raw, RawDataType, Statement,
42 UnresolvedItemName, Value, WithOptionValue,
43};
44use mz_sql::catalog::{
45 CatalogClusterReplica, CatalogError as SqlCatalogError, CatalogItem as SqlCatalogItem,
46 CatalogItemType as SqlCatalogItemType, CatalogItemType, CatalogSchema, CatalogType,
47 CatalogTypeDetails, DefaultPrivilegeAclItem, DefaultPrivilegeObject, IdReference,
48 RoleAttributes, RoleMembership, RoleVars, SystemObjectType,
49};
50use mz_sql::names::{
51 Aug, CommentObjectId, DatabaseId, DependencyIds, FullItemName, QualifiedItemName,
52 QualifiedSchemaName, ResolvedDatabaseSpecifier, ResolvedIds, SchemaId, SchemaSpecifier,
53};
54use mz_sql::plan::{
55 AutoScalingStrategy, ClusterSchedule, ComputeReplicaConfig, ComputeReplicaIntrospectionConfig,
56 ConnectionDetails, CreateClusterManagedPlan, CreateClusterPlan, CreateClusterVariant,
57 CreateSourcePlan, HirRelationExpr, NetworkPolicyRule, OnTimeoutAction, PlanError,
58 WebhookBodyFormat, WebhookHeaders, WebhookValidation,
59};
60use mz_sql::rbac;
61use mz_sql::session::vars::OwnedVarInput;
62use mz_storage_client::controller::IntrospectionType;
63use mz_storage_types::connections::inline::ReferencedConnection;
64use mz_storage_types::sinks::{SinkEnvelope, StorageSinkConnection};
65use mz_storage_types::sources::load_generator::LoadGenerator;
66use mz_storage_types::sources::{
67 GenericSourceConnection, SourceConnection, SourceDesc, SourceEnvelope, SourceExportDataConfig,
68 SourceExportDetails, Timeline,
69};
70use mz_transform::dataflow::DataflowMetainfo;
71use mz_transform::notice::OptimizerNotice;
72use serde::ser::SerializeSeq;
73use serde::{Deserialize, Serialize};
74use timely::progress::Antichain;
75use tracing::debug;
76
77use crate::builtin::{MZ_CATALOG_SERVER_CLUSTER, MZ_SYSTEM_CLUSTER};
78use crate::durable;
79
80pub trait UpdateFrom<T>: From<T> {
82 fn update_from(&mut self, from: T);
83}
84
85#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
86pub struct Database {
87 pub name: String,
88 pub id: DatabaseId,
89 pub oid: u32,
90 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
91 pub schemas_by_id: BTreeMap<SchemaId, Schema>,
92 pub schemas_by_name: BTreeMap<String, SchemaId>,
93 pub owner_id: RoleId,
94 pub privileges: PrivilegeMap,
95}
96
97impl From<Database> for durable::Database {
98 fn from(database: Database) -> durable::Database {
99 durable::Database {
100 id: database.id,
101 oid: database.oid,
102 name: database.name,
103 owner_id: database.owner_id,
104 privileges: database.privileges.into_all_values().collect(),
105 }
106 }
107}
108
109impl From<durable::Database> for Database {
110 fn from(
111 durable::Database {
112 id,
113 oid,
114 name,
115 owner_id,
116 privileges,
117 }: durable::Database,
118 ) -> Database {
119 Database {
120 id,
121 oid,
122 schemas_by_id: BTreeMap::new(),
123 schemas_by_name: BTreeMap::new(),
124 name,
125 owner_id,
126 privileges: PrivilegeMap::from_mz_acl_items(privileges),
127 }
128 }
129}
130
131impl UpdateFrom<durable::Database> for Database {
132 fn update_from(
133 &mut self,
134 durable::Database {
135 id,
136 oid,
137 name,
138 owner_id,
139 privileges,
140 }: durable::Database,
141 ) {
142 self.id = id;
143 self.oid = oid;
144 self.name = name;
145 self.owner_id = owner_id;
146 self.privileges = PrivilegeMap::from_mz_acl_items(privileges);
147 }
148}
149
150#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
151pub struct Schema {
152 pub name: QualifiedSchemaName,
153 pub id: SchemaSpecifier,
154 pub oid: u32,
155 pub items: BTreeMap<String, CatalogItemId>,
156 pub functions: BTreeMap<String, CatalogItemId>,
157 pub types: BTreeMap<String, CatalogItemId>,
158 pub owner_id: RoleId,
159 pub privileges: PrivilegeMap,
160}
161
162impl From<Schema> for durable::Schema {
163 fn from(schema: Schema) -> durable::Schema {
164 durable::Schema {
165 id: schema.id.into(),
166 oid: schema.oid,
167 name: schema.name.schema,
168 database_id: schema.name.database.id(),
169 owner_id: schema.owner_id,
170 privileges: schema.privileges.into_all_values().collect(),
171 }
172 }
173}
174
175impl From<durable::Schema> for Schema {
176 fn from(
177 durable::Schema {
178 id,
179 oid,
180 name,
181 database_id,
182 owner_id,
183 privileges,
184 }: durable::Schema,
185 ) -> Schema {
186 Schema {
187 name: QualifiedSchemaName {
188 database: database_id.into(),
189 schema: name,
190 },
191 id: id.into(),
192 oid,
193 items: BTreeMap::new(),
194 functions: BTreeMap::new(),
195 types: BTreeMap::new(),
196 owner_id,
197 privileges: PrivilegeMap::from_mz_acl_items(privileges),
198 }
199 }
200}
201
202impl UpdateFrom<durable::Schema> for Schema {
203 fn update_from(
204 &mut self,
205 durable::Schema {
206 id,
207 oid,
208 name,
209 database_id,
210 owner_id,
211 privileges,
212 }: durable::Schema,
213 ) {
214 self.name = QualifiedSchemaName {
215 database: database_id.into(),
216 schema: name,
217 };
218 self.id = id.into();
219 self.oid = oid;
220 self.owner_id = owner_id;
221 self.privileges = PrivilegeMap::from_mz_acl_items(privileges);
222 }
223}
224
225#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
226pub struct Role {
227 pub name: String,
228 pub id: RoleId,
229 pub oid: u32,
230 pub attributes: RoleAttributes,
231 pub membership: RoleMembership,
232 pub vars: RoleVars,
233}
234
235impl Role {
236 pub fn is_user(&self) -> bool {
237 self.id.is_user()
238 }
239
240 pub fn vars<'a>(&'a self) -> impl Iterator<Item = (&'a str, &'a OwnedVarInput)> {
241 self.vars.map.iter().map(|(name, val)| (name.as_str(), val))
242 }
243}
244
245impl From<Role> for durable::Role {
246 fn from(role: Role) -> durable::Role {
247 durable::Role {
248 id: role.id,
249 oid: role.oid,
250 name: role.name,
251 attributes: role.attributes,
252 membership: role.membership,
253 vars: role.vars,
254 }
255 }
256}
257
258impl From<durable::Role> for Role {
259 fn from(
260 durable::Role {
261 id,
262 oid,
263 name,
264 attributes,
265 membership,
266 vars,
267 }: durable::Role,
268 ) -> Self {
269 Role {
270 name,
271 id,
272 oid,
273 attributes,
274 membership,
275 vars,
276 }
277 }
278}
279
280impl UpdateFrom<durable::Role> for Role {
281 fn update_from(
282 &mut self,
283 durable::Role {
284 id,
285 oid,
286 name,
287 attributes,
288 membership,
289 vars,
290 }: durable::Role,
291 ) {
292 self.id = id;
293 self.oid = oid;
294 self.name = name;
295 self.attributes = attributes;
296 self.membership = membership;
297 self.vars = vars;
298 }
299}
300
301#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
302pub struct RoleAuth {
303 pub role_id: RoleId,
304 pub password_hash: Option<String>,
305 pub updated_at: u64,
306}
307
308impl From<RoleAuth> for durable::RoleAuth {
309 fn from(role_auth: RoleAuth) -> durable::RoleAuth {
310 durable::RoleAuth {
311 role_id: role_auth.role_id,
312 password_hash: role_auth.password_hash,
313 updated_at: role_auth.updated_at,
314 }
315 }
316}
317
318impl From<durable::RoleAuth> for RoleAuth {
319 fn from(
320 durable::RoleAuth {
321 role_id,
322 password_hash,
323 updated_at,
324 }: durable::RoleAuth,
325 ) -> RoleAuth {
326 RoleAuth {
327 role_id,
328 password_hash,
329 updated_at,
330 }
331 }
332}
333
334impl UpdateFrom<durable::RoleAuth> for RoleAuth {
335 fn update_from(&mut self, from: durable::RoleAuth) {
336 self.role_id = from.role_id;
337 self.password_hash = from.password_hash;
338 self.updated_at = from.updated_at;
339 }
340}
341
342#[derive(Debug, Serialize, Clone, PartialEq)]
343pub struct Cluster {
344 pub name: String,
345 pub id: ClusterId,
346 pub config: ClusterConfig,
347 #[serde(skip)]
348 pub log_indexes: BTreeMap<LogVariant, GlobalId>,
349 pub bound_objects: BTreeSet<CatalogItemId>,
352 pub replica_id_by_name_: BTreeMap<String, ReplicaId>,
353 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
354 pub replicas_by_id_: BTreeMap<ReplicaId, ClusterReplica>,
355 pub owner_id: RoleId,
356 pub privileges: PrivilegeMap,
357}
358
359impl Cluster {
360 pub fn role(&self) -> ClusterRole {
362 if self.name == MZ_SYSTEM_CLUSTER.name {
365 ClusterRole::SystemCritical
366 } else if self.name == MZ_CATALOG_SERVER_CLUSTER.name {
367 ClusterRole::System
368 } else {
369 ClusterRole::User
370 }
371 }
372
373 pub fn is_managed(&self) -> bool {
375 matches!(self.config.variant, ClusterVariant::Managed { .. })
376 }
377
378 pub fn user_replicas(&self) -> impl Iterator<Item = &ClusterReplica> {
380 self.replicas().filter(|r| !r.config.location.internal())
381 }
382
383 pub fn replicas(&self) -> impl Iterator<Item = &ClusterReplica> {
385 self.replicas_by_id_.values()
386 }
387
388 pub fn replica(&self, replica_id: ReplicaId) -> Option<&ClusterReplica> {
390 self.replicas_by_id_.get(&replica_id)
391 }
392
393 pub fn replica_id(&self, name: &str) -> Option<ReplicaId> {
395 self.replica_id_by_name_.get(name).copied()
396 }
397
398 pub fn availability_zones(&self) -> Option<&[String]> {
400 match &self.config.variant {
401 ClusterVariant::Managed(managed) => Some(&managed.availability_zones),
402 ClusterVariant::Unmanaged => None,
403 }
404 }
405
406 pub fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError> {
414 let name = self.name.clone();
415 let variant = match &self.config.variant {
416 ClusterVariant::Managed(ClusterVariantManaged {
417 size,
418 availability_zones,
419 logging,
420 arrangement_compression,
421 replication_factor,
422 optimizer_feature_overrides,
423 schedule,
424 auto_scaling_strategy,
425 reconfiguration: _,
428 burst: _,
429 }) => {
430 let introspection = match logging {
431 ReplicaLogging {
432 log_logging,
433 interval: Some(interval),
434 } => Some(ComputeReplicaIntrospectionConfig {
435 debugging: *log_logging,
436 interval: interval.clone(),
437 }),
438 ReplicaLogging {
439 log_logging: _,
440 interval: None,
441 } => None,
442 };
443 let compute = ComputeReplicaConfig {
444 introspection,
445 arrangement_compression: *arrangement_compression,
446 };
447 CreateClusterVariant::Managed(CreateClusterManagedPlan {
448 replication_factor: replication_factor.clone(),
449 size: size.clone(),
450 availability_zones: availability_zones.clone(),
451 compute,
452 optimizer_feature_overrides: optimizer_feature_overrides.clone(),
453 schedule: schedule.clone(),
454 auto_scaling_strategy: auto_scaling_strategy.clone(),
455 })
456 }
457 ClusterVariant::Unmanaged => {
458 return Err(PlanError::Unsupported {
461 feature: "SHOW CREATE for unmanaged clusters".to_string(),
462 discussion_no: None,
463 });
464 }
465 };
466 let workload_class = self.config.workload_class.clone();
467 Ok(CreateClusterPlan {
468 name,
469 variant,
470 workload_class,
471 if_not_exists: false,
475 })
476 }
477}
478
479impl From<Cluster> for durable::Cluster {
480 fn from(cluster: Cluster) -> durable::Cluster {
481 durable::Cluster {
482 id: cluster.id,
483 name: cluster.name,
484 owner_id: cluster.owner_id,
485 privileges: cluster.privileges.into_all_values().collect(),
486 config: cluster.config.into(),
487 }
488 }
489}
490
491impl From<durable::Cluster> for Cluster {
492 fn from(
493 durable::Cluster {
494 id,
495 name,
496 owner_id,
497 privileges,
498 config,
499 }: durable::Cluster,
500 ) -> Self {
501 Cluster {
502 name: name.clone(),
503 id,
504 bound_objects: BTreeSet::new(),
505 log_indexes: BTreeMap::new(),
506 replica_id_by_name_: BTreeMap::new(),
507 replicas_by_id_: BTreeMap::new(),
508 owner_id,
509 privileges: PrivilegeMap::from_mz_acl_items(privileges),
510 config: config.into(),
511 }
512 }
513}
514
515impl UpdateFrom<durable::Cluster> for Cluster {
516 fn update_from(
517 &mut self,
518 durable::Cluster {
519 id,
520 name,
521 owner_id,
522 privileges,
523 config,
524 }: durable::Cluster,
525 ) {
526 self.id = id;
527 self.name = name;
528 self.owner_id = owner_id;
529 self.privileges = PrivilegeMap::from_mz_acl_items(privileges);
530 self.config = config.into();
531 }
532}
533
534#[derive(Debug, Serialize, Clone, PartialEq)]
535pub struct ClusterReplica {
536 pub name: String,
537 pub cluster_id: ClusterId,
538 pub replica_id: ReplicaId,
539 pub config: ReplicaConfig,
540 pub owner_id: RoleId,
541}
542
543impl From<ClusterReplica> for durable::ClusterReplica {
544 fn from(replica: ClusterReplica) -> durable::ClusterReplica {
545 durable::ClusterReplica {
546 cluster_id: replica.cluster_id,
547 replica_id: replica.replica_id,
548 name: replica.name,
549 config: replica.config.into(),
550 owner_id: replica.owner_id,
551 }
552 }
553}
554
555#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
556pub struct ClusterReplicaProcessStatus {
557 pub status: ClusterStatus,
558 pub restart_count: u64,
561 pub time: DateTime<Utc>,
563}
564
565#[derive(Debug, Serialize, Clone, PartialEq)]
566pub struct SourceReferences {
567 pub updated_at: u64,
568 pub references: Vec<SourceReference>,
569}
570
571#[derive(Debug, Serialize, Clone, PartialEq)]
572pub struct SourceReference {
573 pub name: String,
574 pub namespace: Option<String>,
575 pub columns: Vec<String>,
576}
577
578impl From<SourceReference> for durable::SourceReference {
579 fn from(source_reference: SourceReference) -> durable::SourceReference {
580 durable::SourceReference {
581 name: source_reference.name,
582 namespace: source_reference.namespace,
583 columns: source_reference.columns,
584 }
585 }
586}
587
588impl SourceReferences {
589 pub fn to_durable(self, source_id: CatalogItemId) -> durable::SourceReferences {
590 durable::SourceReferences {
591 source_id,
592 updated_at: self.updated_at,
593 references: self.references.into_iter().map(Into::into).collect(),
594 }
595 }
596}
597
598impl From<durable::SourceReference> for SourceReference {
599 fn from(source_reference: durable::SourceReference) -> SourceReference {
600 SourceReference {
601 name: source_reference.name,
602 namespace: source_reference.namespace,
603 columns: source_reference.columns,
604 }
605 }
606}
607
608impl From<durable::SourceReferences> for SourceReferences {
609 fn from(source_references: durable::SourceReferences) -> SourceReferences {
610 SourceReferences {
611 updated_at: source_references.updated_at,
612 references: source_references
613 .references
614 .into_iter()
615 .map(|source_reference| source_reference.into())
616 .collect(),
617 }
618 }
619}
620
621impl From<mz_sql::plan::SourceReference> for SourceReference {
622 fn from(source_reference: mz_sql::plan::SourceReference) -> SourceReference {
623 SourceReference {
624 name: source_reference.name,
625 namespace: source_reference.namespace,
626 columns: source_reference.columns,
627 }
628 }
629}
630
631impl From<mz_sql::plan::SourceReferences> for SourceReferences {
632 fn from(source_references: mz_sql::plan::SourceReferences) -> SourceReferences {
633 SourceReferences {
634 updated_at: source_references.updated_at,
635 references: source_references
636 .references
637 .into_iter()
638 .map(|source_reference| source_reference.into())
639 .collect(),
640 }
641 }
642}
643
644impl From<SourceReferences> for mz_sql::plan::SourceReferences {
645 fn from(source_references: SourceReferences) -> mz_sql::plan::SourceReferences {
646 mz_sql::plan::SourceReferences {
647 updated_at: source_references.updated_at,
648 references: source_references
649 .references
650 .into_iter()
651 .map(|source_reference| source_reference.into())
652 .collect(),
653 }
654 }
655}
656
657impl From<SourceReference> for mz_sql::plan::SourceReference {
658 fn from(source_reference: SourceReference) -> mz_sql::plan::SourceReference {
659 mz_sql::plan::SourceReference {
660 name: source_reference.name,
661 namespace: source_reference.namespace,
662 columns: source_reference.columns,
663 }
664 }
665}
666
667#[derive(Clone, Debug, Serialize)]
668pub struct CatalogEntry {
669 pub item: CatalogItem,
670 #[serde(skip)]
671 pub referenced_by: Vec<CatalogItemId>,
672 #[serde(skip)]
676 pub used_by: Vec<CatalogItemId>,
677 pub id: CatalogItemId,
678 pub oid: u32,
679 pub name: QualifiedItemName,
680 pub owner_id: RoleId,
681 pub privileges: PrivilegeMap,
682}
683
684#[derive(Clone, Debug)]
699pub struct CatalogCollectionEntry {
700 pub entry: CatalogEntry,
701 pub version: RelationVersionSelector,
702}
703
704impl CatalogCollectionEntry {
705 pub fn relation_desc(&self) -> Option<Cow<'_, RelationDesc>> {
706 self.item().relation_desc(self.version)
707 }
708}
709
710impl mz_sql::catalog::CatalogCollectionItem for CatalogCollectionEntry {
711 fn relation_desc(&self) -> Option<Cow<'_, RelationDesc>> {
712 self.item().relation_desc(self.version)
713 }
714
715 fn global_id(&self) -> GlobalId {
716 self.entry
717 .item()
718 .global_id_for_version(self.version)
719 .expect("catalog corruption, missing version!")
720 }
721}
722
723impl Deref for CatalogCollectionEntry {
724 type Target = CatalogEntry;
725
726 fn deref(&self) -> &CatalogEntry {
727 &self.entry
728 }
729}
730
731impl mz_sql::catalog::CatalogItem for CatalogCollectionEntry {
732 fn name(&self) -> &QualifiedItemName {
733 self.entry.name()
734 }
735
736 fn id(&self) -> CatalogItemId {
737 self.entry.id()
738 }
739
740 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_> {
741 Box::new(self.entry.global_ids())
742 }
743
744 fn oid(&self) -> u32 {
745 self.entry.oid()
746 }
747
748 fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
749 self.entry.func()
750 }
751
752 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
753 self.entry.source_desc()
754 }
755
756 fn connection(
757 &self,
758 ) -> Result<mz_storage_types::connections::Connection<ReferencedConnection>, SqlCatalogError>
759 {
760 mz_sql::catalog::CatalogItem::connection(&self.entry)
761 }
762
763 fn create_sql(&self) -> &str {
764 self.entry.create_sql()
765 }
766
767 fn item_type(&self) -> SqlCatalogItemType {
768 self.entry.item_type()
769 }
770
771 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)> {
772 self.entry.index_details()
773 }
774
775 fn writable_table_details(&self) -> Option<&[Expr<Aug>]> {
776 self.entry.writable_table_details()
777 }
778
779 fn replacement_target(&self) -> Option<CatalogItemId> {
780 self.entry.replacement_target()
781 }
782
783 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>> {
784 self.entry.type_details()
785 }
786
787 fn references(&self) -> &ResolvedIds {
788 self.entry.references()
789 }
790
791 fn uses(&self) -> BTreeSet<CatalogItemId> {
792 self.entry.uses()
793 }
794
795 fn referenced_by(&self) -> &[CatalogItemId] {
796 self.entry.referenced_by()
797 }
798
799 fn used_by(&self) -> &[CatalogItemId] {
800 self.entry.used_by()
801 }
802
803 fn subsource_details(
804 &self,
805 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
806 self.entry.subsource_details()
807 }
808
809 fn source_export_details(
810 &self,
811 ) -> Option<(
812 CatalogItemId,
813 &UnresolvedItemName,
814 &SourceExportDetails,
815 &SourceExportDataConfig<ReferencedConnection>,
816 )> {
817 self.entry.source_export_details()
818 }
819
820 fn is_progress_source(&self) -> bool {
821 self.entry.is_progress_source()
822 }
823
824 fn progress_id(&self) -> Option<CatalogItemId> {
825 self.entry.progress_id()
826 }
827
828 fn owner_id(&self) -> RoleId {
829 *self.entry.owner_id()
830 }
831
832 fn privileges(&self) -> &PrivilegeMap {
833 self.entry.privileges()
834 }
835
836 fn cluster_id(&self) -> Option<ClusterId> {
837 self.entry.item().cluster_id()
838 }
839
840 fn at_version(
841 &self,
842 version: RelationVersionSelector,
843 ) -> Box<dyn mz_sql::catalog::CatalogCollectionItem> {
844 Box::new(CatalogCollectionEntry {
845 entry: self.entry.clone(),
846 version,
847 })
848 }
849
850 fn latest_version(&self) -> Option<RelationVersion> {
851 self.entry.latest_version()
852 }
853}
854
855#[derive(Debug, Clone, Serialize)]
856pub enum CatalogItem {
857 Table(Table),
858 Source(Source),
859 Log(Log),
860 View(View),
861 MaterializedView(MaterializedView),
862 Sink(Sink),
863 Index(Index),
864 Type(Type),
865 Func(Func),
866 Secret(Secret),
867 Connection(Connection),
868 MetricSink(MetricSink),
869}
870
871#[derive(Debug, Clone, Serialize)]
872pub struct Table {
873 pub create_sql: Option<String>,
875 pub desc: VersionedRelationDesc,
877 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
879 pub collections: BTreeMap<RelationVersion, GlobalId>,
880 #[serde(skip)]
882 pub conn_id: Option<ConnectionId>,
883 pub resolved_ids: ResolvedIds,
885 pub custom_logical_compaction_window: Option<CompactionWindow>,
887 pub is_retained_metrics_object: bool,
892 pub data_source: TableDataSource,
894}
895
896impl Table {
897 pub fn timeline(&self) -> Timeline {
898 match &self.data_source {
899 TableDataSource::TableWrites { .. } => Timeline::EpochMilliseconds,
902 TableDataSource::DataSource { timeline, .. } => timeline.clone(),
903 }
904 }
905
906 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
908 self.collections.values().copied()
909 }
910
911 pub fn global_id_writes(&self) -> GlobalId {
913 *self
914 .collections
915 .last_key_value()
916 .expect("at least one version of a table")
917 .1
918 }
919
920 pub fn collection_descs(
922 &self,
923 ) -> impl Iterator<Item = (GlobalId, RelationVersion, RelationDesc)> + '_ {
924 self.collections.iter().map(|(version, gid)| {
925 let desc = self
926 .desc
927 .at_version(RelationVersionSelector::Specific(*version));
928 (*gid, *version, desc)
929 })
930 }
931
932 pub fn desc_for(&self, id: &GlobalId) -> RelationDesc {
934 let (version, _gid) = self
935 .collections
936 .iter()
937 .find(|(_version, gid)| *gid == id)
938 .expect("GlobalId to exist");
939 self.desc
940 .at_version(RelationVersionSelector::Specific(*version))
941 }
942}
943
944#[derive(Clone, Debug, Serialize)]
945pub enum TableDataSource {
946 TableWrites {
948 #[serde(skip)]
949 defaults: Vec<Expr<Aug>>,
950 },
951
952 DataSource {
955 desc: DataSourceDesc,
956 timeline: Timeline,
957 },
958}
959
960#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
961pub enum DataSourceDesc {
962 Ingestion {
964 desc: SourceDesc<ReferencedConnection>,
965 cluster_id: ClusterId,
966 },
967 OldSyntaxIngestion {
969 desc: SourceDesc<ReferencedConnection>,
970 cluster_id: ClusterId,
971 progress_subsource: CatalogItemId,
974 data_config: SourceExportDataConfig<ReferencedConnection>,
975 details: SourceExportDetails,
976 },
977 IngestionExport {
985 ingestion_id: CatalogItemId,
986 external_reference: UnresolvedItemName,
987 details: SourceExportDetails,
988 data_config: SourceExportDataConfig<ReferencedConnection>,
989 },
990 Introspection(IntrospectionType),
992 Progress,
994 Webhook {
996 validate_using: Option<WebhookValidation>,
998 body_format: WebhookBodyFormat,
1000 headers: WebhookHeaders,
1002 cluster_id: ClusterId,
1004 },
1005 Catalog,
1007}
1008
1009impl From<IntrospectionType> for DataSourceDesc {
1010 fn from(typ: IntrospectionType) -> Self {
1011 Self::Introspection(typ)
1012 }
1013}
1014
1015impl DataSourceDesc {
1016 pub fn formats(&self) -> (Option<&str>, Option<&str>) {
1018 match &self {
1019 DataSourceDesc::Ingestion { .. } => (None, None),
1020 DataSourceDesc::OldSyntaxIngestion { data_config, .. } => {
1021 match &data_config.encoding.as_ref() {
1022 Some(encoding) => match &encoding.key {
1023 Some(key) => (Some(key.type_()), Some(encoding.value.type_())),
1024 None => (None, Some(encoding.value.type_())),
1025 },
1026 None => (None, None),
1027 }
1028 }
1029 DataSourceDesc::IngestionExport { data_config, .. } => match &data_config.encoding {
1030 Some(encoding) => match &encoding.key {
1031 Some(key) => (Some(key.type_()), Some(encoding.value.type_())),
1032 None => (None, Some(encoding.value.type_())),
1033 },
1034 None => (None, None),
1035 },
1036 DataSourceDesc::Introspection(_)
1037 | DataSourceDesc::Webhook { .. }
1038 | DataSourceDesc::Progress
1039 | DataSourceDesc::Catalog => (None, None),
1040 }
1041 }
1042
1043 pub fn envelope(&self) -> Option<&str> {
1045 fn envelope_string(envelope: &SourceEnvelope) -> &str {
1050 match envelope {
1051 SourceEnvelope::None(_) => "none",
1052 SourceEnvelope::Upsert(upsert_envelope) => match upsert_envelope.style {
1053 mz_storage_types::sources::envelope::UpsertStyle::Default(_) => "upsert",
1054 mz_storage_types::sources::envelope::UpsertStyle::Debezium { .. } => {
1055 "debezium"
1059 }
1060 mz_storage_types::sources::envelope::UpsertStyle::ValueErrInline { .. } => {
1061 "upsert-value-err-inline"
1062 }
1063 },
1064 SourceEnvelope::CdcV2 => {
1065 "materialize"
1068 }
1069 }
1070 }
1071
1072 match self {
1073 DataSourceDesc::Ingestion { .. } => None,
1078 DataSourceDesc::OldSyntaxIngestion { data_config, .. } => {
1079 Some(envelope_string(&data_config.envelope))
1080 }
1081 DataSourceDesc::IngestionExport { data_config, .. } => {
1082 Some(envelope_string(&data_config.envelope))
1083 }
1084 DataSourceDesc::Introspection(_)
1085 | DataSourceDesc::Webhook { .. }
1086 | DataSourceDesc::Progress
1087 | DataSourceDesc::Catalog => None,
1088 }
1089 }
1090}
1091
1092#[derive(Debug, Clone, Serialize)]
1093pub struct Source {
1094 pub create_sql: Option<String>,
1096 pub global_id: GlobalId,
1098 #[serde(skip)]
1100 pub data_source: DataSourceDesc,
1101 pub desc: RelationDesc,
1103 pub timeline: Timeline,
1105 pub resolved_ids: ResolvedIds,
1107 pub custom_logical_compaction_window: Option<CompactionWindow>,
1111 pub is_retained_metrics_object: bool,
1114}
1115
1116impl Source {
1117 pub fn new(
1124 plan: CreateSourcePlan,
1125 global_id: GlobalId,
1126 resolved_ids: ResolvedIds,
1127 custom_logical_compaction_window: Option<CompactionWindow>,
1128 is_retained_metrics_object: bool,
1129 ) -> Source {
1130 Source {
1131 create_sql: Some(plan.source.create_sql),
1132 data_source: match plan.source.data_source {
1133 mz_sql::plan::DataSourceDesc::Ingestion(desc) => DataSourceDesc::Ingestion {
1134 desc,
1135 cluster_id: plan
1136 .in_cluster
1137 .expect("ingestion-based sources must be given a cluster ID"),
1138 },
1139 mz_sql::plan::DataSourceDesc::OldSyntaxIngestion {
1140 desc,
1141 progress_subsource,
1142 data_config,
1143 details,
1144 } => DataSourceDesc::OldSyntaxIngestion {
1145 desc,
1146 cluster_id: plan
1147 .in_cluster
1148 .expect("ingestion-based sources must be given a cluster ID"),
1149 progress_subsource,
1150 data_config,
1151 details,
1152 },
1153 mz_sql::plan::DataSourceDesc::Progress => {
1154 assert!(
1155 plan.in_cluster.is_none(),
1156 "subsources must not have a host config or cluster_id defined"
1157 );
1158 DataSourceDesc::Progress
1159 }
1160 mz_sql::plan::DataSourceDesc::IngestionExport {
1161 ingestion_id,
1162 external_reference,
1163 details,
1164 data_config,
1165 } => {
1166 assert!(
1167 plan.in_cluster.is_none(),
1168 "subsources must not have a host config or cluster_id defined"
1169 );
1170 DataSourceDesc::IngestionExport {
1171 ingestion_id,
1172 external_reference,
1173 details,
1174 data_config,
1175 }
1176 }
1177 mz_sql::plan::DataSourceDesc::Webhook {
1178 validate_using,
1179 body_format,
1180 headers,
1181 cluster_id,
1182 } => {
1183 mz_ore::soft_assert_or_log!(
1184 cluster_id.is_none(),
1185 "cluster_id set at Source level for Webhooks"
1186 );
1187 DataSourceDesc::Webhook {
1188 validate_using,
1189 body_format,
1190 headers,
1191 cluster_id: plan
1192 .in_cluster
1193 .expect("webhook sources must be given a cluster ID"),
1194 }
1195 }
1196 },
1197 desc: plan.source.desc,
1198 global_id,
1199 timeline: plan.timeline,
1200 resolved_ids,
1201 custom_logical_compaction_window: plan
1202 .source
1203 .compaction_window
1204 .or(custom_logical_compaction_window),
1205 is_retained_metrics_object,
1206 }
1207 }
1208
1209 pub fn source_type(&self) -> &str {
1211 match &self.data_source {
1212 DataSourceDesc::Ingestion { desc, .. }
1213 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => desc.connection.name(),
1214 DataSourceDesc::Progress => "progress",
1215 DataSourceDesc::IngestionExport { .. } => "subsource",
1216 DataSourceDesc::Introspection(_) | DataSourceDesc::Catalog => "source",
1217 DataSourceDesc::Webhook { .. } => "webhook",
1218 }
1219 }
1220
1221 pub fn connection_id(&self) -> Option<CatalogItemId> {
1223 match &self.data_source {
1224 DataSourceDesc::Ingestion { desc, .. }
1225 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => desc.connection.connection_id(),
1226 DataSourceDesc::IngestionExport { .. }
1227 | DataSourceDesc::Introspection(_)
1228 | DataSourceDesc::Webhook { .. }
1229 | DataSourceDesc::Progress
1230 | DataSourceDesc::Catalog => None,
1231 }
1232 }
1233
1234 pub fn global_id(&self) -> GlobalId {
1236 self.global_id
1237 }
1238
1239 pub fn user_controllable_persist_shard_count(&self) -> i64 {
1247 match &self.data_source {
1248 DataSourceDesc::Ingestion { .. } => 0,
1249 DataSourceDesc::OldSyntaxIngestion { desc, .. } => {
1250 match &desc.connection {
1251 GenericSourceConnection::Postgres(_)
1255 | GenericSourceConnection::MySql(_)
1256 | GenericSourceConnection::SqlServer(_) => 0,
1257 GenericSourceConnection::LoadGenerator(lg) => match lg.load_generator {
1258 LoadGenerator::Clock
1260 | LoadGenerator::Counter { .. }
1261 | LoadGenerator::Datums
1262 | LoadGenerator::KeyValue(_) => 1,
1263 LoadGenerator::Auction
1264 | LoadGenerator::Marketing
1265 | LoadGenerator::Tpch { .. } => 0,
1266 },
1267 GenericSourceConnection::Kafka(_) => 1,
1268 }
1269 }
1270 DataSourceDesc::IngestionExport { .. } => 1,
1273 DataSourceDesc::Webhook { .. } => 1,
1274 DataSourceDesc::Introspection(_)
1277 | DataSourceDesc::Progress
1278 | DataSourceDesc::Catalog => 0,
1279 }
1280 }
1281}
1282
1283#[derive(Debug, Clone, Serialize)]
1284pub struct Log {
1285 pub variant: LogVariant,
1287 pub global_id: GlobalId,
1289}
1290
1291impl Log {
1292 pub fn global_id(&self) -> GlobalId {
1294 self.global_id
1295 }
1296}
1297
1298#[derive(Debug, Clone, Serialize)]
1299pub struct Sink {
1300 pub create_sql: String,
1302 pub global_id: GlobalId,
1304 pub from: GlobalId,
1306 pub connection: StorageSinkConnection<ReferencedConnection>,
1308 pub envelope: SinkEnvelope,
1312 pub with_snapshot: bool,
1314 pub version: u64,
1316 pub resolved_ids: ResolvedIds,
1318 pub cluster_id: ClusterId,
1320 pub commit_interval: Option<Duration>,
1322}
1323
1324impl Sink {
1325 pub fn sink_type(&self) -> &str {
1326 self.connection.name()
1327 }
1328
1329 pub fn envelope(&self) -> Option<&str> {
1335 match &self.envelope {
1336 SinkEnvelope::Debezium => Some("debezium"),
1337 SinkEnvelope::Upsert => Some("upsert"),
1338 SinkEnvelope::Append => Some("append"),
1339 }
1340 }
1341
1342 pub fn combined_format(&self) -> Option<Cow<'_, str>> {
1350 match &self.connection {
1351 StorageSinkConnection::Kafka(connection) => Some(connection.format.get_format_name()),
1352 StorageSinkConnection::Iceberg(_) => None,
1353 }
1354 }
1355
1356 pub fn formats(&self) -> Option<(Option<&str>, &str)> {
1362 match &self.connection {
1363 StorageSinkConnection::Kafka(connection) => {
1364 let key_format = connection
1365 .format
1366 .key_format
1367 .as_ref()
1368 .map(|f| f.get_format_name());
1369 let value_format = connection.format.value_format.get_format_name();
1370 Some((key_format, value_format))
1371 }
1372 StorageSinkConnection::Iceberg(_) => None,
1373 }
1374 }
1375
1376 pub fn connection_id(&self) -> Option<CatalogItemId> {
1377 self.connection.connection_id()
1378 }
1379
1380 pub fn global_id(&self) -> GlobalId {
1382 self.global_id
1383 }
1384}
1385
1386#[derive(Debug, Clone, Serialize)]
1387pub struct View {
1388 pub create_sql: String,
1390 pub global_id: GlobalId,
1392 pub raw_expr: Arc<HirRelationExpr>,
1394 pub locally_optimized_expr: Arc<OptimizedMirRelationExpr>,
1396 pub desc: RelationDesc,
1398 pub conn_id: Option<ConnectionId>,
1400 pub resolved_ids: ResolvedIds,
1402 pub dependencies: DependencyIds,
1404}
1405
1406impl View {
1407 pub fn global_id(&self) -> GlobalId {
1409 self.global_id
1410 }
1411}
1412
1413#[derive(Debug, Clone, Serialize)]
1414pub struct MaterializedView {
1415 pub create_sql: String,
1417 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
1419 pub collections: BTreeMap<RelationVersion, GlobalId>,
1420 pub raw_expr: Arc<HirRelationExpr>,
1422 pub locally_optimized_expr: Arc<OptimizedMirRelationExpr>,
1424 pub desc: VersionedRelationDesc,
1426 pub resolved_ids: ResolvedIds,
1428 pub dependencies: DependencyIds,
1430 pub replacement_target: Option<CatalogItemId>,
1432 pub cluster_id: ClusterId,
1434 pub target_replica: Option<ReplicaId>,
1436 pub non_null_assertions: Vec<usize>,
1440 pub custom_logical_compaction_window: Option<CompactionWindow>,
1442 pub refresh_schedule: Option<RefreshSchedule>,
1444 pub initial_as_of: Option<Antichain<mz_repr::Timestamp>>,
1449 #[serde(skip)]
1455 pub optimized_plan: Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1456 #[serde(skip)]
1458 pub physical_plan: Option<Arc<DataflowDescription<ComputePlan>>>,
1459 #[serde(skip)]
1461 pub dataflow_metainfo: Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1462}
1463
1464impl MaterializedView {
1465 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
1467 self.collections.values().copied()
1468 }
1469
1470 pub fn global_id_writes(&self) -> GlobalId {
1473 *self
1474 .collections
1475 .last_key_value()
1476 .expect("at least one version of a materialized view")
1477 .1
1478 }
1479
1480 pub fn collection_descs(
1482 &self,
1483 ) -> impl Iterator<Item = (GlobalId, RelationVersion, RelationDesc)> + '_ {
1484 self.collections.iter().map(|(version, gid)| {
1485 let desc = self
1486 .desc
1487 .at_version(RelationVersionSelector::Specific(*version));
1488 (*gid, *version, desc)
1489 })
1490 }
1491
1492 pub fn desc_for(&self, id: &GlobalId) -> RelationDesc {
1494 let (version, _gid) = self
1495 .collections
1496 .iter()
1497 .find(|(_version, gid)| *gid == id)
1498 .expect("GlobalId to exist");
1499 self.desc
1500 .at_version(RelationVersionSelector::Specific(*version))
1501 }
1502
1503 pub fn apply_replacement(&mut self, replacement: Self) {
1505 let target_id = replacement
1506 .replacement_target
1507 .expect("replacement has target");
1508
1509 fn parse(create_sql: &str) -> mz_sql::ast::CreateMaterializedViewStatement<Raw> {
1510 let res = mz_sql::parse::parse(create_sql).unwrap_or_else(|e| {
1511 panic!("invalid create_sql persisted in catalog: {e}\n{create_sql}");
1512 });
1513 if let Statement::CreateMaterializedView(cmvs) = res.into_element().ast {
1514 cmvs
1515 } else {
1516 panic!("invalid MV create_sql persisted in catalog\n{create_sql}");
1517 }
1518 }
1519
1520 let old_stmt = parse(&self.create_sql);
1521 let rpl_stmt = parse(&replacement.create_sql);
1522 let new_stmt = mz_sql::ast::CreateMaterializedViewStatement {
1523 if_exists: old_stmt.if_exists,
1524 name: old_stmt.name,
1525 columns: rpl_stmt.columns,
1526 replacement_for: None,
1527 in_cluster: rpl_stmt.in_cluster,
1528 in_cluster_replica: rpl_stmt.in_cluster_replica,
1529 query: rpl_stmt.query,
1530 as_of: rpl_stmt.as_of,
1531 with_options: rpl_stmt.with_options,
1532 };
1533 let create_sql = new_stmt.to_ast_string_stable();
1534
1535 let mut collections = std::mem::take(&mut self.collections);
1536 let latest_version = collections.keys().max().expect("at least one version");
1540 let new_version = latest_version.bump();
1541 collections.insert(new_version, replacement.global_id_writes());
1542
1543 let mut resolved_ids = replacement.resolved_ids;
1544 resolved_ids.remove_item(&target_id);
1545 let mut dependencies = replacement.dependencies;
1546 dependencies.0.remove(&target_id);
1547
1548 *self = Self {
1549 create_sql,
1550 collections,
1551 raw_expr: replacement.raw_expr,
1552 locally_optimized_expr: replacement.locally_optimized_expr,
1553 desc: replacement.desc,
1554 resolved_ids,
1555 dependencies,
1556 replacement_target: None,
1557 cluster_id: replacement.cluster_id,
1558 target_replica: replacement.target_replica,
1559 non_null_assertions: replacement.non_null_assertions,
1560 custom_logical_compaction_window: replacement.custom_logical_compaction_window,
1561 refresh_schedule: replacement.refresh_schedule,
1562 initial_as_of: replacement.initial_as_of,
1563 optimized_plan: replacement.optimized_plan,
1564 physical_plan: replacement.physical_plan,
1565 dataflow_metainfo: replacement.dataflow_metainfo,
1566 };
1567 }
1568}
1569
1570#[derive(Debug, Clone, Serialize)]
1571pub struct Index {
1572 pub create_sql: String,
1574 pub global_id: GlobalId,
1576 pub on: GlobalId,
1578 pub keys: Arc<[MirScalarExpr]>,
1580 pub conn_id: Option<ConnectionId>,
1582 pub resolved_ids: ResolvedIds,
1584 pub cluster_id: ClusterId,
1586 pub custom_logical_compaction_window: Option<CompactionWindow>,
1588 pub is_retained_metrics_object: bool,
1593 #[serde(skip)]
1599 pub optimized_plan: Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1600 #[serde(skip)]
1602 pub physical_plan: Option<Arc<DataflowDescription<ComputePlan>>>,
1603 #[serde(skip)]
1605 pub dataflow_metainfo: Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1606}
1607
1608impl Index {
1609 pub fn global_id(&self) -> GlobalId {
1611 self.global_id
1612 }
1613}
1614
1615#[derive(Debug, Clone, Serialize)]
1617pub struct MetricSink {
1618 pub create_sql: String,
1620 pub global_id: GlobalId,
1622 pub from: GlobalId,
1624 pub resolved_ids: ResolvedIds,
1626 pub cluster_id: ClusterId,
1628 pub prefix: String,
1631 #[serde(skip)]
1633 pub optimized_plan: Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1634 #[serde(skip)]
1636 pub physical_plan: Option<Arc<DataflowDescription<ComputePlan>>>,
1637 #[serde(skip)]
1639 pub dataflow_metainfo: Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1640}
1641
1642impl MetricSink {
1643 pub fn global_id(&self) -> GlobalId {
1645 self.global_id
1646 }
1647}
1648
1649#[derive(Debug, Clone, Serialize)]
1650pub struct Type {
1651 pub create_sql: Option<String>,
1653 pub global_id: GlobalId,
1655 #[serde(skip)]
1656 pub details: CatalogTypeDetails<IdReference>,
1657 pub resolved_ids: ResolvedIds,
1659}
1660
1661#[derive(Debug, Clone, Serialize)]
1662pub struct Func {
1663 #[serde(skip)]
1665 pub inner: &'static mz_sql::func::Func,
1666 pub global_id: GlobalId,
1668}
1669
1670#[derive(Debug, Clone, Serialize)]
1671pub struct Secret {
1672 pub create_sql: String,
1674 pub global_id: GlobalId,
1676}
1677
1678#[derive(Debug, Clone, Serialize)]
1679pub struct Connection {
1680 pub create_sql: String,
1682 pub global_id: GlobalId,
1684 pub details: ConnectionDetails,
1686 pub resolved_ids: ResolvedIds,
1688}
1689
1690impl Connection {
1691 pub fn global_id(&self) -> GlobalId {
1693 self.global_id
1694 }
1695}
1696
1697#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1698pub struct NetworkPolicy {
1699 pub name: String,
1700 pub id: NetworkPolicyId,
1701 pub oid: u32,
1702 pub rules: Vec<NetworkPolicyRule>,
1703 pub owner_id: RoleId,
1704 pub privileges: PrivilegeMap,
1705}
1706
1707impl From<NetworkPolicy> for durable::NetworkPolicy {
1708 fn from(policy: NetworkPolicy) -> durable::NetworkPolicy {
1709 durable::NetworkPolicy {
1710 id: policy.id,
1711 oid: policy.oid,
1712 name: policy.name,
1713 rules: policy.rules,
1714 owner_id: policy.owner_id,
1715 privileges: policy.privileges.into_all_values().collect(),
1716 }
1717 }
1718}
1719
1720impl From<durable::NetworkPolicy> for NetworkPolicy {
1721 fn from(
1722 durable::NetworkPolicy {
1723 id,
1724 oid,
1725 name,
1726 rules,
1727 owner_id,
1728 privileges,
1729 }: durable::NetworkPolicy,
1730 ) -> Self {
1731 NetworkPolicy {
1732 id,
1733 oid,
1734 name,
1735 rules,
1736 owner_id,
1737 privileges: PrivilegeMap::from_mz_acl_items(privileges),
1738 }
1739 }
1740}
1741
1742impl UpdateFrom<durable::NetworkPolicy> for NetworkPolicy {
1743 fn update_from(
1744 &mut self,
1745 durable::NetworkPolicy {
1746 id,
1747 oid,
1748 name,
1749 rules,
1750 owner_id,
1751 privileges,
1752 }: durable::NetworkPolicy,
1753 ) {
1754 self.id = id;
1755 self.oid = oid;
1756 self.name = name;
1757 self.rules = rules;
1758 self.owner_id = owner_id;
1759 self.privileges = PrivilegeMap::from_mz_acl_items(privileges);
1760 }
1761}
1762
1763impl CatalogItem {
1764 pub fn typ(&self) -> mz_sql::catalog::CatalogItemType {
1766 match self {
1767 CatalogItem::Table(_) => CatalogItemType::Table,
1768 CatalogItem::Source(_) => CatalogItemType::Source,
1769 CatalogItem::Log(_) => CatalogItemType::Source,
1770 CatalogItem::Sink(_) => CatalogItemType::Sink,
1771 CatalogItem::View(_) => CatalogItemType::View,
1772 CatalogItem::MaterializedView(_) => CatalogItemType::MaterializedView,
1773 CatalogItem::Index(_) => CatalogItemType::Index,
1774 CatalogItem::Type(_) => CatalogItemType::Type,
1775 CatalogItem::Func(_) => CatalogItemType::Func,
1776 CatalogItem::Secret(_) => CatalogItemType::Secret,
1777 CatalogItem::Connection(_) => CatalogItemType::Connection,
1778 CatalogItem::MetricSink(_) => CatalogItemType::MetricSink,
1779 }
1780 }
1781
1782 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
1784 let gid = match self {
1785 CatalogItem::Source(source) => source.global_id,
1786 CatalogItem::Log(log) => log.global_id,
1787 CatalogItem::Sink(sink) => sink.global_id,
1788 CatalogItem::View(view) => view.global_id,
1789 CatalogItem::MaterializedView(mv) => {
1790 return itertools::Either::Left(mv.collections.values().copied());
1791 }
1792 CatalogItem::Index(index) => index.global_id,
1793 CatalogItem::Func(func) => func.global_id,
1794 CatalogItem::Type(ty) => ty.global_id,
1795 CatalogItem::Secret(secret) => secret.global_id,
1796 CatalogItem::Connection(conn) => conn.global_id,
1797 CatalogItem::MetricSink(metric_sink) => metric_sink.global_id,
1798 CatalogItem::Table(table) => {
1799 return itertools::Either::Left(table.collections.values().copied());
1800 }
1801 };
1802 itertools::Either::Right(std::iter::once(gid))
1803 }
1804
1805 pub fn latest_global_id(&self) -> GlobalId {
1809 match self {
1810 CatalogItem::Source(source) => source.global_id,
1811 CatalogItem::Log(log) => log.global_id,
1812 CatalogItem::Sink(sink) => sink.global_id,
1813 CatalogItem::View(view) => view.global_id,
1814 CatalogItem::MaterializedView(mv) => mv.global_id_writes(),
1815 CatalogItem::Index(index) => index.global_id,
1816 CatalogItem::Func(func) => func.global_id,
1817 CatalogItem::Type(ty) => ty.global_id,
1818 CatalogItem::Secret(secret) => secret.global_id,
1819 CatalogItem::Connection(conn) => conn.global_id,
1820 CatalogItem::MetricSink(metric_sink) => metric_sink.global_id,
1821 CatalogItem::Table(table) => table.global_id_writes(),
1822 }
1823 }
1824
1825 pub fn optimized_plan(&self) -> Option<&Arc<DataflowDescription<OptimizedMirRelationExpr>>> {
1827 match self {
1828 CatalogItem::Index(idx) => idx.optimized_plan.as_ref(),
1829 CatalogItem::MaterializedView(mv) => mv.optimized_plan.as_ref(),
1830 CatalogItem::MetricSink(ms) => ms.optimized_plan.as_ref(),
1831 _ => None,
1832 }
1833 }
1834
1835 pub fn physical_plan(&self) -> Option<&Arc<DataflowDescription<ComputePlan>>> {
1837 match self {
1838 CatalogItem::Index(idx) => idx.physical_plan.as_ref(),
1839 CatalogItem::MaterializedView(mv) => mv.physical_plan.as_ref(),
1840 CatalogItem::MetricSink(ms) => ms.physical_plan.as_ref(),
1841 _ => None,
1842 }
1843 }
1844
1845 pub fn dataflow_metainfo(&self) -> Option<&DataflowMetainfo<Arc<OptimizerNotice>>> {
1847 match self {
1848 CatalogItem::Index(idx) => idx.dataflow_metainfo.as_ref(),
1849 CatalogItem::MaterializedView(mv) => mv.dataflow_metainfo.as_ref(),
1850 CatalogItem::MetricSink(ms) => ms.dataflow_metainfo.as_ref(),
1851 _ => None,
1852 }
1853 }
1854
1855 pub fn dataflow_metainfo_mut(&mut self) -> Option<&mut DataflowMetainfo<Arc<OptimizerNotice>>> {
1857 match self {
1858 CatalogItem::Index(idx) => idx.dataflow_metainfo.as_mut(),
1859 CatalogItem::MaterializedView(mv) => mv.dataflow_metainfo.as_mut(),
1860 CatalogItem::MetricSink(ms) => ms.dataflow_metainfo.as_mut(),
1861 _ => None,
1862 }
1863 }
1864
1865 pub fn plan_fields_mut(
1870 &mut self,
1871 ) -> Option<(
1872 &mut Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1873 &mut Option<Arc<DataflowDescription<ComputePlan>>>,
1874 &mut Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1875 )> {
1876 match self {
1877 CatalogItem::Index(idx) => Some((
1878 &mut idx.optimized_plan,
1879 &mut idx.physical_plan,
1880 &mut idx.dataflow_metainfo,
1881 )),
1882 CatalogItem::MaterializedView(mv) => Some((
1883 &mut mv.optimized_plan,
1884 &mut mv.physical_plan,
1885 &mut mv.dataflow_metainfo,
1886 )),
1887 CatalogItem::MetricSink(ms) => Some((
1888 &mut ms.optimized_plan,
1889 &mut ms.physical_plan,
1890 &mut ms.dataflow_metainfo,
1891 )),
1892 _ => None,
1893 }
1894 }
1895
1896 pub fn is_storage_collection(&self) -> bool {
1898 match self {
1899 CatalogItem::Table(_)
1900 | CatalogItem::Source(_)
1901 | CatalogItem::MaterializedView(_)
1902 | CatalogItem::Sink(_) => true,
1903 CatalogItem::Log(_)
1904 | CatalogItem::View(_)
1905 | CatalogItem::Index(_)
1906 | CatalogItem::Type(_)
1907 | CatalogItem::Func(_)
1908 | CatalogItem::Secret(_)
1909 | CatalogItem::Connection(_)
1910 | CatalogItem::MetricSink(_) => false,
1911 }
1912 }
1913
1914 pub fn relation_desc(&self, version: RelationVersionSelector) -> Option<Cow<'_, RelationDesc>> {
1923 match &self {
1924 CatalogItem::Source(src) => Some(Cow::Borrowed(&src.desc)),
1925 CatalogItem::Log(log) => Some(Cow::Owned(log.variant.desc())),
1926 CatalogItem::Table(tbl) => Some(Cow::Owned(tbl.desc.at_version(version))),
1927 CatalogItem::View(view) => Some(Cow::Borrowed(&view.desc)),
1928 CatalogItem::MaterializedView(mview) => {
1929 Some(Cow::Owned(mview.desc.at_version(version)))
1930 }
1931 CatalogItem::Func(_)
1932 | CatalogItem::Index(_)
1933 | CatalogItem::Sink(_)
1934 | CatalogItem::Secret(_)
1935 | CatalogItem::Connection(_)
1936 | CatalogItem::Type(_)
1937 | CatalogItem::MetricSink(_) => None,
1938 }
1939 }
1940
1941 pub fn func(
1942 &self,
1943 entry: &CatalogEntry,
1944 ) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
1945 match &self {
1946 CatalogItem::Func(func) => Ok(func.inner),
1947 _ => Err(SqlCatalogError::UnexpectedType {
1948 name: entry.name().item.to_string(),
1949 actual_type: entry.item_type(),
1950 expected_type: CatalogItemType::Func,
1951 }),
1952 }
1953 }
1954
1955 pub fn source_desc(
1956 &self,
1957 entry: &CatalogEntry,
1958 ) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
1959 match &self {
1960 CatalogItem::Source(source) => match &source.data_source {
1961 DataSourceDesc::Ingestion { desc, .. }
1962 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => Ok(Some(desc)),
1963 DataSourceDesc::IngestionExport { .. }
1964 | DataSourceDesc::Introspection(_)
1965 | DataSourceDesc::Webhook { .. }
1966 | DataSourceDesc::Progress
1967 | DataSourceDesc::Catalog => Ok(None),
1968 },
1969 _ => Err(SqlCatalogError::UnexpectedType {
1970 name: entry.name().item.to_string(),
1971 actual_type: entry.item_type(),
1972 expected_type: CatalogItemType::Source,
1973 }),
1974 }
1975 }
1976
1977 pub fn is_progress_source(&self) -> bool {
1979 matches!(
1980 self,
1981 CatalogItem::Source(Source {
1982 data_source: DataSourceDesc::Progress,
1983 ..
1984 })
1985 )
1986 }
1987
1988 pub fn references(&self) -> &ResolvedIds {
1991 static EMPTY: LazyLock<ResolvedIds> = LazyLock::new(ResolvedIds::empty);
1992 match self {
1993 CatalogItem::Func(_) => &*EMPTY,
1994 CatalogItem::Index(idx) => &idx.resolved_ids,
1995 CatalogItem::Sink(sink) => &sink.resolved_ids,
1996 CatalogItem::Source(source) => &source.resolved_ids,
1997 CatalogItem::Log(_) => &*EMPTY,
1998 CatalogItem::Table(table) => &table.resolved_ids,
1999 CatalogItem::Type(typ) => &typ.resolved_ids,
2000 CatalogItem::View(view) => &view.resolved_ids,
2001 CatalogItem::MaterializedView(mview) => &mview.resolved_ids,
2002 CatalogItem::Secret(_) => &*EMPTY,
2003 CatalogItem::Connection(connection) => &connection.resolved_ids,
2004 CatalogItem::MetricSink(metric_sink) => &metric_sink.resolved_ids,
2005 }
2006 }
2007
2008 pub fn uses(&self) -> BTreeSet<CatalogItemId> {
2014 let mut uses: BTreeSet<_> = self.references().items().copied().collect();
2015 match self {
2016 CatalogItem::Func(_) => {}
2019 CatalogItem::Index(_) => {}
2020 CatalogItem::Sink(_) => {}
2021 CatalogItem::Source(_) => {}
2022 CatalogItem::Log(_) => {}
2023 CatalogItem::Table(_) => {}
2024 CatalogItem::Type(_) => {}
2025 CatalogItem::View(view) => uses.extend(view.dependencies.0.iter().copied()),
2026 CatalogItem::MaterializedView(mview) => {
2027 uses.extend(mview.dependencies.0.iter().copied())
2028 }
2029 CatalogItem::Secret(_) => {}
2030 CatalogItem::Connection(_) => {}
2031 CatalogItem::MetricSink(_) => {}
2032 }
2033 uses
2034 }
2035
2036 pub fn query_dependencies(&self) -> BTreeSet<CatalogItemId> {
2042 match self {
2043 CatalogItem::Func(_) | CatalogItem::View(_) => self.uses(),
2044 CatalogItem::MaterializedView(mv) => {
2045 let mut dependencies = self.uses();
2046 if let Some(target) = mv.replacement_target {
2047 dependencies.remove(&target);
2048 }
2049 dependencies
2050 }
2051 CatalogItem::Index(_)
2052 | CatalogItem::Sink(_)
2053 | CatalogItem::Source(_)
2054 | CatalogItem::Log(_)
2055 | CatalogItem::Table(_)
2056 | CatalogItem::Type(_)
2057 | CatalogItem::Secret(_)
2058 | CatalogItem::Connection(_)
2059 | CatalogItem::MetricSink(_) => BTreeSet::new(),
2060 }
2061 }
2062
2063 pub fn conn_id(&self) -> Option<&ConnectionId> {
2066 match self {
2067 CatalogItem::View(view) => view.conn_id.as_ref(),
2068 CatalogItem::Index(index) => index.conn_id.as_ref(),
2069 CatalogItem::Table(table) => table.conn_id.as_ref(),
2070 CatalogItem::Log(_)
2071 | CatalogItem::Source(_)
2072 | CatalogItem::Sink(_)
2073 | CatalogItem::MaterializedView(_)
2074 | CatalogItem::Secret(_)
2075 | CatalogItem::Type(_)
2076 | CatalogItem::Func(_)
2077 | CatalogItem::Connection(_)
2078 | CatalogItem::MetricSink(_) => None,
2079 }
2080 }
2081
2082 pub fn set_conn_id(&mut self, conn_id: Option<ConnectionId>) {
2085 match self {
2086 CatalogItem::View(view) => view.conn_id = conn_id,
2087 CatalogItem::Index(index) => index.conn_id = conn_id,
2088 CatalogItem::Table(table) => table.conn_id = conn_id,
2089 CatalogItem::Log(_)
2090 | CatalogItem::Source(_)
2091 | CatalogItem::Sink(_)
2092 | CatalogItem::MaterializedView(_)
2093 | CatalogItem::Secret(_)
2094 | CatalogItem::Type(_)
2095 | CatalogItem::Func(_)
2096 | CatalogItem::Connection(_)
2097 | CatalogItem::MetricSink(_) => (),
2098 }
2099 }
2100
2101 pub fn set_create_sql(&mut self, create_sql: String) {
2110 match self {
2111 CatalogItem::View(view) => view.create_sql = create_sql,
2112 CatalogItem::Index(index) => index.create_sql = create_sql,
2113 CatalogItem::Table(table) => table.create_sql = Some(create_sql),
2114 CatalogItem::Log(_)
2115 | CatalogItem::Source(_)
2116 | CatalogItem::Sink(_)
2117 | CatalogItem::MaterializedView(_)
2118 | CatalogItem::Secret(_)
2119 | CatalogItem::Type(_)
2120 | CatalogItem::Func(_)
2121 | CatalogItem::Connection(_)
2122 | CatalogItem::MetricSink(_) => {
2123 unreachable!("only views, indexes, and tables can be temporary")
2124 }
2125 }
2126 }
2127
2128 pub fn is_temporary(&self) -> bool {
2130 self.conn_id().is_some()
2131 }
2132
2133 pub fn rename_schema_refs(
2134 &self,
2135 database_name: &str,
2136 cur_schema_name: &str,
2137 new_schema_name: &str,
2138 ) -> Result<CatalogItem, (String, String)> {
2139 let do_rewrite = |create_sql: String| -> Result<String, (String, String)> {
2140 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2141 .expect("invalid create sql persisted to catalog")
2142 .into_element()
2143 .ast;
2144
2145 mz_sql::ast::transform::create_stmt_rename_schema_refs(
2147 &mut create_stmt,
2148 database_name,
2149 cur_schema_name,
2150 new_schema_name,
2151 )?;
2152
2153 Ok(create_stmt.to_ast_string_stable())
2154 };
2155
2156 match self {
2157 CatalogItem::Table(i) => {
2158 let mut i = i.clone();
2159 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2160 Ok(CatalogItem::Table(i))
2161 }
2162 CatalogItem::Log(i) => Ok(CatalogItem::Log(i.clone())),
2163 CatalogItem::Source(i) => {
2164 let mut i = i.clone();
2165 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2166 Ok(CatalogItem::Source(i))
2167 }
2168 CatalogItem::Sink(i) => {
2169 let mut i = i.clone();
2170 i.create_sql = do_rewrite(i.create_sql)?;
2171 Ok(CatalogItem::Sink(i))
2172 }
2173 CatalogItem::View(i) => {
2174 let mut i = i.clone();
2175 i.create_sql = do_rewrite(i.create_sql)?;
2176 Ok(CatalogItem::View(i))
2177 }
2178 CatalogItem::MaterializedView(i) => {
2179 let mut i = i.clone();
2180 i.create_sql = do_rewrite(i.create_sql)?;
2181 Ok(CatalogItem::MaterializedView(i))
2182 }
2183 CatalogItem::Index(i) => {
2184 let mut i = i.clone();
2185 i.create_sql = do_rewrite(i.create_sql)?;
2186 Ok(CatalogItem::Index(i))
2187 }
2188 CatalogItem::Secret(i) => {
2189 let mut i = i.clone();
2190 i.create_sql = do_rewrite(i.create_sql)?;
2191 Ok(CatalogItem::Secret(i))
2192 }
2193 CatalogItem::Connection(i) => {
2194 let mut i = i.clone();
2195 i.create_sql = do_rewrite(i.create_sql)?;
2196 Ok(CatalogItem::Connection(i))
2197 }
2198 CatalogItem::Type(i) => {
2199 let mut i = i.clone();
2200 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2201 Ok(CatalogItem::Type(i))
2202 }
2203 CatalogItem::Func(i) => Ok(CatalogItem::Func(i.clone())),
2204 CatalogItem::MetricSink(i) => {
2205 let mut i = i.clone();
2206 i.create_sql = do_rewrite(i.create_sql)?;
2207 Ok(CatalogItem::MetricSink(i))
2208 }
2209 }
2210 }
2211
2212 pub fn rename_item_refs(
2216 &self,
2217 from: FullItemName,
2218 to_item_name: String,
2219 rename_self: bool,
2220 ) -> Result<CatalogItem, String> {
2221 let do_rewrite = |create_sql: String| -> Result<String, String> {
2222 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2223 .expect("invalid create sql persisted to catalog")
2224 .into_element()
2225 .ast;
2226 if rename_self {
2227 mz_sql::ast::transform::create_stmt_rename(&mut create_stmt, to_item_name.clone());
2228 }
2229 mz_sql::ast::transform::create_stmt_rename_refs(&mut create_stmt, from, to_item_name)?;
2231 Ok(create_stmt.to_ast_string_stable())
2232 };
2233
2234 match self {
2235 CatalogItem::Table(i) => {
2236 let mut i = i.clone();
2237 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2238 Ok(CatalogItem::Table(i))
2239 }
2240 CatalogItem::Log(i) => Ok(CatalogItem::Log(i.clone())),
2241 CatalogItem::Source(i) => {
2242 let mut i = i.clone();
2243 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2244 Ok(CatalogItem::Source(i))
2245 }
2246 CatalogItem::Sink(i) => {
2247 let mut i = i.clone();
2248 i.create_sql = do_rewrite(i.create_sql)?;
2249 Ok(CatalogItem::Sink(i))
2250 }
2251 CatalogItem::View(i) => {
2252 let mut i = i.clone();
2253 i.create_sql = do_rewrite(i.create_sql)?;
2254 Ok(CatalogItem::View(i))
2255 }
2256 CatalogItem::MaterializedView(i) => {
2257 let mut i = i.clone();
2258 i.create_sql = do_rewrite(i.create_sql)?;
2259 Ok(CatalogItem::MaterializedView(i))
2260 }
2261 CatalogItem::Index(i) => {
2262 let mut i = i.clone();
2263 i.create_sql = do_rewrite(i.create_sql)?;
2264 Ok(CatalogItem::Index(i))
2265 }
2266 CatalogItem::Secret(i) => {
2267 let mut i = i.clone();
2268 i.create_sql = do_rewrite(i.create_sql)?;
2269 Ok(CatalogItem::Secret(i))
2270 }
2271 CatalogItem::Func(_) | CatalogItem::Type(_) => {
2272 unreachable!("{}s cannot be renamed", self.typ())
2273 }
2274 CatalogItem::Connection(i) => {
2275 let mut i = i.clone();
2276 i.create_sql = do_rewrite(i.create_sql)?;
2277 Ok(CatalogItem::Connection(i))
2278 }
2279 CatalogItem::MetricSink(i) => {
2280 let mut i = i.clone();
2281 i.create_sql = do_rewrite(i.create_sql)?;
2282 Ok(CatalogItem::MetricSink(i))
2283 }
2284 }
2285 }
2286
2287 pub fn replace_item_refs(&self, old_id: CatalogItemId, new_id: CatalogItemId) -> CatalogItem {
2289 let do_rewrite = |create_sql: String| -> String {
2290 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2291 .expect("invalid create sql persisted to catalog")
2292 .into_element()
2293 .ast;
2294 mz_sql::ast::transform::create_stmt_replace_ids(
2295 &mut create_stmt,
2296 &[(old_id, new_id)].into(),
2297 );
2298 create_stmt.to_ast_string_stable()
2299 };
2300
2301 match self {
2302 CatalogItem::Table(i) => {
2303 let mut i = i.clone();
2304 i.create_sql = i.create_sql.map(do_rewrite);
2305 CatalogItem::Table(i)
2306 }
2307 CatalogItem::Log(i) => CatalogItem::Log(i.clone()),
2308 CatalogItem::Source(i) => {
2309 let mut i = i.clone();
2310 i.create_sql = i.create_sql.map(do_rewrite);
2311 CatalogItem::Source(i)
2312 }
2313 CatalogItem::Sink(i) => {
2314 let mut i = i.clone();
2315 i.create_sql = do_rewrite(i.create_sql);
2316 CatalogItem::Sink(i)
2317 }
2318 CatalogItem::View(i) => {
2319 let mut i = i.clone();
2320 i.create_sql = do_rewrite(i.create_sql);
2321 CatalogItem::View(i)
2322 }
2323 CatalogItem::MaterializedView(i) => {
2324 let mut i = i.clone();
2325 i.create_sql = do_rewrite(i.create_sql);
2326 CatalogItem::MaterializedView(i)
2327 }
2328 CatalogItem::Index(i) => {
2329 let mut i = i.clone();
2330 i.create_sql = do_rewrite(i.create_sql);
2331 CatalogItem::Index(i)
2332 }
2333 CatalogItem::Secret(i) => {
2334 let mut i = i.clone();
2335 i.create_sql = do_rewrite(i.create_sql);
2336 CatalogItem::Secret(i)
2337 }
2338 CatalogItem::Func(_) | CatalogItem::Type(_) => {
2339 unreachable!("references of {}s cannot be replaced", self.typ())
2340 }
2341 CatalogItem::Connection(i) => {
2342 let mut i = i.clone();
2343 i.create_sql = do_rewrite(i.create_sql);
2344 CatalogItem::Connection(i)
2345 }
2346 CatalogItem::MetricSink(i) => {
2347 let mut i = i.clone();
2348 i.create_sql = do_rewrite(i.create_sql);
2349 CatalogItem::MetricSink(i)
2350 }
2351 }
2352 }
2353 pub fn update_retain_history(
2356 &mut self,
2357 value: Option<Value>,
2358 window: CompactionWindow,
2359 ) -> Result<Option<WithOptionValue<Raw>>, ()> {
2360 let update = |mut ast: &mut Statement<Raw>| {
2361 macro_rules! update_retain_history {
2363 ( $stmt:ident, $opt:ident, $name:ident ) => {{
2364 let pos = $stmt
2366 .with_options
2367 .iter()
2368 .rposition(|o| o.name == mz_sql_parser::ast::$name::RetainHistory);
2370 if let Some(value) = value {
2371 let next = mz_sql_parser::ast::$opt {
2372 name: mz_sql_parser::ast::$name::RetainHistory,
2373 value: Some(WithOptionValue::RetainHistoryFor(value)),
2374 };
2375 if let Some(idx) = pos {
2376 let previous = $stmt.with_options[idx].clone();
2377 $stmt.with_options[idx] = next;
2378 previous.value
2379 } else {
2380 $stmt.with_options.push(next);
2381 None
2382 }
2383 } else {
2384 if let Some(idx) = pos {
2385 $stmt.with_options.swap_remove(idx).value
2386 } else {
2387 None
2388 }
2389 }
2390 }};
2391 }
2392 let previous = match &mut ast {
2393 Statement::CreateTable(stmt) => {
2394 update_retain_history!(stmt, TableOption, TableOptionName)
2395 }
2396 Statement::CreateIndex(stmt) => {
2397 update_retain_history!(stmt, IndexOption, IndexOptionName)
2398 }
2399 Statement::CreateSource(stmt) => {
2400 update_retain_history!(stmt, CreateSourceOption, CreateSourceOptionName)
2401 }
2402 Statement::CreateMaterializedView(stmt) => {
2403 update_retain_history!(stmt, MaterializedViewOption, MaterializedViewOptionName)
2404 }
2405 _ => {
2406 return Err(());
2407 }
2408 };
2409 Ok(previous)
2410 };
2411
2412 let res = self.update_sql(update)?;
2413 let cw = self
2414 .custom_logical_compaction_window_mut()
2415 .expect("item must have compaction window");
2416 *cw = Some(window);
2417 Ok(res)
2418 }
2419
2420 pub fn update_timestamp_interval(
2423 &mut self,
2424 value: Option<Value>,
2425 interval: Duration,
2426 ) -> Result<Option<WithOptionValue<Raw>>, ()> {
2427 let update = |ast: &mut Statement<Raw>| match ast {
2428 Statement::CreateSource(stmt) => {
2429 let pos = stmt.with_options.iter().rposition(|o| {
2430 o.name == mz_sql_parser::ast::CreateSourceOptionName::TimestampInterval
2431 });
2432 let previous = if let Some(value) = value {
2433 let next = mz_sql_parser::ast::CreateSourceOption {
2434 name: mz_sql_parser::ast::CreateSourceOptionName::TimestampInterval,
2435 value: Some(WithOptionValue::Value(value)),
2436 };
2437 if let Some(idx) = pos {
2438 let previous = stmt.with_options[idx].clone();
2439 stmt.with_options[idx] = next;
2440 previous.value
2441 } else {
2442 stmt.with_options.push(next);
2443 None
2444 }
2445 } else if let Some(idx) = pos {
2446 stmt.with_options.swap_remove(idx).value
2447 } else {
2448 None
2449 };
2450 Ok(previous)
2451 }
2452 _ => Err(()),
2453 };
2454
2455 let previous = self.update_sql(update)?;
2456
2457 match self {
2459 CatalogItem::Source(source) => {
2460 match &mut source.data_source {
2461 DataSourceDesc::Ingestion { desc, .. }
2462 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => {
2463 desc.timestamp_interval = interval;
2464 }
2465 _ => return Err(()),
2466 }
2467 Ok(previous)
2468 }
2469 _ => Err(()),
2470 }
2471 }
2472
2473 pub fn add_column(
2474 &mut self,
2475 name: ColumnName,
2476 typ: SqlColumnType,
2477 sql: RawDataType,
2478 ) -> Result<RelationVersion, PlanError> {
2479 let CatalogItem::Table(table) = self else {
2480 return Err(PlanError::Unsupported {
2481 feature: "adding columns to a non-Table".to_string(),
2482 discussion_no: None,
2483 });
2484 };
2485 let next_version = table.desc.add_column(name.clone(), typ);
2486
2487 let update = |mut ast: &mut Statement<Raw>| match &mut ast {
2488 Statement::CreateTable(stmt) => {
2489 let version = ColumnOptionDef {
2490 name: None,
2491 option: ColumnOption::Versioned {
2492 action: ColumnVersioned::Added,
2493 version: next_version.into(),
2494 },
2495 };
2496 let column = ColumnDef {
2497 name: name.into(),
2498 data_type: sql,
2499 collation: None,
2500 options: vec![version],
2501 };
2502 stmt.columns.push(column);
2503 Ok(())
2504 }
2505 _ => Err(()),
2506 };
2507
2508 self.update_sql(update)
2509 .map_err(|()| PlanError::Unstructured("expected CREATE TABLE statement".to_string()))?;
2510 Ok(next_version)
2511 }
2512
2513 pub fn update_sql<F, T>(&mut self, f: F) -> Result<T, ()>
2516 where
2517 F: FnOnce(&mut Statement<Raw>) -> Result<T, ()>,
2518 {
2519 let create_sql = match self {
2520 CatalogItem::Table(Table { create_sql, .. })
2521 | CatalogItem::Type(Type { create_sql, .. })
2522 | CatalogItem::Source(Source { create_sql, .. }) => create_sql.as_mut(),
2523 CatalogItem::Sink(Sink { create_sql, .. })
2524 | CatalogItem::View(View { create_sql, .. })
2525 | CatalogItem::MaterializedView(MaterializedView { create_sql, .. })
2526 | CatalogItem::Index(Index { create_sql, .. })
2527 | CatalogItem::Secret(Secret { create_sql, .. })
2528 | CatalogItem::Connection(Connection { create_sql, .. })
2529 | CatalogItem::MetricSink(MetricSink { create_sql, .. }) => Some(create_sql),
2530 CatalogItem::Func(_) | CatalogItem::Log(_) => None,
2531 };
2532 let Some(create_sql) = create_sql else {
2533 return Err(());
2534 };
2535 let mut ast = mz_sql_parser::parser::parse_statements(create_sql)
2536 .expect("non-system items must be parseable")
2537 .into_element()
2538 .ast;
2539 debug!("rewrite: {}", ast.to_ast_string_redacted());
2540 let t = f(&mut ast)?;
2541 *create_sql = ast.to_ast_string_stable();
2542 debug!("rewrote: {}", ast.to_ast_string_redacted());
2543 Ok(t)
2544 }
2545
2546 pub fn is_compute_object_on_cluster(&self) -> Option<ClusterId> {
2553 match self {
2554 CatalogItem::Index(index) => Some(index.cluster_id),
2555 CatalogItem::MetricSink(metric_sink) => Some(metric_sink.cluster_id),
2556 CatalogItem::Table(_)
2557 | CatalogItem::Source(_)
2558 | CatalogItem::Log(_)
2559 | CatalogItem::View(_)
2560 | CatalogItem::MaterializedView(_)
2561 | CatalogItem::Sink(_)
2562 | CatalogItem::Type(_)
2563 | CatalogItem::Func(_)
2564 | CatalogItem::Secret(_)
2565 | CatalogItem::Connection(_) => None,
2566 }
2567 }
2568
2569 pub fn is_hydratable(&self) -> bool {
2579 match self {
2580 CatalogItem::Index(_)
2581 | CatalogItem::MaterializedView(_)
2582 | CatalogItem::Sink(_)
2583 | CatalogItem::MetricSink(_) => true,
2584 CatalogItem::Source(source) => matches!(
2585 source.data_source,
2586 DataSourceDesc::Ingestion { .. } | DataSourceDesc::OldSyntaxIngestion { .. }
2587 ),
2588 CatalogItem::Table(_)
2589 | CatalogItem::Log(_)
2590 | CatalogItem::View(_)
2591 | CatalogItem::Type(_)
2592 | CatalogItem::Func(_)
2593 | CatalogItem::Secret(_)
2594 | CatalogItem::Connection(_) => false,
2595 }
2596 }
2597
2598 pub fn cluster_id(&self) -> Option<ClusterId> {
2599 match self {
2600 CatalogItem::MaterializedView(mv) => Some(mv.cluster_id),
2601 CatalogItem::Index(index) => Some(index.cluster_id),
2602 CatalogItem::MetricSink(metric_sink) => Some(metric_sink.cluster_id),
2603 CatalogItem::Source(source) => match &source.data_source {
2604 DataSourceDesc::Ingestion { cluster_id, .. }
2605 | DataSourceDesc::OldSyntaxIngestion { cluster_id, .. } => Some(*cluster_id),
2606 DataSourceDesc::IngestionExport { .. } => None,
2610 DataSourceDesc::Webhook { cluster_id, .. } => Some(*cluster_id),
2611 DataSourceDesc::Introspection(_)
2612 | DataSourceDesc::Progress
2613 | DataSourceDesc::Catalog => None,
2614 },
2615 CatalogItem::Sink(sink) => Some(sink.cluster_id),
2616 CatalogItem::Table(_)
2617 | CatalogItem::Log(_)
2618 | CatalogItem::View(_)
2619 | CatalogItem::Type(_)
2620 | CatalogItem::Func(_)
2621 | CatalogItem::Secret(_)
2622 | CatalogItem::Connection(_) => None,
2623 }
2624 }
2625
2626 pub fn custom_logical_compaction_window(&self) -> Option<CompactionWindow> {
2629 match self {
2630 CatalogItem::Table(table) => table.custom_logical_compaction_window,
2631 CatalogItem::Source(source) => source.custom_logical_compaction_window,
2632 CatalogItem::Index(index) => index.custom_logical_compaction_window,
2633 CatalogItem::MaterializedView(mview) => mview.custom_logical_compaction_window,
2634 CatalogItem::Log(_)
2635 | CatalogItem::View(_)
2636 | CatalogItem::Sink(_)
2637 | CatalogItem::Type(_)
2638 | CatalogItem::Func(_)
2639 | CatalogItem::Secret(_)
2640 | CatalogItem::Connection(_)
2641 | CatalogItem::MetricSink(_) => None,
2642 }
2643 }
2644
2645 pub fn custom_logical_compaction_window_mut(
2649 &mut self,
2650 ) -> Option<&mut Option<CompactionWindow>> {
2651 let cw = match self {
2652 CatalogItem::Table(table) => &mut table.custom_logical_compaction_window,
2653 CatalogItem::Source(source) => &mut source.custom_logical_compaction_window,
2654 CatalogItem::Index(index) => &mut index.custom_logical_compaction_window,
2655 CatalogItem::MaterializedView(mview) => &mut mview.custom_logical_compaction_window,
2656 CatalogItem::Log(_)
2657 | CatalogItem::View(_)
2658 | CatalogItem::Sink(_)
2659 | CatalogItem::Type(_)
2660 | CatalogItem::Func(_)
2661 | CatalogItem::Secret(_)
2662 | CatalogItem::Connection(_)
2663 | CatalogItem::MetricSink(_) => return None,
2664 };
2665 Some(cw)
2666 }
2667
2668 pub fn initial_logical_compaction_window(&self) -> Option<CompactionWindow> {
2676 let custom_logical_compaction_window = match self {
2677 CatalogItem::Table(_)
2678 | CatalogItem::Source(_)
2679 | CatalogItem::Index(_)
2680 | CatalogItem::MaterializedView(_) => self.custom_logical_compaction_window(),
2681 CatalogItem::Log(_)
2682 | CatalogItem::View(_)
2683 | CatalogItem::Sink(_)
2684 | CatalogItem::Type(_)
2685 | CatalogItem::Func(_)
2686 | CatalogItem::Secret(_)
2687 | CatalogItem::Connection(_)
2688 | CatalogItem::MetricSink(_) => return None,
2689 };
2690 Some(custom_logical_compaction_window.unwrap_or(CompactionWindow::Default))
2691 }
2692
2693 pub fn is_retained_metrics_object(&self) -> bool {
2697 match self {
2698 CatalogItem::Table(table) => table.is_retained_metrics_object,
2699 CatalogItem::Source(source) => source.is_retained_metrics_object,
2700 CatalogItem::Index(index) => index.is_retained_metrics_object,
2701 CatalogItem::Log(_)
2702 | CatalogItem::View(_)
2703 | CatalogItem::MaterializedView(_)
2704 | CatalogItem::Sink(_)
2705 | CatalogItem::Type(_)
2706 | CatalogItem::Func(_)
2707 | CatalogItem::Secret(_)
2708 | CatalogItem::Connection(_)
2709 | CatalogItem::MetricSink(_) => false,
2710 }
2711 }
2712
2713 pub fn to_serialized(&self) -> (String, GlobalId, BTreeMap<RelationVersion, GlobalId>) {
2714 match self {
2715 CatalogItem::Table(table) => {
2716 let create_sql = table
2717 .create_sql
2718 .clone()
2719 .expect("builtin tables cannot be serialized");
2720 let mut collections = table.collections.clone();
2721 let global_id = collections
2722 .remove(&RelationVersion::root())
2723 .expect("at least one version");
2724 (create_sql, global_id, collections)
2725 }
2726 CatalogItem::Log(_) => unreachable!("builtin logs cannot be serialized"),
2727 CatalogItem::Source(source) => {
2728 assert!(
2729 !matches!(source.data_source, DataSourceDesc::Introspection(_)),
2730 "cannot serialize introspection/builtin sources",
2731 );
2732 let create_sql = source
2733 .create_sql
2734 .clone()
2735 .expect("builtin sources cannot be serialized");
2736 (create_sql, source.global_id, BTreeMap::new())
2737 }
2738 CatalogItem::View(view) => (view.create_sql.clone(), view.global_id, BTreeMap::new()),
2739 CatalogItem::MaterializedView(mview) => {
2740 let mut collections = mview.collections.clone();
2741 let global_id = collections
2742 .remove(&RelationVersion::root())
2743 .expect("at least one version");
2744 (mview.create_sql.clone(), global_id, collections)
2745 }
2746 CatalogItem::Index(index) => {
2747 (index.create_sql.clone(), index.global_id, BTreeMap::new())
2748 }
2749 CatalogItem::Sink(sink) => (sink.create_sql.clone(), sink.global_id, BTreeMap::new()),
2750 CatalogItem::Type(typ) => {
2751 let create_sql = typ
2752 .create_sql
2753 .clone()
2754 .expect("builtin types cannot be serialized");
2755 (create_sql, typ.global_id, BTreeMap::new())
2756 }
2757 CatalogItem::Secret(secret) => {
2758 (secret.create_sql.clone(), secret.global_id, BTreeMap::new())
2759 }
2760 CatalogItem::Connection(connection) => (
2761 connection.create_sql.clone(),
2762 connection.global_id,
2763 BTreeMap::new(),
2764 ),
2765 CatalogItem::Func(_) => unreachable!("cannot serialize functions yet"),
2766 CatalogItem::MetricSink(ms) => (ms.create_sql.clone(), ms.global_id, BTreeMap::new()),
2767 }
2768 }
2769
2770 pub fn into_serialized(self) -> (String, GlobalId, BTreeMap<RelationVersion, GlobalId>) {
2771 match self {
2772 CatalogItem::Table(mut table) => {
2773 let create_sql = table
2774 .create_sql
2775 .expect("builtin tables cannot be serialized");
2776 let global_id = table
2777 .collections
2778 .remove(&RelationVersion::root())
2779 .expect("at least one version");
2780 (create_sql, global_id, table.collections)
2781 }
2782 CatalogItem::Log(_) => unreachable!("builtin logs cannot be serialized"),
2783 CatalogItem::Source(source) => {
2784 assert!(
2785 !matches!(source.data_source, DataSourceDesc::Introspection(_)),
2786 "cannot serialize introspection/builtin sources",
2787 );
2788 let create_sql = source
2789 .create_sql
2790 .expect("builtin sources cannot be serialized");
2791 (create_sql, source.global_id, BTreeMap::new())
2792 }
2793 CatalogItem::View(view) => (view.create_sql, view.global_id, BTreeMap::new()),
2794 CatalogItem::MaterializedView(mut mview) => {
2795 let global_id = mview
2796 .collections
2797 .remove(&RelationVersion::root())
2798 .expect("at least one version");
2799 (mview.create_sql, global_id, mview.collections)
2800 }
2801 CatalogItem::Index(index) => (index.create_sql, index.global_id, BTreeMap::new()),
2802 CatalogItem::Sink(sink) => (sink.create_sql, sink.global_id, BTreeMap::new()),
2803 CatalogItem::Type(typ) => {
2804 let create_sql = typ.create_sql.expect("builtin types cannot be serialized");
2805 (create_sql, typ.global_id, BTreeMap::new())
2806 }
2807 CatalogItem::Secret(secret) => (secret.create_sql, secret.global_id, BTreeMap::new()),
2808 CatalogItem::Connection(connection) => {
2809 (connection.create_sql, connection.global_id, BTreeMap::new())
2810 }
2811 CatalogItem::Func(_) => unreachable!("cannot serialize functions yet"),
2812 CatalogItem::MetricSink(ms) => (ms.create_sql, ms.global_id, BTreeMap::new()),
2813 }
2814 }
2815
2816 pub fn global_id_for_version(&self, version: RelationVersionSelector) -> Option<GlobalId> {
2819 let collections = match self {
2820 CatalogItem::MaterializedView(mv) => &mv.collections,
2821 CatalogItem::Table(table) => &table.collections,
2822 CatalogItem::Source(source) => return Some(source.global_id),
2823 CatalogItem::Log(log) => return Some(log.global_id),
2824 CatalogItem::View(view) => return Some(view.global_id),
2825 CatalogItem::Sink(sink) => return Some(sink.global_id),
2826 CatalogItem::Index(index) => return Some(index.global_id),
2827 CatalogItem::Type(ty) => return Some(ty.global_id),
2828 CatalogItem::Func(func) => return Some(func.global_id),
2829 CatalogItem::Secret(secret) => return Some(secret.global_id),
2830 CatalogItem::Connection(conn) => return Some(conn.global_id),
2831 CatalogItem::MetricSink(metric_sink) => return Some(metric_sink.global_id),
2832 };
2833 match version {
2834 RelationVersionSelector::Latest => collections.values().last().copied(),
2835 RelationVersionSelector::Specific(version) => collections.get(&version).copied(),
2836 }
2837 }
2838}
2839
2840impl CatalogEntry {
2841 pub fn relation_desc_latest(&self) -> Option<Cow<'_, RelationDesc>> {
2844 self.item.relation_desc(RelationVersionSelector::Latest)
2845 }
2846
2847 pub fn has_columns(&self) -> bool {
2849 match self.item() {
2850 CatalogItem::Type(Type { details, .. }) => {
2851 matches!(details.typ, CatalogType::Record { .. })
2852 }
2853 _ => self.relation_desc_latest().is_some(),
2854 }
2855 }
2856
2857 pub fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
2859 self.item.func(self)
2860 }
2861
2862 pub fn index(&self) -> Option<&Index> {
2864 match self.item() {
2865 CatalogItem::Index(idx) => Some(idx),
2866 _ => None,
2867 }
2868 }
2869
2870 pub fn materialized_view(&self) -> Option<&MaterializedView> {
2872 match self.item() {
2873 CatalogItem::MaterializedView(mv) => Some(mv),
2874 _ => None,
2875 }
2876 }
2877
2878 pub fn table(&self) -> Option<&Table> {
2880 match self.item() {
2881 CatalogItem::Table(tbl) => Some(tbl),
2882 _ => None,
2883 }
2884 }
2885
2886 pub fn source(&self) -> Option<&Source> {
2888 match self.item() {
2889 CatalogItem::Source(src) => Some(src),
2890 _ => None,
2891 }
2892 }
2893
2894 pub fn sink(&self) -> Option<&Sink> {
2896 match self.item() {
2897 CatalogItem::Sink(sink) => Some(sink),
2898 _ => None,
2899 }
2900 }
2901
2902 pub fn secret(&self) -> Option<&Secret> {
2904 match self.item() {
2905 CatalogItem::Secret(secret) => Some(secret),
2906 _ => None,
2907 }
2908 }
2909
2910 pub fn connection(&self) -> Result<&Connection, SqlCatalogError> {
2911 match self.item() {
2912 CatalogItem::Connection(connection) => Ok(connection),
2913 _ => {
2914 let db_name = match self.name().qualifiers.database_spec {
2915 ResolvedDatabaseSpecifier::Ambient => "".to_string(),
2916 ResolvedDatabaseSpecifier::Id(id) => format!("{id}."),
2917 };
2918 Err(SqlCatalogError::UnknownConnection(format!(
2919 "{}{}.{}",
2920 db_name,
2921 self.name().qualifiers.schema_spec,
2922 self.name().item
2923 )))
2924 }
2925 }
2926 }
2927
2928 pub fn source_desc(
2931 &self,
2932 ) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
2933 self.item.source_desc(self)
2934 }
2935
2936 pub fn is_connection(&self) -> bool {
2938 matches!(self.item(), CatalogItem::Connection(_))
2939 }
2940
2941 pub fn is_table(&self) -> bool {
2943 matches!(self.item(), CatalogItem::Table(_))
2944 }
2945
2946 pub fn is_source(&self) -> bool {
2949 matches!(self.item(), CatalogItem::Source(_))
2950 }
2951
2952 pub fn subsource_details(
2955 &self,
2956 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
2957 match &self.item() {
2958 CatalogItem::Source(source) => match &source.data_source {
2959 DataSourceDesc::IngestionExport {
2960 ingestion_id,
2961 external_reference,
2962 details,
2963 data_config: _,
2964 } => Some((*ingestion_id, external_reference, details)),
2965 _ => None,
2966 },
2967 _ => None,
2968 }
2969 }
2970
2971 pub fn source_export_details(
2974 &self,
2975 ) -> Option<(
2976 CatalogItemId,
2977 &UnresolvedItemName,
2978 &SourceExportDetails,
2979 &SourceExportDataConfig<ReferencedConnection>,
2980 )> {
2981 match &self.item() {
2982 CatalogItem::Source(source) => match &source.data_source {
2983 DataSourceDesc::IngestionExport {
2984 ingestion_id,
2985 external_reference,
2986 details,
2987 data_config,
2988 } => Some((*ingestion_id, external_reference, details, data_config)),
2989 _ => None,
2990 },
2991 CatalogItem::Table(table) => match &table.data_source {
2992 TableDataSource::DataSource {
2993 desc:
2994 DataSourceDesc::IngestionExport {
2995 ingestion_id,
2996 external_reference,
2997 details,
2998 data_config,
2999 },
3000 timeline: _,
3001 } => Some((*ingestion_id, external_reference, details, data_config)),
3002 _ => None,
3003 },
3004 _ => None,
3005 }
3006 }
3007
3008 pub fn is_progress_source(&self) -> bool {
3010 self.item().is_progress_source()
3011 }
3012
3013 pub fn progress_id(&self) -> Option<CatalogItemId> {
3015 match &self.item() {
3016 CatalogItem::Source(source) => match &source.data_source {
3017 DataSourceDesc::Ingestion { .. } => Some(self.id),
3018 DataSourceDesc::OldSyntaxIngestion {
3019 progress_subsource, ..
3020 } => Some(*progress_subsource),
3021 DataSourceDesc::IngestionExport { .. }
3022 | DataSourceDesc::Introspection(_)
3023 | DataSourceDesc::Progress
3024 | DataSourceDesc::Webhook { .. }
3025 | DataSourceDesc::Catalog => None,
3026 },
3027 CatalogItem::Table(_)
3028 | CatalogItem::Log(_)
3029 | CatalogItem::View(_)
3030 | CatalogItem::MaterializedView(_)
3031 | CatalogItem::Sink(_)
3032 | CatalogItem::Index(_)
3033 | CatalogItem::Type(_)
3034 | CatalogItem::Func(_)
3035 | CatalogItem::Secret(_)
3036 | CatalogItem::Connection(_)
3037 | CatalogItem::MetricSink(_) => None,
3038 }
3039 }
3040
3041 pub fn is_sink(&self) -> bool {
3043 matches!(self.item(), CatalogItem::Sink(_))
3044 }
3045
3046 pub fn is_materialized_view(&self) -> bool {
3048 matches!(self.item(), CatalogItem::MaterializedView(_))
3049 }
3050
3051 pub fn is_view(&self) -> bool {
3053 matches!(self.item(), CatalogItem::View(_))
3054 }
3055
3056 pub fn is_secret(&self) -> bool {
3058 matches!(self.item(), CatalogItem::Secret(_))
3059 }
3060
3061 pub fn is_introspection_source(&self) -> bool {
3063 matches!(self.item(), CatalogItem::Log(_))
3064 }
3065
3066 pub fn is_index(&self) -> bool {
3068 matches!(self.item(), CatalogItem::Index(_))
3069 }
3070
3071 pub fn is_metric_sink(&self) -> bool {
3073 matches!(self.item(), CatalogItem::MetricSink(_))
3074 }
3075
3076 pub fn is_relation(&self) -> bool {
3078 mz_sql::catalog::ObjectType::from(self.item_type()).is_relation()
3079 }
3080
3081 pub fn references(&self) -> &ResolvedIds {
3084 self.item.references()
3085 }
3086
3087 pub fn uses(&self) -> BTreeSet<CatalogItemId> {
3093 self.item.uses()
3094 }
3095
3096 pub fn item(&self) -> &CatalogItem {
3098 &self.item
3099 }
3100
3101 pub fn item_mut(&mut self) -> &mut CatalogItem {
3104 &mut self.item
3105 }
3106
3107 pub fn id(&self) -> CatalogItemId {
3109 self.id
3110 }
3111
3112 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
3114 self.item().global_ids()
3115 }
3116
3117 pub fn latest_global_id(&self) -> GlobalId {
3118 self.item().latest_global_id()
3119 }
3120
3121 pub fn oid(&self) -> u32 {
3123 self.oid
3124 }
3125
3126 pub fn name(&self) -> &QualifiedItemName {
3128 &self.name
3129 }
3130
3131 pub fn referenced_by(&self) -> &[CatalogItemId] {
3133 &self.referenced_by
3134 }
3135
3136 pub fn used_by(&self) -> &[CatalogItemId] {
3138 &self.used_by
3139 }
3140
3141 pub fn conn_id(&self) -> Option<&ConnectionId> {
3144 self.item.conn_id()
3145 }
3146
3147 pub fn owner_id(&self) -> &RoleId {
3149 &self.owner_id
3150 }
3151
3152 pub fn privileges(&self) -> &PrivilegeMap {
3154 &self.privileges
3155 }
3156
3157 pub fn comment_object_id(&self) -> CommentObjectId {
3159 use CatalogItemType::*;
3160 match self.item_type() {
3161 Table => CommentObjectId::Table(self.id),
3162 Source => CommentObjectId::Source(self.id),
3163 Sink => CommentObjectId::Sink(self.id),
3164 View => CommentObjectId::View(self.id),
3165 MaterializedView => CommentObjectId::MaterializedView(self.id),
3166 Index => CommentObjectId::Index(self.id),
3167 Func => CommentObjectId::Func(self.id),
3168 Connection => CommentObjectId::Connection(self.id),
3169 Type => CommentObjectId::Type(self.id),
3170 Secret => CommentObjectId::Secret(self.id),
3171 MetricSink => CommentObjectId::MetricSink(self.id),
3172 }
3173 }
3174}
3175
3176#[derive(Debug, Clone, Default)]
3177pub struct CommentsMap {
3178 map: BTreeMap<CommentObjectId, BTreeMap<Option<usize>, String>>,
3179}
3180
3181impl CommentsMap {
3182 pub fn update_comment(
3183 &mut self,
3184 object_id: CommentObjectId,
3185 sub_component: Option<usize>,
3186 comment: Option<String>,
3187 ) -> Option<String> {
3188 let object_comments = self.map.entry(object_id).or_default();
3189
3190 let (empty, prev) = if let Some(comment) = comment {
3192 let prev = object_comments.insert(sub_component, comment);
3193 (false, prev)
3194 } else {
3195 let prev = object_comments.remove(&sub_component);
3196 (object_comments.is_empty(), prev)
3197 };
3198
3199 if empty {
3201 self.map.remove(&object_id);
3202 }
3203
3204 prev
3206 }
3207
3208 pub fn drop_comments(
3214 &mut self,
3215 object_ids: &BTreeSet<CommentObjectId>,
3216 ) -> Vec<(CommentObjectId, Option<usize>, String)> {
3217 let mut removed_comments = Vec::new();
3218
3219 for object_id in object_ids {
3220 if let Some(comments) = self.map.remove(object_id) {
3221 let removed = comments
3222 .into_iter()
3223 .map(|(sub_comp, comment)| (object_id.clone(), sub_comp, comment));
3224 removed_comments.extend(removed);
3225 }
3226 }
3227
3228 removed_comments
3229 }
3230
3231 pub fn iter(&self) -> impl Iterator<Item = (CommentObjectId, Option<usize>, &str)> {
3232 self.map
3233 .iter()
3234 .map(|(id, comments)| {
3235 comments
3236 .iter()
3237 .map(|(pos, comment)| (*id, *pos, comment.as_str()))
3238 })
3239 .flatten()
3240 }
3241
3242 pub fn get_object_comments(
3243 &self,
3244 object_id: CommentObjectId,
3245 ) -> Option<&BTreeMap<Option<usize>, String>> {
3246 self.map.get(&object_id)
3247 }
3248}
3249
3250impl Serialize for CommentsMap {
3251 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3252 where
3253 S: serde::Serializer,
3254 {
3255 let comment_count = self
3256 .map
3257 .iter()
3258 .map(|(_object_id, comments)| comments.len())
3259 .sum();
3260
3261 let mut seq = serializer.serialize_seq(Some(comment_count))?;
3262 for (object_id, sub) in &self.map {
3263 for (sub_component, comment) in sub {
3264 seq.serialize_element(&(
3265 format!("{object_id:?}"),
3266 format!("{sub_component:?}"),
3267 comment,
3268 ))?;
3269 }
3270 }
3271 seq.end()
3272 }
3273}
3274
3275#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Default)]
3276pub struct DefaultPrivileges {
3277 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
3278 privileges: BTreeMap<DefaultPrivilegeObject, RoleDefaultPrivileges>,
3279}
3280
3281#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Default)]
3284struct RoleDefaultPrivileges(
3285 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
3287 BTreeMap<RoleId, DefaultPrivilegeAclItem>,
3288);
3289
3290impl Deref for RoleDefaultPrivileges {
3291 type Target = BTreeMap<RoleId, DefaultPrivilegeAclItem>;
3292
3293 fn deref(&self) -> &Self::Target {
3294 &self.0
3295 }
3296}
3297
3298impl DerefMut for RoleDefaultPrivileges {
3299 fn deref_mut(&mut self) -> &mut Self::Target {
3300 &mut self.0
3301 }
3302}
3303
3304impl DefaultPrivileges {
3305 pub fn grant(&mut self, object: DefaultPrivilegeObject, privilege: DefaultPrivilegeAclItem) {
3307 if privilege.acl_mode.is_empty() {
3308 return;
3309 }
3310
3311 let privileges = self.privileges.entry(object).or_default();
3312 if let Some(default_privilege) = privileges.get_mut(&privilege.grantee) {
3313 default_privilege.acl_mode |= privilege.acl_mode;
3314 } else {
3315 privileges.insert(privilege.grantee, privilege);
3316 }
3317 }
3318
3319 pub fn revoke(&mut self, object: &DefaultPrivilegeObject, privilege: &DefaultPrivilegeAclItem) {
3321 if let Some(privileges) = self.privileges.get_mut(object) {
3322 if let Some(default_privilege) = privileges.get_mut(&privilege.grantee) {
3323 default_privilege.acl_mode =
3324 default_privilege.acl_mode.difference(privilege.acl_mode);
3325 if default_privilege.acl_mode.is_empty() {
3326 privileges.remove(&privilege.grantee);
3327 }
3328 }
3329 if privileges.is_empty() {
3330 self.privileges.remove(object);
3331 }
3332 }
3333 }
3334
3335 pub fn get_privileges_for_grantee(
3338 &self,
3339 object: &DefaultPrivilegeObject,
3340 grantee: &RoleId,
3341 ) -> Option<&AclMode> {
3342 self.privileges
3343 .get(object)
3344 .and_then(|privileges| privileges.get(grantee))
3345 .map(|privilege| &privilege.acl_mode)
3346 }
3347
3348 pub fn get_applicable_privileges(
3350 &self,
3351 role_id: RoleId,
3352 database_id: Option<DatabaseId>,
3353 schema_id: Option<SchemaId>,
3354 object_type: mz_sql::catalog::ObjectType,
3355 ) -> impl Iterator<Item = DefaultPrivilegeAclItem> + '_ {
3356 let privilege_object_type = if object_type.is_relation() {
3360 mz_sql::catalog::ObjectType::Table
3361 } else {
3362 object_type
3363 };
3364 let valid_acl_mode = rbac::all_object_privileges(SystemObjectType::Object(object_type));
3365
3366 [
3370 DefaultPrivilegeObject {
3371 role_id,
3372 database_id,
3373 schema_id,
3374 object_type: privilege_object_type,
3375 },
3376 DefaultPrivilegeObject {
3377 role_id,
3378 database_id,
3379 schema_id: None,
3380 object_type: privilege_object_type,
3381 },
3382 DefaultPrivilegeObject {
3383 role_id,
3384 database_id: None,
3385 schema_id: None,
3386 object_type: privilege_object_type,
3387 },
3388 DefaultPrivilegeObject {
3389 role_id: RoleId::Public,
3390 database_id,
3391 schema_id,
3392 object_type: privilege_object_type,
3393 },
3394 DefaultPrivilegeObject {
3395 role_id: RoleId::Public,
3396 database_id,
3397 schema_id: None,
3398 object_type: privilege_object_type,
3399 },
3400 DefaultPrivilegeObject {
3401 role_id: RoleId::Public,
3402 database_id: None,
3403 schema_id: None,
3404 object_type: privilege_object_type,
3405 },
3406 ]
3407 .into_iter()
3408 .filter_map(|object| self.privileges.get(&object))
3409 .flat_map(|acl_map| acl_map.values())
3410 .fold(
3412 BTreeMap::new(),
3413 |mut accum, DefaultPrivilegeAclItem { grantee, acl_mode }| {
3414 let accum_acl_mode = accum.entry(grantee).or_insert_with(AclMode::empty);
3415 *accum_acl_mode |= *acl_mode;
3416 accum
3417 },
3418 )
3419 .into_iter()
3420 .map(move |(grantee, acl_mode)| (grantee, acl_mode & valid_acl_mode))
3425 .filter(|(_, acl_mode)| !acl_mode.is_empty())
3427 .map(|(grantee, acl_mode)| DefaultPrivilegeAclItem {
3428 grantee: *grantee,
3429 acl_mode,
3430 })
3431 }
3432
3433 pub fn iter(
3434 &self,
3435 ) -> impl Iterator<
3436 Item = (
3437 &DefaultPrivilegeObject,
3438 impl Iterator<Item = &DefaultPrivilegeAclItem>,
3439 ),
3440 > {
3441 self.privileges
3442 .iter()
3443 .map(|(object, acl_map)| (object, acl_map.values()))
3444 }
3445}
3446
3447#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3448pub struct ClusterConfig {
3449 pub variant: ClusterVariant,
3450 pub workload_class: Option<String>,
3451}
3452
3453impl ClusterConfig {
3454 pub fn features(&self) -> Option<&OptimizerFeatureOverrides> {
3455 match &self.variant {
3456 ClusterVariant::Managed(managed) => Some(&managed.optimizer_feature_overrides),
3457 ClusterVariant::Unmanaged => None,
3458 }
3459 }
3460}
3461
3462impl From<ClusterConfig> for durable::ClusterConfig {
3463 fn from(config: ClusterConfig) -> Self {
3464 Self {
3465 variant: config.variant.into(),
3466 workload_class: config.workload_class,
3467 }
3468 }
3469}
3470
3471impl From<durable::ClusterConfig> for ClusterConfig {
3472 fn from(config: durable::ClusterConfig) -> Self {
3473 Self {
3474 variant: config.variant.into(),
3475 workload_class: config.workload_class,
3476 }
3477 }
3478}
3479
3480#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3481pub struct ClusterVariantManaged {
3482 pub size: String,
3483 pub availability_zones: Vec<String>,
3484 pub logging: ReplicaLogging,
3485 pub arrangement_compression: bool,
3487 pub replication_factor: u32,
3488 pub optimizer_feature_overrides: OptimizerFeatureOverrides,
3489 pub schedule: ClusterSchedule,
3490 pub auto_scaling_strategy: Option<AutoScalingStrategy>,
3493 pub reconfiguration: Option<ReconfigurationState>,
3495 pub burst: Option<BurstState>,
3497}
3498
3499#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3501pub struct ManagedReplicaConfigShape<'a> {
3502 pub size: &'a str,
3503 pub availability_zones: &'a [String],
3504 pub logging: &'a ReplicaLogging,
3505 pub arrangement_compression: bool,
3506}
3507
3508impl<'a> ManagedReplicaConfigShape<'a> {
3509 pub fn new(
3511 size: &'a str,
3512 availability_zones: &'a [String],
3513 logging: &'a ReplicaLogging,
3514 arrangement_compression: bool,
3515 ) -> Self {
3516 Self {
3517 size,
3518 availability_zones,
3519 logging,
3520 arrangement_compression,
3521 }
3522 }
3523}
3524
3525impl ClusterVariantManaged {
3526 pub fn replica_config_shape(&self) -> ManagedReplicaConfigShape<'_> {
3528 let ClusterVariantManaged {
3529 size,
3530 availability_zones,
3531 logging,
3532 arrangement_compression,
3533 replication_factor: _,
3534 optimizer_feature_overrides: _,
3535 schedule: _,
3536 auto_scaling_strategy: _,
3537 reconfiguration: _,
3538 burst: _,
3539 } = self;
3540 ManagedReplicaConfigShape::new(size, availability_zones, logging, *arrangement_compression)
3541 }
3542
3543 pub fn realized_reconfiguration_target(&self) -> ReconfigurationTarget {
3545 let ClusterVariantManaged {
3546 size,
3547 availability_zones,
3548 logging,
3549 arrangement_compression,
3550 replication_factor,
3551 optimizer_feature_overrides: _,
3552 schedule: _,
3553 auto_scaling_strategy: _,
3554 reconfiguration: _,
3555 burst: _,
3556 } = self;
3557 ReconfigurationTarget {
3558 size: size.clone(),
3559 replication_factor: *replication_factor,
3560 availability_zones: availability_zones.clone(),
3561 logging: logging.clone(),
3562 arrangement_compression: *arrangement_compression,
3563 }
3564 }
3565
3566 pub fn apply_reconfiguration_target(&mut self, target: ReconfigurationTarget) {
3570 let ReconfigurationTarget {
3573 size,
3574 replication_factor,
3575 availability_zones,
3576 logging,
3577 arrangement_compression,
3578 } = target;
3579 self.size = size;
3580 self.replication_factor = replication_factor;
3581 self.availability_zones = availability_zones;
3582 self.logging = logging;
3583 self.arrangement_compression = arrangement_compression;
3584 }
3585
3586 pub fn has_unwarranted_burst_record(&self) -> bool {
3591 let Some(record) = &self.burst else {
3592 return false;
3593 };
3594 let hydration_size = self
3595 .auto_scaling_strategy
3596 .as_ref()
3597 .and_then(|strategy| strategy.on_hydration.as_ref())
3598 .map(|policy| policy.hydration_size.as_str());
3599 !mz_adapter_types::cluster_state::burst_record_warranted(
3600 &record.burst_size,
3601 self.replication_factor,
3602 hydration_size,
3603 )
3604 }
3605}
3606
3607impl From<ClusterVariantManaged> for durable::ClusterVariantManaged {
3608 fn from(managed: ClusterVariantManaged) -> Self {
3609 let ClusterVariantManaged {
3612 size,
3613 availability_zones,
3614 logging,
3615 arrangement_compression,
3616 replication_factor,
3617 optimizer_feature_overrides,
3618 schedule,
3619 auto_scaling_strategy,
3620 reconfiguration,
3621 burst,
3622 } = managed;
3623 Self {
3624 size,
3625 availability_zones,
3626 logging,
3627 arrangement_compression,
3628 replication_factor,
3629 optimizer_feature_overrides: optimizer_feature_overrides.into(),
3630 schedule,
3631 auto_scaling_strategy,
3632 reconfiguration: reconfiguration.map(Into::into),
3633 burst: burst.map(Into::into),
3634 }
3635 }
3636}
3637
3638impl From<durable::ClusterVariantManaged> for ClusterVariantManaged {
3639 fn from(managed: durable::ClusterVariantManaged) -> Self {
3640 let durable::ClusterVariantManaged {
3643 size,
3644 availability_zones,
3645 logging,
3646 arrangement_compression,
3647 replication_factor,
3648 optimizer_feature_overrides,
3649 schedule,
3650 auto_scaling_strategy,
3651 reconfiguration,
3652 burst,
3653 } = managed;
3654 Self {
3655 size,
3656 availability_zones,
3657 logging,
3658 arrangement_compression,
3659 replication_factor,
3660 optimizer_feature_overrides: optimizer_feature_overrides.into(),
3661 schedule,
3662 auto_scaling_strategy,
3663 reconfiguration: reconfiguration.map(Into::into),
3664 burst: burst.map(Into::into),
3665 }
3666 }
3667}
3668
3669#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3676pub struct ReconfigurationState {
3677 pub target: ReconfigurationTarget,
3678 pub deadline: Timestamp,
3679 pub on_timeout: OnTimeoutAction,
3680 pub status: ReconfigurationStatus,
3681}
3682
3683#[derive(
3702 Clone,
3703 Copy,
3704 Debug,
3705 Deserialize,
3706 Serialize,
3707 PartialOrd,
3708 PartialEq,
3709 Eq,
3710 Ord
3711)]
3712pub enum ReconfigurationStatus {
3713 InProgress,
3714 Finalized,
3715 TimedOut,
3716 Cancelled,
3717 ResourceExhausted,
3718}
3719
3720impl From<ReconfigurationStatus> for durable::ReconfigurationStatus {
3721 fn from(status: ReconfigurationStatus) -> Self {
3722 match status {
3723 ReconfigurationStatus::InProgress => durable::ReconfigurationStatus::InProgress,
3724 ReconfigurationStatus::Finalized => durable::ReconfigurationStatus::Finalized,
3725 ReconfigurationStatus::TimedOut => durable::ReconfigurationStatus::TimedOut,
3726 ReconfigurationStatus::Cancelled => durable::ReconfigurationStatus::Cancelled,
3727 ReconfigurationStatus::ResourceExhausted => {
3728 durable::ReconfigurationStatus::ResourceExhausted
3729 }
3730 }
3731 }
3732}
3733
3734impl From<durable::ReconfigurationStatus> for ReconfigurationStatus {
3735 fn from(status: durable::ReconfigurationStatus) -> Self {
3736 match status {
3737 durable::ReconfigurationStatus::InProgress => ReconfigurationStatus::InProgress,
3738 durable::ReconfigurationStatus::Finalized => ReconfigurationStatus::Finalized,
3739 durable::ReconfigurationStatus::TimedOut => ReconfigurationStatus::TimedOut,
3740 durable::ReconfigurationStatus::Cancelled => ReconfigurationStatus::Cancelled,
3741 durable::ReconfigurationStatus::ResourceExhausted => {
3742 ReconfigurationStatus::ResourceExhausted
3743 }
3744 }
3745 }
3746}
3747
3748impl ReconfigurationState {
3749 pub fn is_in_progress(&self) -> bool {
3750 matches!(self.status, ReconfigurationStatus::InProgress)
3751 }
3752}
3753
3754impl From<ReconfigurationState> for durable::ReconfigurationState {
3755 fn from(state: ReconfigurationState) -> Self {
3756 let ReconfigurationState {
3759 target,
3760 deadline,
3761 on_timeout,
3762 status,
3763 } = state;
3764 Self {
3765 target: target.into(),
3766 deadline,
3767 on_timeout,
3768 status: status.into(),
3769 }
3770 }
3771}
3772
3773impl From<durable::ReconfigurationState> for ReconfigurationState {
3774 fn from(state: durable::ReconfigurationState) -> Self {
3775 let durable::ReconfigurationState {
3778 target,
3779 deadline,
3780 on_timeout,
3781 status,
3782 } = state;
3783 Self {
3784 target: target.into(),
3785 deadline,
3786 on_timeout,
3787 status: status.into(),
3788 }
3789 }
3790}
3791
3792#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3794pub struct ReconfigurationTarget {
3795 pub size: String,
3796 pub replication_factor: u32,
3797 pub availability_zones: Vec<String>,
3798 pub logging: ReplicaLogging,
3799 pub arrangement_compression: bool,
3800}
3801
3802impl ReconfigurationTarget {
3803 pub fn matches_realized_config(&self, managed: &ClusterVariantManaged) -> bool {
3805 self == &managed.realized_reconfiguration_target()
3806 }
3807}
3808
3809impl From<ReconfigurationTarget> for durable::ReconfigurationTarget {
3810 fn from(target: ReconfigurationTarget) -> Self {
3811 let ReconfigurationTarget {
3814 size,
3815 replication_factor,
3816 availability_zones,
3817 logging,
3818 arrangement_compression,
3819 } = target;
3820 Self {
3821 size,
3822 replication_factor,
3823 availability_zones,
3824 logging,
3825 arrangement_compression,
3826 }
3827 }
3828}
3829
3830impl From<durable::ReconfigurationTarget> for ReconfigurationTarget {
3831 fn from(target: durable::ReconfigurationTarget) -> Self {
3832 let durable::ReconfigurationTarget {
3835 size,
3836 replication_factor,
3837 availability_zones,
3838 logging,
3839 arrangement_compression,
3840 } = target;
3841 Self {
3842 size,
3843 replication_factor,
3844 availability_zones,
3845 logging,
3846 arrangement_compression,
3847 }
3848 }
3849}
3850
3851#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3853pub struct BurstState {
3854 pub burst_size: String,
3855 pub linger_duration: Duration,
3856 pub steady_hydrated_at: Option<Timestamp>,
3857}
3858
3859impl From<BurstState> for durable::BurstState {
3860 fn from(burst: BurstState) -> Self {
3861 let BurstState {
3864 burst_size,
3865 linger_duration,
3866 steady_hydrated_at,
3867 } = burst;
3868 Self {
3869 burst_size,
3870 linger_duration,
3871 steady_hydrated_at,
3872 }
3873 }
3874}
3875
3876impl From<durable::BurstState> for BurstState {
3877 fn from(burst: durable::BurstState) -> Self {
3878 let durable::BurstState {
3881 burst_size,
3882 linger_duration,
3883 steady_hydrated_at,
3884 } = burst;
3885 Self {
3886 burst_size,
3887 linger_duration,
3888 steady_hydrated_at,
3889 }
3890 }
3891}
3892
3893#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3894pub enum ClusterVariant {
3895 Managed(ClusterVariantManaged),
3896 Unmanaged,
3897}
3898
3899impl From<ClusterVariant> for durable::ClusterVariant {
3900 fn from(variant: ClusterVariant) -> Self {
3901 match variant {
3902 ClusterVariant::Managed(managed) => Self::Managed(managed.into()),
3903 ClusterVariant::Unmanaged => Self::Unmanaged,
3904 }
3905 }
3906}
3907
3908impl From<durable::ClusterVariant> for ClusterVariant {
3909 fn from(variant: durable::ClusterVariant) -> Self {
3910 match variant {
3911 durable::ClusterVariant::Managed(managed) => Self::Managed(managed.into()),
3912 durable::ClusterVariant::Unmanaged => Self::Unmanaged,
3913 }
3914 }
3915}
3916
3917impl mz_sql::catalog::CatalogDatabase for Database {
3918 fn name(&self) -> &str {
3919 &self.name
3920 }
3921
3922 fn id(&self) -> DatabaseId {
3923 self.id
3924 }
3925
3926 fn has_schemas(&self) -> bool {
3927 !self.schemas_by_name.is_empty()
3928 }
3929
3930 fn schema_ids(&self) -> &BTreeMap<String, SchemaId> {
3931 &self.schemas_by_name
3932 }
3933
3934 #[allow(clippy::as_conversions)]
3936 fn schemas(&self) -> Vec<&dyn CatalogSchema> {
3937 self.schemas_by_id
3938 .values()
3939 .map(|schema| schema as &dyn CatalogSchema)
3940 .collect()
3941 }
3942
3943 fn owner_id(&self) -> RoleId {
3944 self.owner_id
3945 }
3946
3947 fn privileges(&self) -> &PrivilegeMap {
3948 &self.privileges
3949 }
3950}
3951
3952impl mz_sql::catalog::CatalogSchema for Schema {
3953 fn database(&self) -> &ResolvedDatabaseSpecifier {
3954 &self.name.database
3955 }
3956
3957 fn name(&self) -> &QualifiedSchemaName {
3958 &self.name
3959 }
3960
3961 fn id(&self) -> &SchemaSpecifier {
3962 &self.id
3963 }
3964
3965 fn has_items(&self) -> bool {
3966 !self.items.is_empty() || !self.types.is_empty() || !self.functions.is_empty()
3970 }
3971
3972 fn item_ids(&self) -> Box<dyn Iterator<Item = CatalogItemId> + '_> {
3973 Box::new(
3974 self.items
3975 .values()
3976 .chain(self.functions.values())
3977 .chain(self.types.values())
3978 .copied(),
3979 )
3980 }
3981
3982 fn owner_id(&self) -> RoleId {
3983 self.owner_id
3984 }
3985
3986 fn privileges(&self) -> &PrivilegeMap {
3987 &self.privileges
3988 }
3989}
3990
3991impl mz_sql::catalog::CatalogRole for Role {
3992 fn name(&self) -> &str {
3993 &self.name
3994 }
3995
3996 fn id(&self) -> RoleId {
3997 self.id
3998 }
3999
4000 fn membership(&self) -> &BTreeMap<RoleId, RoleId> {
4001 &self.membership.map
4002 }
4003
4004 fn attributes(&self) -> &RoleAttributes {
4005 &self.attributes
4006 }
4007
4008 fn vars(&self) -> &BTreeMap<String, OwnedVarInput> {
4009 &self.vars.map
4010 }
4011}
4012
4013impl mz_sql::catalog::CatalogNetworkPolicy for NetworkPolicy {
4014 fn name(&self) -> &str {
4015 &self.name
4016 }
4017
4018 fn id(&self) -> NetworkPolicyId {
4019 self.id
4020 }
4021
4022 fn owner_id(&self) -> RoleId {
4023 self.owner_id
4024 }
4025
4026 fn privileges(&self) -> &PrivilegeMap {
4027 &self.privileges
4028 }
4029}
4030
4031impl mz_sql::catalog::CatalogCluster<'_> for Cluster {
4032 fn name(&self) -> &str {
4033 &self.name
4034 }
4035
4036 fn id(&self) -> ClusterId {
4037 self.id
4038 }
4039
4040 fn bound_objects(&self) -> &BTreeSet<CatalogItemId> {
4041 &self.bound_objects
4042 }
4043
4044 fn replica_ids(&self) -> &BTreeMap<String, ReplicaId> {
4045 &self.replica_id_by_name_
4046 }
4047
4048 #[allow(clippy::as_conversions)]
4050 fn replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>> {
4051 self.replicas()
4052 .map(|replica| replica as &dyn CatalogClusterReplica)
4053 .collect()
4054 }
4055
4056 fn replica(&self, id: ReplicaId) -> &dyn CatalogClusterReplica<'_> {
4057 self.replica(id).expect("catalog out of sync")
4058 }
4059
4060 fn owner_id(&self) -> RoleId {
4061 self.owner_id
4062 }
4063
4064 fn privileges(&self) -> &PrivilegeMap {
4065 &self.privileges
4066 }
4067
4068 fn is_managed(&self) -> bool {
4069 self.is_managed()
4070 }
4071
4072 fn managed_size(&self) -> Option<&str> {
4073 match &self.config.variant {
4074 ClusterVariant::Managed(ClusterVariantManaged { size, .. }) => Some(size),
4075 ClusterVariant::Unmanaged => None,
4076 }
4077 }
4078
4079 fn schedule(&self) -> Option<&ClusterSchedule> {
4080 match &self.config.variant {
4081 ClusterVariant::Managed(ClusterVariantManaged { schedule, .. }) => Some(schedule),
4082 ClusterVariant::Unmanaged => None,
4083 }
4084 }
4085
4086 fn replication_factor(&self) -> Option<u32> {
4087 match &self.config.variant {
4088 ClusterVariant::Managed(ClusterVariantManaged {
4089 replication_factor, ..
4090 }) => Some(*replication_factor),
4091 ClusterVariant::Unmanaged => None,
4092 }
4093 }
4094
4095 fn auto_scaling_strategy(&self) -> Option<&AutoScalingStrategy> {
4096 match &self.config.variant {
4097 ClusterVariant::Managed(ClusterVariantManaged {
4098 auto_scaling_strategy,
4099 ..
4100 }) => auto_scaling_strategy.as_ref(),
4101 ClusterVariant::Unmanaged => None,
4102 }
4103 }
4104 fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError> {
4105 self.try_to_plan()
4106 }
4107}
4108
4109impl mz_sql::catalog::CatalogClusterReplica<'_> for ClusterReplica {
4110 fn name(&self) -> &str {
4111 &self.name
4112 }
4113
4114 fn cluster_id(&self) -> ClusterId {
4115 self.cluster_id
4116 }
4117
4118 fn replica_id(&self) -> ReplicaId {
4119 self.replica_id
4120 }
4121
4122 fn owner_id(&self) -> RoleId {
4123 self.owner_id
4124 }
4125
4126 fn internal(&self) -> bool {
4127 self.config.location.internal()
4128 }
4129}
4130
4131impl mz_sql::catalog::CatalogItem for CatalogEntry {
4132 fn name(&self) -> &QualifiedItemName {
4133 self.name()
4134 }
4135
4136 fn id(&self) -> CatalogItemId {
4137 self.id()
4138 }
4139
4140 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_> {
4141 Box::new(self.global_ids())
4142 }
4143
4144 fn oid(&self) -> u32 {
4145 self.oid()
4146 }
4147
4148 fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
4149 self.func()
4150 }
4151
4152 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
4153 self.source_desc()
4154 }
4155
4156 fn connection(
4157 &self,
4158 ) -> Result<mz_storage_types::connections::Connection<ReferencedConnection>, SqlCatalogError>
4159 {
4160 Ok(self.connection()?.details.to_connection())
4161 }
4162
4163 fn create_sql(&self) -> &str {
4164 match self.item() {
4165 CatalogItem::Table(Table { create_sql, .. }) => {
4166 create_sql.as_deref().unwrap_or("<builtin>")
4167 }
4168 CatalogItem::Source(Source { create_sql, .. }) => {
4169 create_sql.as_deref().unwrap_or("<builtin>")
4170 }
4171 CatalogItem::Sink(Sink { create_sql, .. }) => create_sql,
4172 CatalogItem::View(View { create_sql, .. }) => create_sql,
4173 CatalogItem::MaterializedView(MaterializedView { create_sql, .. }) => create_sql,
4174 CatalogItem::Index(Index { create_sql, .. }) => create_sql,
4175 CatalogItem::Type(Type { create_sql, .. }) => {
4176 create_sql.as_deref().unwrap_or("<builtin>")
4177 }
4178 CatalogItem::Secret(Secret { create_sql, .. }) => create_sql,
4179 CatalogItem::Connection(Connection { create_sql, .. }) => create_sql,
4180 CatalogItem::MetricSink(MetricSink { create_sql, .. }) => create_sql,
4181 CatalogItem::Func(_) => "<builtin>",
4182 CatalogItem::Log(_) => "<builtin>",
4183 }
4184 }
4185
4186 fn item_type(&self) -> SqlCatalogItemType {
4187 self.item().typ()
4188 }
4189
4190 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)> {
4191 if let CatalogItem::Index(Index { keys, on, .. }) = self.item() {
4192 Some((keys, *on))
4193 } else {
4194 None
4195 }
4196 }
4197
4198 fn writable_table_details(&self) -> Option<&[Expr<Aug>]> {
4199 if let CatalogItem::Table(Table {
4200 data_source: TableDataSource::TableWrites { defaults },
4201 ..
4202 }) = self.item()
4203 {
4204 Some(defaults.as_slice())
4205 } else {
4206 None
4207 }
4208 }
4209
4210 fn replacement_target(&self) -> Option<CatalogItemId> {
4211 if let CatalogItem::MaterializedView(mv) = self.item() {
4212 mv.replacement_target
4213 } else {
4214 None
4215 }
4216 }
4217
4218 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>> {
4219 if let CatalogItem::Type(Type { details, .. }) = self.item() {
4220 Some(details)
4221 } else {
4222 None
4223 }
4224 }
4225
4226 fn references(&self) -> &ResolvedIds {
4227 self.references()
4228 }
4229
4230 fn uses(&self) -> BTreeSet<CatalogItemId> {
4231 self.uses()
4232 }
4233
4234 fn referenced_by(&self) -> &[CatalogItemId] {
4235 self.referenced_by()
4236 }
4237
4238 fn used_by(&self) -> &[CatalogItemId] {
4239 self.used_by()
4240 }
4241
4242 fn subsource_details(
4243 &self,
4244 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
4245 self.subsource_details()
4246 }
4247
4248 fn source_export_details(
4249 &self,
4250 ) -> Option<(
4251 CatalogItemId,
4252 &UnresolvedItemName,
4253 &SourceExportDetails,
4254 &SourceExportDataConfig<ReferencedConnection>,
4255 )> {
4256 self.source_export_details()
4257 }
4258
4259 fn is_progress_source(&self) -> bool {
4260 self.is_progress_source()
4261 }
4262
4263 fn progress_id(&self) -> Option<CatalogItemId> {
4264 self.progress_id()
4265 }
4266
4267 fn owner_id(&self) -> RoleId {
4268 self.owner_id
4269 }
4270
4271 fn privileges(&self) -> &PrivilegeMap {
4272 &self.privileges
4273 }
4274
4275 fn cluster_id(&self) -> Option<ClusterId> {
4276 self.item().cluster_id()
4277 }
4278
4279 fn at_version(
4280 &self,
4281 version: RelationVersionSelector,
4282 ) -> Box<dyn mz_sql::catalog::CatalogCollectionItem> {
4283 Box::new(CatalogCollectionEntry {
4284 entry: self.clone(),
4285 version,
4286 })
4287 }
4288
4289 fn latest_version(&self) -> Option<RelationVersion> {
4290 self.table().map(|t| t.desc.latest_version())
4291 }
4292}
4293
4294#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4296pub struct StateUpdate {
4297 pub kind: StateUpdateKind,
4298 pub ts: Timestamp,
4299 pub diff: StateDiff,
4300}
4301
4302#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4306pub enum StateUpdateKind {
4307 Role(durable::objects::Role),
4308 RoleAuth(durable::objects::RoleAuth),
4309 Database(durable::objects::Database),
4310 Schema(durable::objects::Schema),
4311 DefaultPrivilege(durable::objects::DefaultPrivilege),
4312 SystemPrivilege(MzAclItem),
4313 SystemConfiguration(durable::objects::SystemConfiguration),
4314 Cluster(durable::objects::Cluster),
4315 ClusterSystemConfiguration(durable::objects::ClusterSystemConfiguration),
4316 NetworkPolicy(durable::objects::NetworkPolicy),
4317 IntrospectionSourceIndex(durable::objects::IntrospectionSourceIndex),
4318 ClusterReplica(durable::objects::ClusterReplica),
4319 ReplicaSystemConfiguration(durable::objects::ReplicaSystemConfiguration),
4320 SourceReferences(durable::objects::SourceReferences),
4321 SystemObjectMapping(durable::objects::SystemObjectMapping),
4322 Item(durable::objects::Item),
4323 Comment(durable::objects::Comment),
4324 AuditLog(durable::objects::AuditLog),
4325 StorageCollectionMetadata(durable::objects::StorageCollectionMetadata),
4327 UnfinalizedShard(durable::objects::UnfinalizedShard),
4328}
4329
4330#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
4332pub enum StateDiff {
4333 Retraction,
4334 Addition,
4335}
4336
4337impl From<StateDiff> for Diff {
4338 fn from(diff: StateDiff) -> Self {
4339 match diff {
4340 StateDiff::Retraction => Diff::MINUS_ONE,
4341 StateDiff::Addition => Diff::ONE,
4342 }
4343 }
4344}
4345impl TryFrom<Diff> for StateDiff {
4346 type Error = String;
4347
4348 fn try_from(diff: Diff) -> Result<Self, Self::Error> {
4349 match diff {
4350 Diff::MINUS_ONE => Ok(Self::Retraction),
4351 Diff::ONE => Ok(Self::Addition),
4352 diff => Err(format!("invalid diff {diff}")),
4353 }
4354 }
4355}