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 plan_fields_mut(
1860 &mut self,
1861 ) -> Option<(
1862 &mut Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1863 &mut Option<Arc<DataflowDescription<ComputePlan>>>,
1864 &mut Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1865 )> {
1866 match self {
1867 CatalogItem::Index(idx) => Some((
1868 &mut idx.optimized_plan,
1869 &mut idx.physical_plan,
1870 &mut idx.dataflow_metainfo,
1871 )),
1872 CatalogItem::MaterializedView(mv) => Some((
1873 &mut mv.optimized_plan,
1874 &mut mv.physical_plan,
1875 &mut mv.dataflow_metainfo,
1876 )),
1877 CatalogItem::MetricSink(ms) => Some((
1878 &mut ms.optimized_plan,
1879 &mut ms.physical_plan,
1880 &mut ms.dataflow_metainfo,
1881 )),
1882 _ => None,
1883 }
1884 }
1885
1886 pub fn is_storage_collection(&self) -> bool {
1888 match self {
1889 CatalogItem::Table(_)
1890 | CatalogItem::Source(_)
1891 | CatalogItem::MaterializedView(_)
1892 | CatalogItem::Sink(_) => true,
1893 CatalogItem::Log(_)
1894 | CatalogItem::View(_)
1895 | CatalogItem::Index(_)
1896 | CatalogItem::Type(_)
1897 | CatalogItem::Func(_)
1898 | CatalogItem::Secret(_)
1899 | CatalogItem::Connection(_)
1900 | CatalogItem::MetricSink(_) => false,
1901 }
1902 }
1903
1904 pub fn relation_desc(&self, version: RelationVersionSelector) -> Option<Cow<'_, RelationDesc>> {
1913 match &self {
1914 CatalogItem::Source(src) => Some(Cow::Borrowed(&src.desc)),
1915 CatalogItem::Log(log) => Some(Cow::Owned(log.variant.desc())),
1916 CatalogItem::Table(tbl) => Some(Cow::Owned(tbl.desc.at_version(version))),
1917 CatalogItem::View(view) => Some(Cow::Borrowed(&view.desc)),
1918 CatalogItem::MaterializedView(mview) => {
1919 Some(Cow::Owned(mview.desc.at_version(version)))
1920 }
1921 CatalogItem::Func(_)
1922 | CatalogItem::Index(_)
1923 | CatalogItem::Sink(_)
1924 | CatalogItem::Secret(_)
1925 | CatalogItem::Connection(_)
1926 | CatalogItem::Type(_)
1927 | CatalogItem::MetricSink(_) => None,
1928 }
1929 }
1930
1931 pub fn func(
1932 &self,
1933 entry: &CatalogEntry,
1934 ) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
1935 match &self {
1936 CatalogItem::Func(func) => Ok(func.inner),
1937 _ => Err(SqlCatalogError::UnexpectedType {
1938 name: entry.name().item.to_string(),
1939 actual_type: entry.item_type(),
1940 expected_type: CatalogItemType::Func,
1941 }),
1942 }
1943 }
1944
1945 pub fn source_desc(
1946 &self,
1947 entry: &CatalogEntry,
1948 ) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
1949 match &self {
1950 CatalogItem::Source(source) => match &source.data_source {
1951 DataSourceDesc::Ingestion { desc, .. }
1952 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => Ok(Some(desc)),
1953 DataSourceDesc::IngestionExport { .. }
1954 | DataSourceDesc::Introspection(_)
1955 | DataSourceDesc::Webhook { .. }
1956 | DataSourceDesc::Progress
1957 | DataSourceDesc::Catalog => Ok(None),
1958 },
1959 _ => Err(SqlCatalogError::UnexpectedType {
1960 name: entry.name().item.to_string(),
1961 actual_type: entry.item_type(),
1962 expected_type: CatalogItemType::Source,
1963 }),
1964 }
1965 }
1966
1967 pub fn is_progress_source(&self) -> bool {
1969 matches!(
1970 self,
1971 CatalogItem::Source(Source {
1972 data_source: DataSourceDesc::Progress,
1973 ..
1974 })
1975 )
1976 }
1977
1978 pub fn references(&self) -> &ResolvedIds {
1981 static EMPTY: LazyLock<ResolvedIds> = LazyLock::new(ResolvedIds::empty);
1982 match self {
1983 CatalogItem::Func(_) => &*EMPTY,
1984 CatalogItem::Index(idx) => &idx.resolved_ids,
1985 CatalogItem::Sink(sink) => &sink.resolved_ids,
1986 CatalogItem::Source(source) => &source.resolved_ids,
1987 CatalogItem::Log(_) => &*EMPTY,
1988 CatalogItem::Table(table) => &table.resolved_ids,
1989 CatalogItem::Type(typ) => &typ.resolved_ids,
1990 CatalogItem::View(view) => &view.resolved_ids,
1991 CatalogItem::MaterializedView(mview) => &mview.resolved_ids,
1992 CatalogItem::Secret(_) => &*EMPTY,
1993 CatalogItem::Connection(connection) => &connection.resolved_ids,
1994 CatalogItem::MetricSink(metric_sink) => &metric_sink.resolved_ids,
1995 }
1996 }
1997
1998 pub fn uses(&self) -> BTreeSet<CatalogItemId> {
2004 let mut uses: BTreeSet<_> = self.references().items().copied().collect();
2005 match self {
2006 CatalogItem::Func(_) => {}
2009 CatalogItem::Index(_) => {}
2010 CatalogItem::Sink(_) => {}
2011 CatalogItem::Source(_) => {}
2012 CatalogItem::Log(_) => {}
2013 CatalogItem::Table(_) => {}
2014 CatalogItem::Type(_) => {}
2015 CatalogItem::View(view) => uses.extend(view.dependencies.0.iter().copied()),
2016 CatalogItem::MaterializedView(mview) => {
2017 uses.extend(mview.dependencies.0.iter().copied())
2018 }
2019 CatalogItem::Secret(_) => {}
2020 CatalogItem::Connection(_) => {}
2021 CatalogItem::MetricSink(_) => {}
2022 }
2023 uses
2024 }
2025
2026 pub fn conn_id(&self) -> Option<&ConnectionId> {
2029 match self {
2030 CatalogItem::View(view) => view.conn_id.as_ref(),
2031 CatalogItem::Index(index) => index.conn_id.as_ref(),
2032 CatalogItem::Table(table) => table.conn_id.as_ref(),
2033 CatalogItem::Log(_)
2034 | CatalogItem::Source(_)
2035 | CatalogItem::Sink(_)
2036 | CatalogItem::MaterializedView(_)
2037 | CatalogItem::Secret(_)
2038 | CatalogItem::Type(_)
2039 | CatalogItem::Func(_)
2040 | CatalogItem::Connection(_)
2041 | CatalogItem::MetricSink(_) => None,
2042 }
2043 }
2044
2045 pub fn set_conn_id(&mut self, conn_id: Option<ConnectionId>) {
2048 match self {
2049 CatalogItem::View(view) => view.conn_id = conn_id,
2050 CatalogItem::Index(index) => index.conn_id = conn_id,
2051 CatalogItem::Table(table) => table.conn_id = conn_id,
2052 CatalogItem::Log(_)
2053 | CatalogItem::Source(_)
2054 | CatalogItem::Sink(_)
2055 | CatalogItem::MaterializedView(_)
2056 | CatalogItem::Secret(_)
2057 | CatalogItem::Type(_)
2058 | CatalogItem::Func(_)
2059 | CatalogItem::Connection(_)
2060 | CatalogItem::MetricSink(_) => (),
2061 }
2062 }
2063
2064 pub fn set_create_sql(&mut self, create_sql: String) {
2073 match self {
2074 CatalogItem::View(view) => view.create_sql = create_sql,
2075 CatalogItem::Index(index) => index.create_sql = create_sql,
2076 CatalogItem::Table(table) => table.create_sql = Some(create_sql),
2077 CatalogItem::Log(_)
2078 | CatalogItem::Source(_)
2079 | CatalogItem::Sink(_)
2080 | CatalogItem::MaterializedView(_)
2081 | CatalogItem::Secret(_)
2082 | CatalogItem::Type(_)
2083 | CatalogItem::Func(_)
2084 | CatalogItem::Connection(_)
2085 | CatalogItem::MetricSink(_) => {
2086 unreachable!("only views, indexes, and tables can be temporary")
2087 }
2088 }
2089 }
2090
2091 pub fn is_temporary(&self) -> bool {
2093 self.conn_id().is_some()
2094 }
2095
2096 pub fn rename_schema_refs(
2097 &self,
2098 database_name: &str,
2099 cur_schema_name: &str,
2100 new_schema_name: &str,
2101 ) -> Result<CatalogItem, (String, String)> {
2102 let do_rewrite = |create_sql: String| -> Result<String, (String, String)> {
2103 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2104 .expect("invalid create sql persisted to catalog")
2105 .into_element()
2106 .ast;
2107
2108 mz_sql::ast::transform::create_stmt_rename_schema_refs(
2110 &mut create_stmt,
2111 database_name,
2112 cur_schema_name,
2113 new_schema_name,
2114 )?;
2115
2116 Ok(create_stmt.to_ast_string_stable())
2117 };
2118
2119 match self {
2120 CatalogItem::Table(i) => {
2121 let mut i = i.clone();
2122 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2123 Ok(CatalogItem::Table(i))
2124 }
2125 CatalogItem::Log(i) => Ok(CatalogItem::Log(i.clone())),
2126 CatalogItem::Source(i) => {
2127 let mut i = i.clone();
2128 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2129 Ok(CatalogItem::Source(i))
2130 }
2131 CatalogItem::Sink(i) => {
2132 let mut i = i.clone();
2133 i.create_sql = do_rewrite(i.create_sql)?;
2134 Ok(CatalogItem::Sink(i))
2135 }
2136 CatalogItem::View(i) => {
2137 let mut i = i.clone();
2138 i.create_sql = do_rewrite(i.create_sql)?;
2139 Ok(CatalogItem::View(i))
2140 }
2141 CatalogItem::MaterializedView(i) => {
2142 let mut i = i.clone();
2143 i.create_sql = do_rewrite(i.create_sql)?;
2144 Ok(CatalogItem::MaterializedView(i))
2145 }
2146 CatalogItem::Index(i) => {
2147 let mut i = i.clone();
2148 i.create_sql = do_rewrite(i.create_sql)?;
2149 Ok(CatalogItem::Index(i))
2150 }
2151 CatalogItem::Secret(i) => {
2152 let mut i = i.clone();
2153 i.create_sql = do_rewrite(i.create_sql)?;
2154 Ok(CatalogItem::Secret(i))
2155 }
2156 CatalogItem::Connection(i) => {
2157 let mut i = i.clone();
2158 i.create_sql = do_rewrite(i.create_sql)?;
2159 Ok(CatalogItem::Connection(i))
2160 }
2161 CatalogItem::Type(i) => {
2162 let mut i = i.clone();
2163 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2164 Ok(CatalogItem::Type(i))
2165 }
2166 CatalogItem::Func(i) => Ok(CatalogItem::Func(i.clone())),
2167 CatalogItem::MetricSink(i) => {
2168 let mut i = i.clone();
2169 i.create_sql = do_rewrite(i.create_sql)?;
2170 Ok(CatalogItem::MetricSink(i))
2171 }
2172 }
2173 }
2174
2175 pub fn rename_item_refs(
2179 &self,
2180 from: FullItemName,
2181 to_item_name: String,
2182 rename_self: bool,
2183 ) -> Result<CatalogItem, String> {
2184 let do_rewrite = |create_sql: String| -> Result<String, String> {
2185 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2186 .expect("invalid create sql persisted to catalog")
2187 .into_element()
2188 .ast;
2189 if rename_self {
2190 mz_sql::ast::transform::create_stmt_rename(&mut create_stmt, to_item_name.clone());
2191 }
2192 mz_sql::ast::transform::create_stmt_rename_refs(&mut create_stmt, from, to_item_name)?;
2194 Ok(create_stmt.to_ast_string_stable())
2195 };
2196
2197 match self {
2198 CatalogItem::Table(i) => {
2199 let mut i = i.clone();
2200 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2201 Ok(CatalogItem::Table(i))
2202 }
2203 CatalogItem::Log(i) => Ok(CatalogItem::Log(i.clone())),
2204 CatalogItem::Source(i) => {
2205 let mut i = i.clone();
2206 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2207 Ok(CatalogItem::Source(i))
2208 }
2209 CatalogItem::Sink(i) => {
2210 let mut i = i.clone();
2211 i.create_sql = do_rewrite(i.create_sql)?;
2212 Ok(CatalogItem::Sink(i))
2213 }
2214 CatalogItem::View(i) => {
2215 let mut i = i.clone();
2216 i.create_sql = do_rewrite(i.create_sql)?;
2217 Ok(CatalogItem::View(i))
2218 }
2219 CatalogItem::MaterializedView(i) => {
2220 let mut i = i.clone();
2221 i.create_sql = do_rewrite(i.create_sql)?;
2222 Ok(CatalogItem::MaterializedView(i))
2223 }
2224 CatalogItem::Index(i) => {
2225 let mut i = i.clone();
2226 i.create_sql = do_rewrite(i.create_sql)?;
2227 Ok(CatalogItem::Index(i))
2228 }
2229 CatalogItem::Secret(i) => {
2230 let mut i = i.clone();
2231 i.create_sql = do_rewrite(i.create_sql)?;
2232 Ok(CatalogItem::Secret(i))
2233 }
2234 CatalogItem::Func(_) | CatalogItem::Type(_) => {
2235 unreachable!("{}s cannot be renamed", self.typ())
2236 }
2237 CatalogItem::Connection(i) => {
2238 let mut i = i.clone();
2239 i.create_sql = do_rewrite(i.create_sql)?;
2240 Ok(CatalogItem::Connection(i))
2241 }
2242 CatalogItem::MetricSink(i) => {
2243 let mut i = i.clone();
2244 i.create_sql = do_rewrite(i.create_sql)?;
2245 Ok(CatalogItem::MetricSink(i))
2246 }
2247 }
2248 }
2249
2250 pub fn replace_item_refs(&self, old_id: CatalogItemId, new_id: CatalogItemId) -> CatalogItem {
2252 let do_rewrite = |create_sql: String| -> String {
2253 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2254 .expect("invalid create sql persisted to catalog")
2255 .into_element()
2256 .ast;
2257 mz_sql::ast::transform::create_stmt_replace_ids(
2258 &mut create_stmt,
2259 &[(old_id, new_id)].into(),
2260 );
2261 create_stmt.to_ast_string_stable()
2262 };
2263
2264 match self {
2265 CatalogItem::Table(i) => {
2266 let mut i = i.clone();
2267 i.create_sql = i.create_sql.map(do_rewrite);
2268 CatalogItem::Table(i)
2269 }
2270 CatalogItem::Log(i) => CatalogItem::Log(i.clone()),
2271 CatalogItem::Source(i) => {
2272 let mut i = i.clone();
2273 i.create_sql = i.create_sql.map(do_rewrite);
2274 CatalogItem::Source(i)
2275 }
2276 CatalogItem::Sink(i) => {
2277 let mut i = i.clone();
2278 i.create_sql = do_rewrite(i.create_sql);
2279 CatalogItem::Sink(i)
2280 }
2281 CatalogItem::View(i) => {
2282 let mut i = i.clone();
2283 i.create_sql = do_rewrite(i.create_sql);
2284 CatalogItem::View(i)
2285 }
2286 CatalogItem::MaterializedView(i) => {
2287 let mut i = i.clone();
2288 i.create_sql = do_rewrite(i.create_sql);
2289 CatalogItem::MaterializedView(i)
2290 }
2291 CatalogItem::Index(i) => {
2292 let mut i = i.clone();
2293 i.create_sql = do_rewrite(i.create_sql);
2294 CatalogItem::Index(i)
2295 }
2296 CatalogItem::Secret(i) => {
2297 let mut i = i.clone();
2298 i.create_sql = do_rewrite(i.create_sql);
2299 CatalogItem::Secret(i)
2300 }
2301 CatalogItem::Func(_) | CatalogItem::Type(_) => {
2302 unreachable!("references of {}s cannot be replaced", self.typ())
2303 }
2304 CatalogItem::Connection(i) => {
2305 let mut i = i.clone();
2306 i.create_sql = do_rewrite(i.create_sql);
2307 CatalogItem::Connection(i)
2308 }
2309 CatalogItem::MetricSink(i) => {
2310 let mut i = i.clone();
2311 i.create_sql = do_rewrite(i.create_sql);
2312 CatalogItem::MetricSink(i)
2313 }
2314 }
2315 }
2316 pub fn update_retain_history(
2319 &mut self,
2320 value: Option<Value>,
2321 window: CompactionWindow,
2322 ) -> Result<Option<WithOptionValue<Raw>>, ()> {
2323 let update = |mut ast: &mut Statement<Raw>| {
2324 macro_rules! update_retain_history {
2326 ( $stmt:ident, $opt:ident, $name:ident ) => {{
2327 let pos = $stmt
2329 .with_options
2330 .iter()
2331 .rposition(|o| o.name == mz_sql_parser::ast::$name::RetainHistory);
2333 if let Some(value) = value {
2334 let next = mz_sql_parser::ast::$opt {
2335 name: mz_sql_parser::ast::$name::RetainHistory,
2336 value: Some(WithOptionValue::RetainHistoryFor(value)),
2337 };
2338 if let Some(idx) = pos {
2339 let previous = $stmt.with_options[idx].clone();
2340 $stmt.with_options[idx] = next;
2341 previous.value
2342 } else {
2343 $stmt.with_options.push(next);
2344 None
2345 }
2346 } else {
2347 if let Some(idx) = pos {
2348 $stmt.with_options.swap_remove(idx).value
2349 } else {
2350 None
2351 }
2352 }
2353 }};
2354 }
2355 let previous = match &mut ast {
2356 Statement::CreateTable(stmt) => {
2357 update_retain_history!(stmt, TableOption, TableOptionName)
2358 }
2359 Statement::CreateIndex(stmt) => {
2360 update_retain_history!(stmt, IndexOption, IndexOptionName)
2361 }
2362 Statement::CreateSource(stmt) => {
2363 update_retain_history!(stmt, CreateSourceOption, CreateSourceOptionName)
2364 }
2365 Statement::CreateMaterializedView(stmt) => {
2366 update_retain_history!(stmt, MaterializedViewOption, MaterializedViewOptionName)
2367 }
2368 _ => {
2369 return Err(());
2370 }
2371 };
2372 Ok(previous)
2373 };
2374
2375 let res = self.update_sql(update)?;
2376 let cw = self
2377 .custom_logical_compaction_window_mut()
2378 .expect("item must have compaction window");
2379 *cw = Some(window);
2380 Ok(res)
2381 }
2382
2383 pub fn update_timestamp_interval(
2386 &mut self,
2387 value: Option<Value>,
2388 interval: Duration,
2389 ) -> Result<Option<WithOptionValue<Raw>>, ()> {
2390 let update = |ast: &mut Statement<Raw>| match ast {
2391 Statement::CreateSource(stmt) => {
2392 let pos = stmt.with_options.iter().rposition(|o| {
2393 o.name == mz_sql_parser::ast::CreateSourceOptionName::TimestampInterval
2394 });
2395 let previous = if let Some(value) = value {
2396 let next = mz_sql_parser::ast::CreateSourceOption {
2397 name: mz_sql_parser::ast::CreateSourceOptionName::TimestampInterval,
2398 value: Some(WithOptionValue::Value(value)),
2399 };
2400 if let Some(idx) = pos {
2401 let previous = stmt.with_options[idx].clone();
2402 stmt.with_options[idx] = next;
2403 previous.value
2404 } else {
2405 stmt.with_options.push(next);
2406 None
2407 }
2408 } else if let Some(idx) = pos {
2409 stmt.with_options.swap_remove(idx).value
2410 } else {
2411 None
2412 };
2413 Ok(previous)
2414 }
2415 _ => Err(()),
2416 };
2417
2418 let previous = self.update_sql(update)?;
2419
2420 match self {
2422 CatalogItem::Source(source) => {
2423 match &mut source.data_source {
2424 DataSourceDesc::Ingestion { desc, .. }
2425 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => {
2426 desc.timestamp_interval = interval;
2427 }
2428 _ => return Err(()),
2429 }
2430 Ok(previous)
2431 }
2432 _ => Err(()),
2433 }
2434 }
2435
2436 pub fn add_column(
2437 &mut self,
2438 name: ColumnName,
2439 typ: SqlColumnType,
2440 sql: RawDataType,
2441 ) -> Result<RelationVersion, PlanError> {
2442 let CatalogItem::Table(table) = self else {
2443 return Err(PlanError::Unsupported {
2444 feature: "adding columns to a non-Table".to_string(),
2445 discussion_no: None,
2446 });
2447 };
2448 let next_version = table.desc.add_column(name.clone(), typ);
2449
2450 let update = |mut ast: &mut Statement<Raw>| match &mut ast {
2451 Statement::CreateTable(stmt) => {
2452 let version = ColumnOptionDef {
2453 name: None,
2454 option: ColumnOption::Versioned {
2455 action: ColumnVersioned::Added,
2456 version: next_version.into(),
2457 },
2458 };
2459 let column = ColumnDef {
2460 name: name.into(),
2461 data_type: sql,
2462 collation: None,
2463 options: vec![version],
2464 };
2465 stmt.columns.push(column);
2466 Ok(())
2467 }
2468 _ => Err(()),
2469 };
2470
2471 self.update_sql(update)
2472 .map_err(|()| PlanError::Unstructured("expected CREATE TABLE statement".to_string()))?;
2473 Ok(next_version)
2474 }
2475
2476 pub fn update_sql<F, T>(&mut self, f: F) -> Result<T, ()>
2479 where
2480 F: FnOnce(&mut Statement<Raw>) -> Result<T, ()>,
2481 {
2482 let create_sql = match self {
2483 CatalogItem::Table(Table { create_sql, .. })
2484 | CatalogItem::Type(Type { create_sql, .. })
2485 | CatalogItem::Source(Source { create_sql, .. }) => create_sql.as_mut(),
2486 CatalogItem::Sink(Sink { create_sql, .. })
2487 | CatalogItem::View(View { create_sql, .. })
2488 | CatalogItem::MaterializedView(MaterializedView { create_sql, .. })
2489 | CatalogItem::Index(Index { create_sql, .. })
2490 | CatalogItem::Secret(Secret { create_sql, .. })
2491 | CatalogItem::Connection(Connection { create_sql, .. })
2492 | CatalogItem::MetricSink(MetricSink { create_sql, .. }) => Some(create_sql),
2493 CatalogItem::Func(_) | CatalogItem::Log(_) => None,
2494 };
2495 let Some(create_sql) = create_sql else {
2496 return Err(());
2497 };
2498 let mut ast = mz_sql_parser::parser::parse_statements(create_sql)
2499 .expect("non-system items must be parseable")
2500 .into_element()
2501 .ast;
2502 debug!("rewrite: {}", ast.to_ast_string_redacted());
2503 let t = f(&mut ast)?;
2504 *create_sql = ast.to_ast_string_stable();
2505 debug!("rewrote: {}", ast.to_ast_string_redacted());
2506 Ok(t)
2507 }
2508
2509 pub fn is_compute_object_on_cluster(&self) -> Option<ClusterId> {
2516 match self {
2517 CatalogItem::Index(index) => Some(index.cluster_id),
2518 CatalogItem::MetricSink(metric_sink) => Some(metric_sink.cluster_id),
2519 CatalogItem::Table(_)
2520 | CatalogItem::Source(_)
2521 | CatalogItem::Log(_)
2522 | CatalogItem::View(_)
2523 | CatalogItem::MaterializedView(_)
2524 | CatalogItem::Sink(_)
2525 | CatalogItem::Type(_)
2526 | CatalogItem::Func(_)
2527 | CatalogItem::Secret(_)
2528 | CatalogItem::Connection(_) => None,
2529 }
2530 }
2531
2532 pub fn is_hydratable(&self) -> bool {
2542 match self {
2543 CatalogItem::Index(_)
2544 | CatalogItem::MaterializedView(_)
2545 | CatalogItem::Sink(_)
2546 | CatalogItem::MetricSink(_) => true,
2547 CatalogItem::Source(source) => matches!(
2548 source.data_source,
2549 DataSourceDesc::Ingestion { .. } | DataSourceDesc::OldSyntaxIngestion { .. }
2550 ),
2551 CatalogItem::Table(_)
2552 | CatalogItem::Log(_)
2553 | CatalogItem::View(_)
2554 | CatalogItem::Type(_)
2555 | CatalogItem::Func(_)
2556 | CatalogItem::Secret(_)
2557 | CatalogItem::Connection(_) => false,
2558 }
2559 }
2560
2561 pub fn cluster_id(&self) -> Option<ClusterId> {
2562 match self {
2563 CatalogItem::MaterializedView(mv) => Some(mv.cluster_id),
2564 CatalogItem::Index(index) => Some(index.cluster_id),
2565 CatalogItem::MetricSink(metric_sink) => Some(metric_sink.cluster_id),
2566 CatalogItem::Source(source) => match &source.data_source {
2567 DataSourceDesc::Ingestion { cluster_id, .. }
2568 | DataSourceDesc::OldSyntaxIngestion { cluster_id, .. } => Some(*cluster_id),
2569 DataSourceDesc::IngestionExport { .. } => None,
2573 DataSourceDesc::Webhook { cluster_id, .. } => Some(*cluster_id),
2574 DataSourceDesc::Introspection(_)
2575 | DataSourceDesc::Progress
2576 | DataSourceDesc::Catalog => None,
2577 },
2578 CatalogItem::Sink(sink) => Some(sink.cluster_id),
2579 CatalogItem::Table(_)
2580 | CatalogItem::Log(_)
2581 | CatalogItem::View(_)
2582 | CatalogItem::Type(_)
2583 | CatalogItem::Func(_)
2584 | CatalogItem::Secret(_)
2585 | CatalogItem::Connection(_) => None,
2586 }
2587 }
2588
2589 pub fn custom_logical_compaction_window(&self) -> Option<CompactionWindow> {
2592 match self {
2593 CatalogItem::Table(table) => table.custom_logical_compaction_window,
2594 CatalogItem::Source(source) => source.custom_logical_compaction_window,
2595 CatalogItem::Index(index) => index.custom_logical_compaction_window,
2596 CatalogItem::MaterializedView(mview) => mview.custom_logical_compaction_window,
2597 CatalogItem::Log(_)
2598 | CatalogItem::View(_)
2599 | CatalogItem::Sink(_)
2600 | CatalogItem::Type(_)
2601 | CatalogItem::Func(_)
2602 | CatalogItem::Secret(_)
2603 | CatalogItem::Connection(_)
2604 | CatalogItem::MetricSink(_) => None,
2605 }
2606 }
2607
2608 pub fn custom_logical_compaction_window_mut(
2612 &mut self,
2613 ) -> Option<&mut Option<CompactionWindow>> {
2614 let cw = match self {
2615 CatalogItem::Table(table) => &mut table.custom_logical_compaction_window,
2616 CatalogItem::Source(source) => &mut source.custom_logical_compaction_window,
2617 CatalogItem::Index(index) => &mut index.custom_logical_compaction_window,
2618 CatalogItem::MaterializedView(mview) => &mut mview.custom_logical_compaction_window,
2619 CatalogItem::Log(_)
2620 | CatalogItem::View(_)
2621 | CatalogItem::Sink(_)
2622 | CatalogItem::Type(_)
2623 | CatalogItem::Func(_)
2624 | CatalogItem::Secret(_)
2625 | CatalogItem::Connection(_)
2626 | CatalogItem::MetricSink(_) => return None,
2627 };
2628 Some(cw)
2629 }
2630
2631 pub fn initial_logical_compaction_window(&self) -> Option<CompactionWindow> {
2639 let custom_logical_compaction_window = match self {
2640 CatalogItem::Table(_)
2641 | CatalogItem::Source(_)
2642 | CatalogItem::Index(_)
2643 | CatalogItem::MaterializedView(_) => self.custom_logical_compaction_window(),
2644 CatalogItem::Log(_)
2645 | CatalogItem::View(_)
2646 | CatalogItem::Sink(_)
2647 | CatalogItem::Type(_)
2648 | CatalogItem::Func(_)
2649 | CatalogItem::Secret(_)
2650 | CatalogItem::Connection(_)
2651 | CatalogItem::MetricSink(_) => return None,
2652 };
2653 Some(custom_logical_compaction_window.unwrap_or(CompactionWindow::Default))
2654 }
2655
2656 pub fn is_retained_metrics_object(&self) -> bool {
2660 match self {
2661 CatalogItem::Table(table) => table.is_retained_metrics_object,
2662 CatalogItem::Source(source) => source.is_retained_metrics_object,
2663 CatalogItem::Index(index) => index.is_retained_metrics_object,
2664 CatalogItem::Log(_)
2665 | CatalogItem::View(_)
2666 | CatalogItem::MaterializedView(_)
2667 | CatalogItem::Sink(_)
2668 | CatalogItem::Type(_)
2669 | CatalogItem::Func(_)
2670 | CatalogItem::Secret(_)
2671 | CatalogItem::Connection(_)
2672 | CatalogItem::MetricSink(_) => false,
2673 }
2674 }
2675
2676 pub fn to_serialized(&self) -> (String, GlobalId, BTreeMap<RelationVersion, GlobalId>) {
2677 match self {
2678 CatalogItem::Table(table) => {
2679 let create_sql = table
2680 .create_sql
2681 .clone()
2682 .expect("builtin tables cannot be serialized");
2683 let mut collections = table.collections.clone();
2684 let global_id = collections
2685 .remove(&RelationVersion::root())
2686 .expect("at least one version");
2687 (create_sql, global_id, collections)
2688 }
2689 CatalogItem::Log(_) => unreachable!("builtin logs cannot be serialized"),
2690 CatalogItem::Source(source) => {
2691 assert!(
2692 !matches!(source.data_source, DataSourceDesc::Introspection(_)),
2693 "cannot serialize introspection/builtin sources",
2694 );
2695 let create_sql = source
2696 .create_sql
2697 .clone()
2698 .expect("builtin sources cannot be serialized");
2699 (create_sql, source.global_id, BTreeMap::new())
2700 }
2701 CatalogItem::View(view) => (view.create_sql.clone(), view.global_id, BTreeMap::new()),
2702 CatalogItem::MaterializedView(mview) => {
2703 let mut collections = mview.collections.clone();
2704 let global_id = collections
2705 .remove(&RelationVersion::root())
2706 .expect("at least one version");
2707 (mview.create_sql.clone(), global_id, collections)
2708 }
2709 CatalogItem::Index(index) => {
2710 (index.create_sql.clone(), index.global_id, BTreeMap::new())
2711 }
2712 CatalogItem::Sink(sink) => (sink.create_sql.clone(), sink.global_id, BTreeMap::new()),
2713 CatalogItem::Type(typ) => {
2714 let create_sql = typ
2715 .create_sql
2716 .clone()
2717 .expect("builtin types cannot be serialized");
2718 (create_sql, typ.global_id, BTreeMap::new())
2719 }
2720 CatalogItem::Secret(secret) => {
2721 (secret.create_sql.clone(), secret.global_id, BTreeMap::new())
2722 }
2723 CatalogItem::Connection(connection) => (
2724 connection.create_sql.clone(),
2725 connection.global_id,
2726 BTreeMap::new(),
2727 ),
2728 CatalogItem::Func(_) => unreachable!("cannot serialize functions yet"),
2729 CatalogItem::MetricSink(ms) => (ms.create_sql.clone(), ms.global_id, BTreeMap::new()),
2730 }
2731 }
2732
2733 pub fn into_serialized(self) -> (String, GlobalId, BTreeMap<RelationVersion, GlobalId>) {
2734 match self {
2735 CatalogItem::Table(mut table) => {
2736 let create_sql = table
2737 .create_sql
2738 .expect("builtin tables cannot be serialized");
2739 let global_id = table
2740 .collections
2741 .remove(&RelationVersion::root())
2742 .expect("at least one version");
2743 (create_sql, global_id, table.collections)
2744 }
2745 CatalogItem::Log(_) => unreachable!("builtin logs cannot be serialized"),
2746 CatalogItem::Source(source) => {
2747 assert!(
2748 !matches!(source.data_source, DataSourceDesc::Introspection(_)),
2749 "cannot serialize introspection/builtin sources",
2750 );
2751 let create_sql = source
2752 .create_sql
2753 .expect("builtin sources cannot be serialized");
2754 (create_sql, source.global_id, BTreeMap::new())
2755 }
2756 CatalogItem::View(view) => (view.create_sql, view.global_id, BTreeMap::new()),
2757 CatalogItem::MaterializedView(mut mview) => {
2758 let global_id = mview
2759 .collections
2760 .remove(&RelationVersion::root())
2761 .expect("at least one version");
2762 (mview.create_sql, global_id, mview.collections)
2763 }
2764 CatalogItem::Index(index) => (index.create_sql, index.global_id, BTreeMap::new()),
2765 CatalogItem::Sink(sink) => (sink.create_sql, sink.global_id, BTreeMap::new()),
2766 CatalogItem::Type(typ) => {
2767 let create_sql = typ.create_sql.expect("builtin types cannot be serialized");
2768 (create_sql, typ.global_id, BTreeMap::new())
2769 }
2770 CatalogItem::Secret(secret) => (secret.create_sql, secret.global_id, BTreeMap::new()),
2771 CatalogItem::Connection(connection) => {
2772 (connection.create_sql, connection.global_id, BTreeMap::new())
2773 }
2774 CatalogItem::Func(_) => unreachable!("cannot serialize functions yet"),
2775 CatalogItem::MetricSink(ms) => (ms.create_sql, ms.global_id, BTreeMap::new()),
2776 }
2777 }
2778
2779 pub fn global_id_for_version(&self, version: RelationVersionSelector) -> Option<GlobalId> {
2782 let collections = match self {
2783 CatalogItem::MaterializedView(mv) => &mv.collections,
2784 CatalogItem::Table(table) => &table.collections,
2785 CatalogItem::Source(source) => return Some(source.global_id),
2786 CatalogItem::Log(log) => return Some(log.global_id),
2787 CatalogItem::View(view) => return Some(view.global_id),
2788 CatalogItem::Sink(sink) => return Some(sink.global_id),
2789 CatalogItem::Index(index) => return Some(index.global_id),
2790 CatalogItem::Type(ty) => return Some(ty.global_id),
2791 CatalogItem::Func(func) => return Some(func.global_id),
2792 CatalogItem::Secret(secret) => return Some(secret.global_id),
2793 CatalogItem::Connection(conn) => return Some(conn.global_id),
2794 CatalogItem::MetricSink(metric_sink) => return Some(metric_sink.global_id),
2795 };
2796 match version {
2797 RelationVersionSelector::Latest => collections.values().last().copied(),
2798 RelationVersionSelector::Specific(version) => collections.get(&version).copied(),
2799 }
2800 }
2801}
2802
2803impl CatalogEntry {
2804 pub fn relation_desc_latest(&self) -> Option<Cow<'_, RelationDesc>> {
2807 self.item.relation_desc(RelationVersionSelector::Latest)
2808 }
2809
2810 pub fn has_columns(&self) -> bool {
2812 match self.item() {
2813 CatalogItem::Type(Type { details, .. }) => {
2814 matches!(details.typ, CatalogType::Record { .. })
2815 }
2816 _ => self.relation_desc_latest().is_some(),
2817 }
2818 }
2819
2820 pub fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
2822 self.item.func(self)
2823 }
2824
2825 pub fn index(&self) -> Option<&Index> {
2827 match self.item() {
2828 CatalogItem::Index(idx) => Some(idx),
2829 _ => None,
2830 }
2831 }
2832
2833 pub fn materialized_view(&self) -> Option<&MaterializedView> {
2835 match self.item() {
2836 CatalogItem::MaterializedView(mv) => Some(mv),
2837 _ => None,
2838 }
2839 }
2840
2841 pub fn table(&self) -> Option<&Table> {
2843 match self.item() {
2844 CatalogItem::Table(tbl) => Some(tbl),
2845 _ => None,
2846 }
2847 }
2848
2849 pub fn source(&self) -> Option<&Source> {
2851 match self.item() {
2852 CatalogItem::Source(src) => Some(src),
2853 _ => None,
2854 }
2855 }
2856
2857 pub fn sink(&self) -> Option<&Sink> {
2859 match self.item() {
2860 CatalogItem::Sink(sink) => Some(sink),
2861 _ => None,
2862 }
2863 }
2864
2865 pub fn secret(&self) -> Option<&Secret> {
2867 match self.item() {
2868 CatalogItem::Secret(secret) => Some(secret),
2869 _ => None,
2870 }
2871 }
2872
2873 pub fn connection(&self) -> Result<&Connection, SqlCatalogError> {
2874 match self.item() {
2875 CatalogItem::Connection(connection) => Ok(connection),
2876 _ => {
2877 let db_name = match self.name().qualifiers.database_spec {
2878 ResolvedDatabaseSpecifier::Ambient => "".to_string(),
2879 ResolvedDatabaseSpecifier::Id(id) => format!("{id}."),
2880 };
2881 Err(SqlCatalogError::UnknownConnection(format!(
2882 "{}{}.{}",
2883 db_name,
2884 self.name().qualifiers.schema_spec,
2885 self.name().item
2886 )))
2887 }
2888 }
2889 }
2890
2891 pub fn source_desc(
2894 &self,
2895 ) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
2896 self.item.source_desc(self)
2897 }
2898
2899 pub fn is_connection(&self) -> bool {
2901 matches!(self.item(), CatalogItem::Connection(_))
2902 }
2903
2904 pub fn is_table(&self) -> bool {
2906 matches!(self.item(), CatalogItem::Table(_))
2907 }
2908
2909 pub fn is_source(&self) -> bool {
2912 matches!(self.item(), CatalogItem::Source(_))
2913 }
2914
2915 pub fn subsource_details(
2918 &self,
2919 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
2920 match &self.item() {
2921 CatalogItem::Source(source) => match &source.data_source {
2922 DataSourceDesc::IngestionExport {
2923 ingestion_id,
2924 external_reference,
2925 details,
2926 data_config: _,
2927 } => Some((*ingestion_id, external_reference, details)),
2928 _ => None,
2929 },
2930 _ => None,
2931 }
2932 }
2933
2934 pub fn source_export_details(
2937 &self,
2938 ) -> Option<(
2939 CatalogItemId,
2940 &UnresolvedItemName,
2941 &SourceExportDetails,
2942 &SourceExportDataConfig<ReferencedConnection>,
2943 )> {
2944 match &self.item() {
2945 CatalogItem::Source(source) => match &source.data_source {
2946 DataSourceDesc::IngestionExport {
2947 ingestion_id,
2948 external_reference,
2949 details,
2950 data_config,
2951 } => Some((*ingestion_id, external_reference, details, data_config)),
2952 _ => None,
2953 },
2954 CatalogItem::Table(table) => match &table.data_source {
2955 TableDataSource::DataSource {
2956 desc:
2957 DataSourceDesc::IngestionExport {
2958 ingestion_id,
2959 external_reference,
2960 details,
2961 data_config,
2962 },
2963 timeline: _,
2964 } => Some((*ingestion_id, external_reference, details, data_config)),
2965 _ => None,
2966 },
2967 _ => None,
2968 }
2969 }
2970
2971 pub fn is_progress_source(&self) -> bool {
2973 self.item().is_progress_source()
2974 }
2975
2976 pub fn progress_id(&self) -> Option<CatalogItemId> {
2978 match &self.item() {
2979 CatalogItem::Source(source) => match &source.data_source {
2980 DataSourceDesc::Ingestion { .. } => Some(self.id),
2981 DataSourceDesc::OldSyntaxIngestion {
2982 progress_subsource, ..
2983 } => Some(*progress_subsource),
2984 DataSourceDesc::IngestionExport { .. }
2985 | DataSourceDesc::Introspection(_)
2986 | DataSourceDesc::Progress
2987 | DataSourceDesc::Webhook { .. }
2988 | DataSourceDesc::Catalog => None,
2989 },
2990 CatalogItem::Table(_)
2991 | CatalogItem::Log(_)
2992 | CatalogItem::View(_)
2993 | CatalogItem::MaterializedView(_)
2994 | CatalogItem::Sink(_)
2995 | CatalogItem::Index(_)
2996 | CatalogItem::Type(_)
2997 | CatalogItem::Func(_)
2998 | CatalogItem::Secret(_)
2999 | CatalogItem::Connection(_)
3000 | CatalogItem::MetricSink(_) => None,
3001 }
3002 }
3003
3004 pub fn is_sink(&self) -> bool {
3006 matches!(self.item(), CatalogItem::Sink(_))
3007 }
3008
3009 pub fn is_materialized_view(&self) -> bool {
3011 matches!(self.item(), CatalogItem::MaterializedView(_))
3012 }
3013
3014 pub fn is_view(&self) -> bool {
3016 matches!(self.item(), CatalogItem::View(_))
3017 }
3018
3019 pub fn is_secret(&self) -> bool {
3021 matches!(self.item(), CatalogItem::Secret(_))
3022 }
3023
3024 pub fn is_introspection_source(&self) -> bool {
3026 matches!(self.item(), CatalogItem::Log(_))
3027 }
3028
3029 pub fn is_index(&self) -> bool {
3031 matches!(self.item(), CatalogItem::Index(_))
3032 }
3033
3034 pub fn is_metric_sink(&self) -> bool {
3036 matches!(self.item(), CatalogItem::MetricSink(_))
3037 }
3038
3039 pub fn is_relation(&self) -> bool {
3041 mz_sql::catalog::ObjectType::from(self.item_type()).is_relation()
3042 }
3043
3044 pub fn references(&self) -> &ResolvedIds {
3047 self.item.references()
3048 }
3049
3050 pub fn uses(&self) -> BTreeSet<CatalogItemId> {
3056 self.item.uses()
3057 }
3058
3059 pub fn item(&self) -> &CatalogItem {
3061 &self.item
3062 }
3063
3064 pub fn item_mut(&mut self) -> &mut CatalogItem {
3067 &mut self.item
3068 }
3069
3070 pub fn id(&self) -> CatalogItemId {
3072 self.id
3073 }
3074
3075 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
3077 self.item().global_ids()
3078 }
3079
3080 pub fn latest_global_id(&self) -> GlobalId {
3081 self.item().latest_global_id()
3082 }
3083
3084 pub fn oid(&self) -> u32 {
3086 self.oid
3087 }
3088
3089 pub fn name(&self) -> &QualifiedItemName {
3091 &self.name
3092 }
3093
3094 pub fn referenced_by(&self) -> &[CatalogItemId] {
3096 &self.referenced_by
3097 }
3098
3099 pub fn used_by(&self) -> &[CatalogItemId] {
3101 &self.used_by
3102 }
3103
3104 pub fn conn_id(&self) -> Option<&ConnectionId> {
3107 self.item.conn_id()
3108 }
3109
3110 pub fn owner_id(&self) -> &RoleId {
3112 &self.owner_id
3113 }
3114
3115 pub fn privileges(&self) -> &PrivilegeMap {
3117 &self.privileges
3118 }
3119
3120 pub fn comment_object_id(&self) -> CommentObjectId {
3122 use CatalogItemType::*;
3123 match self.item_type() {
3124 Table => CommentObjectId::Table(self.id),
3125 Source => CommentObjectId::Source(self.id),
3126 Sink => CommentObjectId::Sink(self.id),
3127 View => CommentObjectId::View(self.id),
3128 MaterializedView => CommentObjectId::MaterializedView(self.id),
3129 Index => CommentObjectId::Index(self.id),
3130 Func => CommentObjectId::Func(self.id),
3131 Connection => CommentObjectId::Connection(self.id),
3132 Type => CommentObjectId::Type(self.id),
3133 Secret => CommentObjectId::Secret(self.id),
3134 MetricSink => CommentObjectId::MetricSink(self.id),
3135 }
3136 }
3137}
3138
3139#[derive(Debug, Clone, Default)]
3140pub struct CommentsMap {
3141 map: BTreeMap<CommentObjectId, BTreeMap<Option<usize>, String>>,
3142}
3143
3144impl CommentsMap {
3145 pub fn update_comment(
3146 &mut self,
3147 object_id: CommentObjectId,
3148 sub_component: Option<usize>,
3149 comment: Option<String>,
3150 ) -> Option<String> {
3151 let object_comments = self.map.entry(object_id).or_default();
3152
3153 let (empty, prev) = if let Some(comment) = comment {
3155 let prev = object_comments.insert(sub_component, comment);
3156 (false, prev)
3157 } else {
3158 let prev = object_comments.remove(&sub_component);
3159 (object_comments.is_empty(), prev)
3160 };
3161
3162 if empty {
3164 self.map.remove(&object_id);
3165 }
3166
3167 prev
3169 }
3170
3171 pub fn drop_comments(
3177 &mut self,
3178 object_ids: &BTreeSet<CommentObjectId>,
3179 ) -> Vec<(CommentObjectId, Option<usize>, String)> {
3180 let mut removed_comments = Vec::new();
3181
3182 for object_id in object_ids {
3183 if let Some(comments) = self.map.remove(object_id) {
3184 let removed = comments
3185 .into_iter()
3186 .map(|(sub_comp, comment)| (object_id.clone(), sub_comp, comment));
3187 removed_comments.extend(removed);
3188 }
3189 }
3190
3191 removed_comments
3192 }
3193
3194 pub fn iter(&self) -> impl Iterator<Item = (CommentObjectId, Option<usize>, &str)> {
3195 self.map
3196 .iter()
3197 .map(|(id, comments)| {
3198 comments
3199 .iter()
3200 .map(|(pos, comment)| (*id, *pos, comment.as_str()))
3201 })
3202 .flatten()
3203 }
3204
3205 pub fn get_object_comments(
3206 &self,
3207 object_id: CommentObjectId,
3208 ) -> Option<&BTreeMap<Option<usize>, String>> {
3209 self.map.get(&object_id)
3210 }
3211}
3212
3213impl Serialize for CommentsMap {
3214 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3215 where
3216 S: serde::Serializer,
3217 {
3218 let comment_count = self
3219 .map
3220 .iter()
3221 .map(|(_object_id, comments)| comments.len())
3222 .sum();
3223
3224 let mut seq = serializer.serialize_seq(Some(comment_count))?;
3225 for (object_id, sub) in &self.map {
3226 for (sub_component, comment) in sub {
3227 seq.serialize_element(&(
3228 format!("{object_id:?}"),
3229 format!("{sub_component:?}"),
3230 comment,
3231 ))?;
3232 }
3233 }
3234 seq.end()
3235 }
3236}
3237
3238#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Default)]
3239pub struct DefaultPrivileges {
3240 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
3241 privileges: BTreeMap<DefaultPrivilegeObject, RoleDefaultPrivileges>,
3242}
3243
3244#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Default)]
3247struct RoleDefaultPrivileges(
3248 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
3250 BTreeMap<RoleId, DefaultPrivilegeAclItem>,
3251);
3252
3253impl Deref for RoleDefaultPrivileges {
3254 type Target = BTreeMap<RoleId, DefaultPrivilegeAclItem>;
3255
3256 fn deref(&self) -> &Self::Target {
3257 &self.0
3258 }
3259}
3260
3261impl DerefMut for RoleDefaultPrivileges {
3262 fn deref_mut(&mut self) -> &mut Self::Target {
3263 &mut self.0
3264 }
3265}
3266
3267impl DefaultPrivileges {
3268 pub fn grant(&mut self, object: DefaultPrivilegeObject, privilege: DefaultPrivilegeAclItem) {
3270 if privilege.acl_mode.is_empty() {
3271 return;
3272 }
3273
3274 let privileges = self.privileges.entry(object).or_default();
3275 if let Some(default_privilege) = privileges.get_mut(&privilege.grantee) {
3276 default_privilege.acl_mode |= privilege.acl_mode;
3277 } else {
3278 privileges.insert(privilege.grantee, privilege);
3279 }
3280 }
3281
3282 pub fn revoke(&mut self, object: &DefaultPrivilegeObject, privilege: &DefaultPrivilegeAclItem) {
3284 if let Some(privileges) = self.privileges.get_mut(object) {
3285 if let Some(default_privilege) = privileges.get_mut(&privilege.grantee) {
3286 default_privilege.acl_mode =
3287 default_privilege.acl_mode.difference(privilege.acl_mode);
3288 if default_privilege.acl_mode.is_empty() {
3289 privileges.remove(&privilege.grantee);
3290 }
3291 }
3292 if privileges.is_empty() {
3293 self.privileges.remove(object);
3294 }
3295 }
3296 }
3297
3298 pub fn get_privileges_for_grantee(
3301 &self,
3302 object: &DefaultPrivilegeObject,
3303 grantee: &RoleId,
3304 ) -> Option<&AclMode> {
3305 self.privileges
3306 .get(object)
3307 .and_then(|privileges| privileges.get(grantee))
3308 .map(|privilege| &privilege.acl_mode)
3309 }
3310
3311 pub fn get_applicable_privileges(
3313 &self,
3314 role_id: RoleId,
3315 database_id: Option<DatabaseId>,
3316 schema_id: Option<SchemaId>,
3317 object_type: mz_sql::catalog::ObjectType,
3318 ) -> impl Iterator<Item = DefaultPrivilegeAclItem> + '_ {
3319 let privilege_object_type = if object_type.is_relation() {
3323 mz_sql::catalog::ObjectType::Table
3324 } else {
3325 object_type
3326 };
3327 let valid_acl_mode = rbac::all_object_privileges(SystemObjectType::Object(object_type));
3328
3329 [
3333 DefaultPrivilegeObject {
3334 role_id,
3335 database_id,
3336 schema_id,
3337 object_type: privilege_object_type,
3338 },
3339 DefaultPrivilegeObject {
3340 role_id,
3341 database_id,
3342 schema_id: None,
3343 object_type: privilege_object_type,
3344 },
3345 DefaultPrivilegeObject {
3346 role_id,
3347 database_id: None,
3348 schema_id: None,
3349 object_type: privilege_object_type,
3350 },
3351 DefaultPrivilegeObject {
3352 role_id: RoleId::Public,
3353 database_id,
3354 schema_id,
3355 object_type: privilege_object_type,
3356 },
3357 DefaultPrivilegeObject {
3358 role_id: RoleId::Public,
3359 database_id,
3360 schema_id: None,
3361 object_type: privilege_object_type,
3362 },
3363 DefaultPrivilegeObject {
3364 role_id: RoleId::Public,
3365 database_id: None,
3366 schema_id: None,
3367 object_type: privilege_object_type,
3368 },
3369 ]
3370 .into_iter()
3371 .filter_map(|object| self.privileges.get(&object))
3372 .flat_map(|acl_map| acl_map.values())
3373 .fold(
3375 BTreeMap::new(),
3376 |mut accum, DefaultPrivilegeAclItem { grantee, acl_mode }| {
3377 let accum_acl_mode = accum.entry(grantee).or_insert_with(AclMode::empty);
3378 *accum_acl_mode |= *acl_mode;
3379 accum
3380 },
3381 )
3382 .into_iter()
3383 .map(move |(grantee, acl_mode)| (grantee, acl_mode & valid_acl_mode))
3388 .filter(|(_, acl_mode)| !acl_mode.is_empty())
3390 .map(|(grantee, acl_mode)| DefaultPrivilegeAclItem {
3391 grantee: *grantee,
3392 acl_mode,
3393 })
3394 }
3395
3396 pub fn iter(
3397 &self,
3398 ) -> impl Iterator<
3399 Item = (
3400 &DefaultPrivilegeObject,
3401 impl Iterator<Item = &DefaultPrivilegeAclItem>,
3402 ),
3403 > {
3404 self.privileges
3405 .iter()
3406 .map(|(object, acl_map)| (object, acl_map.values()))
3407 }
3408}
3409
3410#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3411pub struct ClusterConfig {
3412 pub variant: ClusterVariant,
3413 pub workload_class: Option<String>,
3414}
3415
3416impl ClusterConfig {
3417 pub fn features(&self) -> Option<&OptimizerFeatureOverrides> {
3418 match &self.variant {
3419 ClusterVariant::Managed(managed) => Some(&managed.optimizer_feature_overrides),
3420 ClusterVariant::Unmanaged => None,
3421 }
3422 }
3423}
3424
3425impl From<ClusterConfig> for durable::ClusterConfig {
3426 fn from(config: ClusterConfig) -> Self {
3427 Self {
3428 variant: config.variant.into(),
3429 workload_class: config.workload_class,
3430 }
3431 }
3432}
3433
3434impl From<durable::ClusterConfig> for ClusterConfig {
3435 fn from(config: durable::ClusterConfig) -> Self {
3436 Self {
3437 variant: config.variant.into(),
3438 workload_class: config.workload_class,
3439 }
3440 }
3441}
3442
3443#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3444pub struct ClusterVariantManaged {
3445 pub size: String,
3446 pub availability_zones: Vec<String>,
3447 pub logging: ReplicaLogging,
3448 pub arrangement_compression: bool,
3450 pub replication_factor: u32,
3451 pub optimizer_feature_overrides: OptimizerFeatureOverrides,
3452 pub schedule: ClusterSchedule,
3453 pub auto_scaling_strategy: Option<AutoScalingStrategy>,
3456 pub reconfiguration: Option<ReconfigurationState>,
3458 pub burst: Option<BurstState>,
3460}
3461
3462#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3464pub struct ManagedReplicaConfigShape<'a> {
3465 pub size: &'a str,
3466 pub availability_zones: &'a [String],
3467 pub logging: &'a ReplicaLogging,
3468 pub arrangement_compression: bool,
3469}
3470
3471impl<'a> ManagedReplicaConfigShape<'a> {
3472 pub fn new(
3474 size: &'a str,
3475 availability_zones: &'a [String],
3476 logging: &'a ReplicaLogging,
3477 arrangement_compression: bool,
3478 ) -> Self {
3479 Self {
3480 size,
3481 availability_zones,
3482 logging,
3483 arrangement_compression,
3484 }
3485 }
3486}
3487
3488impl ClusterVariantManaged {
3489 pub fn replica_config_shape(&self) -> ManagedReplicaConfigShape<'_> {
3491 let ClusterVariantManaged {
3492 size,
3493 availability_zones,
3494 logging,
3495 arrangement_compression,
3496 replication_factor: _,
3497 optimizer_feature_overrides: _,
3498 schedule: _,
3499 auto_scaling_strategy: _,
3500 reconfiguration: _,
3501 burst: _,
3502 } = self;
3503 ManagedReplicaConfigShape::new(size, availability_zones, logging, *arrangement_compression)
3504 }
3505
3506 pub fn realized_reconfiguration_target(&self) -> ReconfigurationTarget {
3508 let ClusterVariantManaged {
3509 size,
3510 availability_zones,
3511 logging,
3512 arrangement_compression,
3513 replication_factor,
3514 optimizer_feature_overrides: _,
3515 schedule: _,
3516 auto_scaling_strategy: _,
3517 reconfiguration: _,
3518 burst: _,
3519 } = self;
3520 ReconfigurationTarget {
3521 size: size.clone(),
3522 replication_factor: *replication_factor,
3523 availability_zones: availability_zones.clone(),
3524 logging: logging.clone(),
3525 arrangement_compression: *arrangement_compression,
3526 }
3527 }
3528
3529 pub fn apply_reconfiguration_target(&mut self, target: ReconfigurationTarget) {
3533 let ReconfigurationTarget {
3536 size,
3537 replication_factor,
3538 availability_zones,
3539 logging,
3540 arrangement_compression,
3541 } = target;
3542 self.size = size;
3543 self.replication_factor = replication_factor;
3544 self.availability_zones = availability_zones;
3545 self.logging = logging;
3546 self.arrangement_compression = arrangement_compression;
3547 }
3548
3549 pub fn has_unwarranted_burst_record(&self) -> bool {
3554 let Some(record) = &self.burst else {
3555 return false;
3556 };
3557 let hydration_size = self
3558 .auto_scaling_strategy
3559 .as_ref()
3560 .and_then(|strategy| strategy.on_hydration.as_ref())
3561 .map(|policy| policy.hydration_size.as_str());
3562 !mz_adapter_types::cluster_state::burst_record_warranted(
3563 &record.burst_size,
3564 self.replication_factor,
3565 hydration_size,
3566 )
3567 }
3568}
3569
3570impl From<ClusterVariantManaged> for durable::ClusterVariantManaged {
3571 fn from(managed: ClusterVariantManaged) -> Self {
3572 let ClusterVariantManaged {
3575 size,
3576 availability_zones,
3577 logging,
3578 arrangement_compression,
3579 replication_factor,
3580 optimizer_feature_overrides,
3581 schedule,
3582 auto_scaling_strategy,
3583 reconfiguration,
3584 burst,
3585 } = managed;
3586 Self {
3587 size,
3588 availability_zones,
3589 logging,
3590 arrangement_compression,
3591 replication_factor,
3592 optimizer_feature_overrides: optimizer_feature_overrides.into(),
3593 schedule,
3594 auto_scaling_strategy,
3595 reconfiguration: reconfiguration.map(Into::into),
3596 burst: burst.map(Into::into),
3597 }
3598 }
3599}
3600
3601impl From<durable::ClusterVariantManaged> for ClusterVariantManaged {
3602 fn from(managed: durable::ClusterVariantManaged) -> Self {
3603 let durable::ClusterVariantManaged {
3606 size,
3607 availability_zones,
3608 logging,
3609 arrangement_compression,
3610 replication_factor,
3611 optimizer_feature_overrides,
3612 schedule,
3613 auto_scaling_strategy,
3614 reconfiguration,
3615 burst,
3616 } = managed;
3617 Self {
3618 size,
3619 availability_zones,
3620 logging,
3621 arrangement_compression,
3622 replication_factor,
3623 optimizer_feature_overrides: optimizer_feature_overrides.into(),
3624 schedule,
3625 auto_scaling_strategy,
3626 reconfiguration: reconfiguration.map(Into::into),
3627 burst: burst.map(Into::into),
3628 }
3629 }
3630}
3631
3632#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3639pub struct ReconfigurationState {
3640 pub target: ReconfigurationTarget,
3641 pub deadline: Timestamp,
3642 pub on_timeout: OnTimeoutAction,
3643 pub status: ReconfigurationStatus,
3644}
3645
3646#[derive(
3665 Clone,
3666 Copy,
3667 Debug,
3668 Deserialize,
3669 Serialize,
3670 PartialOrd,
3671 PartialEq,
3672 Eq,
3673 Ord
3674)]
3675pub enum ReconfigurationStatus {
3676 InProgress,
3677 Finalized,
3678 TimedOut,
3679 Cancelled,
3680 ResourceExhausted,
3681}
3682
3683impl From<ReconfigurationStatus> for durable::ReconfigurationStatus {
3684 fn from(status: ReconfigurationStatus) -> Self {
3685 match status {
3686 ReconfigurationStatus::InProgress => durable::ReconfigurationStatus::InProgress,
3687 ReconfigurationStatus::Finalized => durable::ReconfigurationStatus::Finalized,
3688 ReconfigurationStatus::TimedOut => durable::ReconfigurationStatus::TimedOut,
3689 ReconfigurationStatus::Cancelled => durable::ReconfigurationStatus::Cancelled,
3690 ReconfigurationStatus::ResourceExhausted => {
3691 durable::ReconfigurationStatus::ResourceExhausted
3692 }
3693 }
3694 }
3695}
3696
3697impl From<durable::ReconfigurationStatus> for ReconfigurationStatus {
3698 fn from(status: durable::ReconfigurationStatus) -> Self {
3699 match status {
3700 durable::ReconfigurationStatus::InProgress => ReconfigurationStatus::InProgress,
3701 durable::ReconfigurationStatus::Finalized => ReconfigurationStatus::Finalized,
3702 durable::ReconfigurationStatus::TimedOut => ReconfigurationStatus::TimedOut,
3703 durable::ReconfigurationStatus::Cancelled => ReconfigurationStatus::Cancelled,
3704 durable::ReconfigurationStatus::ResourceExhausted => {
3705 ReconfigurationStatus::ResourceExhausted
3706 }
3707 }
3708 }
3709}
3710
3711impl ReconfigurationState {
3712 pub fn is_in_progress(&self) -> bool {
3713 matches!(self.status, ReconfigurationStatus::InProgress)
3714 }
3715}
3716
3717impl From<ReconfigurationState> for durable::ReconfigurationState {
3718 fn from(state: ReconfigurationState) -> Self {
3719 let ReconfigurationState {
3722 target,
3723 deadline,
3724 on_timeout,
3725 status,
3726 } = state;
3727 Self {
3728 target: target.into(),
3729 deadline,
3730 on_timeout,
3731 status: status.into(),
3732 }
3733 }
3734}
3735
3736impl From<durable::ReconfigurationState> for ReconfigurationState {
3737 fn from(state: durable::ReconfigurationState) -> Self {
3738 let durable::ReconfigurationState {
3741 target,
3742 deadline,
3743 on_timeout,
3744 status,
3745 } = state;
3746 Self {
3747 target: target.into(),
3748 deadline,
3749 on_timeout,
3750 status: status.into(),
3751 }
3752 }
3753}
3754
3755#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3757pub struct ReconfigurationTarget {
3758 pub size: String,
3759 pub replication_factor: u32,
3760 pub availability_zones: Vec<String>,
3761 pub logging: ReplicaLogging,
3762 pub arrangement_compression: bool,
3763}
3764
3765impl ReconfigurationTarget {
3766 pub fn matches_realized_config(&self, managed: &ClusterVariantManaged) -> bool {
3768 self == &managed.realized_reconfiguration_target()
3769 }
3770}
3771
3772impl From<ReconfigurationTarget> for durable::ReconfigurationTarget {
3773 fn from(target: ReconfigurationTarget) -> Self {
3774 let ReconfigurationTarget {
3777 size,
3778 replication_factor,
3779 availability_zones,
3780 logging,
3781 arrangement_compression,
3782 } = target;
3783 Self {
3784 size,
3785 replication_factor,
3786 availability_zones,
3787 logging,
3788 arrangement_compression,
3789 }
3790 }
3791}
3792
3793impl From<durable::ReconfigurationTarget> for ReconfigurationTarget {
3794 fn from(target: durable::ReconfigurationTarget) -> Self {
3795 let durable::ReconfigurationTarget {
3798 size,
3799 replication_factor,
3800 availability_zones,
3801 logging,
3802 arrangement_compression,
3803 } = target;
3804 Self {
3805 size,
3806 replication_factor,
3807 availability_zones,
3808 logging,
3809 arrangement_compression,
3810 }
3811 }
3812}
3813
3814#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3816pub struct BurstState {
3817 pub burst_size: String,
3818 pub linger_duration: Duration,
3819 pub steady_hydrated_at: Option<Timestamp>,
3820}
3821
3822impl From<BurstState> for durable::BurstState {
3823 fn from(burst: BurstState) -> Self {
3824 let BurstState {
3827 burst_size,
3828 linger_duration,
3829 steady_hydrated_at,
3830 } = burst;
3831 Self {
3832 burst_size,
3833 linger_duration,
3834 steady_hydrated_at,
3835 }
3836 }
3837}
3838
3839impl From<durable::BurstState> for BurstState {
3840 fn from(burst: durable::BurstState) -> Self {
3841 let durable::BurstState {
3844 burst_size,
3845 linger_duration,
3846 steady_hydrated_at,
3847 } = burst;
3848 Self {
3849 burst_size,
3850 linger_duration,
3851 steady_hydrated_at,
3852 }
3853 }
3854}
3855
3856#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3857pub enum ClusterVariant {
3858 Managed(ClusterVariantManaged),
3859 Unmanaged,
3860}
3861
3862impl From<ClusterVariant> for durable::ClusterVariant {
3863 fn from(variant: ClusterVariant) -> Self {
3864 match variant {
3865 ClusterVariant::Managed(managed) => Self::Managed(managed.into()),
3866 ClusterVariant::Unmanaged => Self::Unmanaged,
3867 }
3868 }
3869}
3870
3871impl From<durable::ClusterVariant> for ClusterVariant {
3872 fn from(variant: durable::ClusterVariant) -> Self {
3873 match variant {
3874 durable::ClusterVariant::Managed(managed) => Self::Managed(managed.into()),
3875 durable::ClusterVariant::Unmanaged => Self::Unmanaged,
3876 }
3877 }
3878}
3879
3880impl mz_sql::catalog::CatalogDatabase for Database {
3881 fn name(&self) -> &str {
3882 &self.name
3883 }
3884
3885 fn id(&self) -> DatabaseId {
3886 self.id
3887 }
3888
3889 fn has_schemas(&self) -> bool {
3890 !self.schemas_by_name.is_empty()
3891 }
3892
3893 fn schema_ids(&self) -> &BTreeMap<String, SchemaId> {
3894 &self.schemas_by_name
3895 }
3896
3897 #[allow(clippy::as_conversions)]
3899 fn schemas(&self) -> Vec<&dyn CatalogSchema> {
3900 self.schemas_by_id
3901 .values()
3902 .map(|schema| schema as &dyn CatalogSchema)
3903 .collect()
3904 }
3905
3906 fn owner_id(&self) -> RoleId {
3907 self.owner_id
3908 }
3909
3910 fn privileges(&self) -> &PrivilegeMap {
3911 &self.privileges
3912 }
3913}
3914
3915impl mz_sql::catalog::CatalogSchema for Schema {
3916 fn database(&self) -> &ResolvedDatabaseSpecifier {
3917 &self.name.database
3918 }
3919
3920 fn name(&self) -> &QualifiedSchemaName {
3921 &self.name
3922 }
3923
3924 fn id(&self) -> &SchemaSpecifier {
3925 &self.id
3926 }
3927
3928 fn has_items(&self) -> bool {
3929 !self.items.is_empty() || !self.types.is_empty() || !self.functions.is_empty()
3933 }
3934
3935 fn item_ids(&self) -> Box<dyn Iterator<Item = CatalogItemId> + '_> {
3936 Box::new(
3937 self.items
3938 .values()
3939 .chain(self.functions.values())
3940 .chain(self.types.values())
3941 .copied(),
3942 )
3943 }
3944
3945 fn owner_id(&self) -> RoleId {
3946 self.owner_id
3947 }
3948
3949 fn privileges(&self) -> &PrivilegeMap {
3950 &self.privileges
3951 }
3952}
3953
3954impl mz_sql::catalog::CatalogRole for Role {
3955 fn name(&self) -> &str {
3956 &self.name
3957 }
3958
3959 fn id(&self) -> RoleId {
3960 self.id
3961 }
3962
3963 fn membership(&self) -> &BTreeMap<RoleId, RoleId> {
3964 &self.membership.map
3965 }
3966
3967 fn attributes(&self) -> &RoleAttributes {
3968 &self.attributes
3969 }
3970
3971 fn vars(&self) -> &BTreeMap<String, OwnedVarInput> {
3972 &self.vars.map
3973 }
3974}
3975
3976impl mz_sql::catalog::CatalogNetworkPolicy for NetworkPolicy {
3977 fn name(&self) -> &str {
3978 &self.name
3979 }
3980
3981 fn id(&self) -> NetworkPolicyId {
3982 self.id
3983 }
3984
3985 fn owner_id(&self) -> RoleId {
3986 self.owner_id
3987 }
3988
3989 fn privileges(&self) -> &PrivilegeMap {
3990 &self.privileges
3991 }
3992}
3993
3994impl mz_sql::catalog::CatalogCluster<'_> for Cluster {
3995 fn name(&self) -> &str {
3996 &self.name
3997 }
3998
3999 fn id(&self) -> ClusterId {
4000 self.id
4001 }
4002
4003 fn bound_objects(&self) -> &BTreeSet<CatalogItemId> {
4004 &self.bound_objects
4005 }
4006
4007 fn replica_ids(&self) -> &BTreeMap<String, ReplicaId> {
4008 &self.replica_id_by_name_
4009 }
4010
4011 #[allow(clippy::as_conversions)]
4013 fn replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>> {
4014 self.replicas()
4015 .map(|replica| replica as &dyn CatalogClusterReplica)
4016 .collect()
4017 }
4018
4019 fn replica(&self, id: ReplicaId) -> &dyn CatalogClusterReplica<'_> {
4020 self.replica(id).expect("catalog out of sync")
4021 }
4022
4023 fn owner_id(&self) -> RoleId {
4024 self.owner_id
4025 }
4026
4027 fn privileges(&self) -> &PrivilegeMap {
4028 &self.privileges
4029 }
4030
4031 fn is_managed(&self) -> bool {
4032 self.is_managed()
4033 }
4034
4035 fn managed_size(&self) -> Option<&str> {
4036 match &self.config.variant {
4037 ClusterVariant::Managed(ClusterVariantManaged { size, .. }) => Some(size),
4038 ClusterVariant::Unmanaged => None,
4039 }
4040 }
4041
4042 fn schedule(&self) -> Option<&ClusterSchedule> {
4043 match &self.config.variant {
4044 ClusterVariant::Managed(ClusterVariantManaged { schedule, .. }) => Some(schedule),
4045 ClusterVariant::Unmanaged => None,
4046 }
4047 }
4048
4049 fn replication_factor(&self) -> Option<u32> {
4050 match &self.config.variant {
4051 ClusterVariant::Managed(ClusterVariantManaged {
4052 replication_factor, ..
4053 }) => Some(*replication_factor),
4054 ClusterVariant::Unmanaged => None,
4055 }
4056 }
4057
4058 fn auto_scaling_strategy(&self) -> Option<&AutoScalingStrategy> {
4059 match &self.config.variant {
4060 ClusterVariant::Managed(ClusterVariantManaged {
4061 auto_scaling_strategy,
4062 ..
4063 }) => auto_scaling_strategy.as_ref(),
4064 ClusterVariant::Unmanaged => None,
4065 }
4066 }
4067 fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError> {
4068 self.try_to_plan()
4069 }
4070}
4071
4072impl mz_sql::catalog::CatalogClusterReplica<'_> for ClusterReplica {
4073 fn name(&self) -> &str {
4074 &self.name
4075 }
4076
4077 fn cluster_id(&self) -> ClusterId {
4078 self.cluster_id
4079 }
4080
4081 fn replica_id(&self) -> ReplicaId {
4082 self.replica_id
4083 }
4084
4085 fn owner_id(&self) -> RoleId {
4086 self.owner_id
4087 }
4088
4089 fn internal(&self) -> bool {
4090 self.config.location.internal()
4091 }
4092}
4093
4094impl mz_sql::catalog::CatalogItem for CatalogEntry {
4095 fn name(&self) -> &QualifiedItemName {
4096 self.name()
4097 }
4098
4099 fn id(&self) -> CatalogItemId {
4100 self.id()
4101 }
4102
4103 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_> {
4104 Box::new(self.global_ids())
4105 }
4106
4107 fn oid(&self) -> u32 {
4108 self.oid()
4109 }
4110
4111 fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
4112 self.func()
4113 }
4114
4115 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
4116 self.source_desc()
4117 }
4118
4119 fn connection(
4120 &self,
4121 ) -> Result<mz_storage_types::connections::Connection<ReferencedConnection>, SqlCatalogError>
4122 {
4123 Ok(self.connection()?.details.to_connection())
4124 }
4125
4126 fn create_sql(&self) -> &str {
4127 match self.item() {
4128 CatalogItem::Table(Table { create_sql, .. }) => {
4129 create_sql.as_deref().unwrap_or("<builtin>")
4130 }
4131 CatalogItem::Source(Source { create_sql, .. }) => {
4132 create_sql.as_deref().unwrap_or("<builtin>")
4133 }
4134 CatalogItem::Sink(Sink { create_sql, .. }) => create_sql,
4135 CatalogItem::View(View { create_sql, .. }) => create_sql,
4136 CatalogItem::MaterializedView(MaterializedView { create_sql, .. }) => create_sql,
4137 CatalogItem::Index(Index { create_sql, .. }) => create_sql,
4138 CatalogItem::Type(Type { create_sql, .. }) => {
4139 create_sql.as_deref().unwrap_or("<builtin>")
4140 }
4141 CatalogItem::Secret(Secret { create_sql, .. }) => create_sql,
4142 CatalogItem::Connection(Connection { create_sql, .. }) => create_sql,
4143 CatalogItem::MetricSink(MetricSink { create_sql, .. }) => create_sql,
4144 CatalogItem::Func(_) => "<builtin>",
4145 CatalogItem::Log(_) => "<builtin>",
4146 }
4147 }
4148
4149 fn item_type(&self) -> SqlCatalogItemType {
4150 self.item().typ()
4151 }
4152
4153 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)> {
4154 if let CatalogItem::Index(Index { keys, on, .. }) = self.item() {
4155 Some((keys, *on))
4156 } else {
4157 None
4158 }
4159 }
4160
4161 fn writable_table_details(&self) -> Option<&[Expr<Aug>]> {
4162 if let CatalogItem::Table(Table {
4163 data_source: TableDataSource::TableWrites { defaults },
4164 ..
4165 }) = self.item()
4166 {
4167 Some(defaults.as_slice())
4168 } else {
4169 None
4170 }
4171 }
4172
4173 fn replacement_target(&self) -> Option<CatalogItemId> {
4174 if let CatalogItem::MaterializedView(mv) = self.item() {
4175 mv.replacement_target
4176 } else {
4177 None
4178 }
4179 }
4180
4181 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>> {
4182 if let CatalogItem::Type(Type { details, .. }) = self.item() {
4183 Some(details)
4184 } else {
4185 None
4186 }
4187 }
4188
4189 fn references(&self) -> &ResolvedIds {
4190 self.references()
4191 }
4192
4193 fn uses(&self) -> BTreeSet<CatalogItemId> {
4194 self.uses()
4195 }
4196
4197 fn referenced_by(&self) -> &[CatalogItemId] {
4198 self.referenced_by()
4199 }
4200
4201 fn used_by(&self) -> &[CatalogItemId] {
4202 self.used_by()
4203 }
4204
4205 fn subsource_details(
4206 &self,
4207 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
4208 self.subsource_details()
4209 }
4210
4211 fn source_export_details(
4212 &self,
4213 ) -> Option<(
4214 CatalogItemId,
4215 &UnresolvedItemName,
4216 &SourceExportDetails,
4217 &SourceExportDataConfig<ReferencedConnection>,
4218 )> {
4219 self.source_export_details()
4220 }
4221
4222 fn is_progress_source(&self) -> bool {
4223 self.is_progress_source()
4224 }
4225
4226 fn progress_id(&self) -> Option<CatalogItemId> {
4227 self.progress_id()
4228 }
4229
4230 fn owner_id(&self) -> RoleId {
4231 self.owner_id
4232 }
4233
4234 fn privileges(&self) -> &PrivilegeMap {
4235 &self.privileges
4236 }
4237
4238 fn cluster_id(&self) -> Option<ClusterId> {
4239 self.item().cluster_id()
4240 }
4241
4242 fn at_version(
4243 &self,
4244 version: RelationVersionSelector,
4245 ) -> Box<dyn mz_sql::catalog::CatalogCollectionItem> {
4246 Box::new(CatalogCollectionEntry {
4247 entry: self.clone(),
4248 version,
4249 })
4250 }
4251
4252 fn latest_version(&self) -> Option<RelationVersion> {
4253 self.table().map(|t| t.desc.latest_version())
4254 }
4255}
4256
4257#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4259pub struct StateUpdate {
4260 pub kind: StateUpdateKind,
4261 pub ts: Timestamp,
4262 pub diff: StateDiff,
4263}
4264
4265#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4269pub enum StateUpdateKind {
4270 Role(durable::objects::Role),
4271 RoleAuth(durable::objects::RoleAuth),
4272 Database(durable::objects::Database),
4273 Schema(durable::objects::Schema),
4274 DefaultPrivilege(durable::objects::DefaultPrivilege),
4275 SystemPrivilege(MzAclItem),
4276 SystemConfiguration(durable::objects::SystemConfiguration),
4277 Cluster(durable::objects::Cluster),
4278 ClusterSystemConfiguration(durable::objects::ClusterSystemConfiguration),
4279 NetworkPolicy(durable::objects::NetworkPolicy),
4280 IntrospectionSourceIndex(durable::objects::IntrospectionSourceIndex),
4281 ClusterReplica(durable::objects::ClusterReplica),
4282 ReplicaSystemConfiguration(durable::objects::ReplicaSystemConfiguration),
4283 SourceReferences(durable::objects::SourceReferences),
4284 SystemObjectMapping(durable::objects::SystemObjectMapping),
4285 Item(durable::objects::Item),
4286 Comment(durable::objects::Comment),
4287 AuditLog(durable::objects::AuditLog),
4288 StorageCollectionMetadata(durable::objects::StorageCollectionMetadata),
4290 UnfinalizedShard(durable::objects::UnfinalizedShard),
4291}
4292
4293#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
4295pub enum StateDiff {
4296 Retraction,
4297 Addition,
4298}
4299
4300impl From<StateDiff> for Diff {
4301 fn from(diff: StateDiff) -> Self {
4302 match diff {
4303 StateDiff::Retraction => Diff::MINUS_ONE,
4304 StateDiff::Addition => Diff::ONE,
4305 }
4306 }
4307}
4308impl TryFrom<Diff> for StateDiff {
4309 type Error = String;
4310
4311 fn try_from(diff: Diff) -> Result<Self, Self::Error> {
4312 match diff {
4313 Diff::MINUS_ONE => Ok(Self::Retraction),
4314 Diff::ONE => Ok(Self::Addition),
4315 diff => Err(format!("invalid diff {diff}")),
4316 }
4317 }
4318}