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