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 arrangement_compression,
415 replication_factor,
416 optimizer_feature_overrides,
417 schedule,
418 auto_scaling_strategy,
419 reconfiguration: _,
422 burst: _,
423 }) => {
424 let introspection = match logging {
425 ReplicaLogging {
426 log_logging,
427 interval: Some(interval),
428 } => Some(ComputeReplicaIntrospectionConfig {
429 debugging: *log_logging,
430 interval: interval.clone(),
431 }),
432 ReplicaLogging {
433 log_logging: _,
434 interval: None,
435 } => None,
436 };
437 let compute = ComputeReplicaConfig {
438 introspection,
439 arrangement_compression: *arrangement_compression,
440 };
441 CreateClusterVariant::Managed(CreateClusterManagedPlan {
442 replication_factor: replication_factor.clone(),
443 size: size.clone(),
444 availability_zones: availability_zones.clone(),
445 compute,
446 optimizer_feature_overrides: optimizer_feature_overrides.clone(),
447 schedule: schedule.clone(),
448 auto_scaling_strategy: auto_scaling_strategy.clone(),
449 })
450 }
451 ClusterVariant::Unmanaged => {
452 return Err(PlanError::Unsupported {
455 feature: "SHOW CREATE for unmanaged clusters".to_string(),
456 discussion_no: None,
457 });
458 }
459 };
460 let workload_class = self.config.workload_class.clone();
461 Ok(CreateClusterPlan {
462 name,
463 variant,
464 workload_class,
465 })
466 }
467}
468
469impl From<Cluster> for durable::Cluster {
470 fn from(cluster: Cluster) -> durable::Cluster {
471 durable::Cluster {
472 id: cluster.id,
473 name: cluster.name,
474 owner_id: cluster.owner_id,
475 privileges: cluster.privileges.into_all_values().collect(),
476 config: cluster.config.into(),
477 }
478 }
479}
480
481impl From<durable::Cluster> for Cluster {
482 fn from(
483 durable::Cluster {
484 id,
485 name,
486 owner_id,
487 privileges,
488 config,
489 }: durable::Cluster,
490 ) -> Self {
491 Cluster {
492 name: name.clone(),
493 id,
494 bound_objects: BTreeSet::new(),
495 log_indexes: BTreeMap::new(),
496 replica_id_by_name_: BTreeMap::new(),
497 replicas_by_id_: BTreeMap::new(),
498 owner_id,
499 privileges: PrivilegeMap::from_mz_acl_items(privileges),
500 config: config.into(),
501 }
502 }
503}
504
505impl UpdateFrom<durable::Cluster> for Cluster {
506 fn update_from(
507 &mut self,
508 durable::Cluster {
509 id,
510 name,
511 owner_id,
512 privileges,
513 config,
514 }: durable::Cluster,
515 ) {
516 self.id = id;
517 self.name = name;
518 self.owner_id = owner_id;
519 self.privileges = PrivilegeMap::from_mz_acl_items(privileges);
520 self.config = config.into();
521 }
522}
523
524#[derive(Debug, Serialize, Clone, PartialEq)]
525pub struct ClusterReplica {
526 pub name: String,
527 pub cluster_id: ClusterId,
528 pub replica_id: ReplicaId,
529 pub config: ReplicaConfig,
530 pub owner_id: RoleId,
531}
532
533impl From<ClusterReplica> for durable::ClusterReplica {
534 fn from(replica: ClusterReplica) -> durable::ClusterReplica {
535 durable::ClusterReplica {
536 cluster_id: replica.cluster_id,
537 replica_id: replica.replica_id,
538 name: replica.name,
539 config: replica.config.into(),
540 owner_id: replica.owner_id,
541 }
542 }
543}
544
545#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
546pub struct ClusterReplicaProcessStatus {
547 pub status: ClusterStatus,
548 pub restart_count: u64,
551 pub time: DateTime<Utc>,
553}
554
555#[derive(Debug, Serialize, Clone, PartialEq)]
556pub struct SourceReferences {
557 pub updated_at: u64,
558 pub references: Vec<SourceReference>,
559}
560
561#[derive(Debug, Serialize, Clone, PartialEq)]
562pub struct SourceReference {
563 pub name: String,
564 pub namespace: Option<String>,
565 pub columns: Vec<String>,
566}
567
568impl From<SourceReference> for durable::SourceReference {
569 fn from(source_reference: SourceReference) -> durable::SourceReference {
570 durable::SourceReference {
571 name: source_reference.name,
572 namespace: source_reference.namespace,
573 columns: source_reference.columns,
574 }
575 }
576}
577
578impl SourceReferences {
579 pub fn to_durable(self, source_id: CatalogItemId) -> durable::SourceReferences {
580 durable::SourceReferences {
581 source_id,
582 updated_at: self.updated_at,
583 references: self.references.into_iter().map(Into::into).collect(),
584 }
585 }
586}
587
588impl From<durable::SourceReference> for SourceReference {
589 fn from(source_reference: durable::SourceReference) -> SourceReference {
590 SourceReference {
591 name: source_reference.name,
592 namespace: source_reference.namespace,
593 columns: source_reference.columns,
594 }
595 }
596}
597
598impl From<durable::SourceReferences> for SourceReferences {
599 fn from(source_references: durable::SourceReferences) -> SourceReferences {
600 SourceReferences {
601 updated_at: source_references.updated_at,
602 references: source_references
603 .references
604 .into_iter()
605 .map(|source_reference| source_reference.into())
606 .collect(),
607 }
608 }
609}
610
611impl From<mz_sql::plan::SourceReference> for SourceReference {
612 fn from(source_reference: mz_sql::plan::SourceReference) -> SourceReference {
613 SourceReference {
614 name: source_reference.name,
615 namespace: source_reference.namespace,
616 columns: source_reference.columns,
617 }
618 }
619}
620
621impl From<mz_sql::plan::SourceReferences> for SourceReferences {
622 fn from(source_references: mz_sql::plan::SourceReferences) -> SourceReferences {
623 SourceReferences {
624 updated_at: source_references.updated_at,
625 references: source_references
626 .references
627 .into_iter()
628 .map(|source_reference| source_reference.into())
629 .collect(),
630 }
631 }
632}
633
634impl From<SourceReferences> for mz_sql::plan::SourceReferences {
635 fn from(source_references: SourceReferences) -> mz_sql::plan::SourceReferences {
636 mz_sql::plan::SourceReferences {
637 updated_at: source_references.updated_at,
638 references: source_references
639 .references
640 .into_iter()
641 .map(|source_reference| source_reference.into())
642 .collect(),
643 }
644 }
645}
646
647impl From<SourceReference> for mz_sql::plan::SourceReference {
648 fn from(source_reference: SourceReference) -> mz_sql::plan::SourceReference {
649 mz_sql::plan::SourceReference {
650 name: source_reference.name,
651 namespace: source_reference.namespace,
652 columns: source_reference.columns,
653 }
654 }
655}
656
657#[derive(Clone, Debug, Serialize)]
658pub struct CatalogEntry {
659 pub item: CatalogItem,
660 #[serde(skip)]
661 pub referenced_by: Vec<CatalogItemId>,
662 #[serde(skip)]
666 pub used_by: Vec<CatalogItemId>,
667 pub id: CatalogItemId,
668 pub oid: u32,
669 pub name: QualifiedItemName,
670 pub owner_id: RoleId,
671 pub privileges: PrivilegeMap,
672}
673
674#[derive(Clone, Debug)]
689pub struct CatalogCollectionEntry {
690 pub entry: CatalogEntry,
691 pub version: RelationVersionSelector,
692}
693
694impl CatalogCollectionEntry {
695 pub fn relation_desc(&self) -> Option<Cow<'_, RelationDesc>> {
696 self.item().relation_desc(self.version)
697 }
698}
699
700impl mz_sql::catalog::CatalogCollectionItem for CatalogCollectionEntry {
701 fn relation_desc(&self) -> Option<Cow<'_, RelationDesc>> {
702 self.item().relation_desc(self.version)
703 }
704
705 fn global_id(&self) -> GlobalId {
706 self.entry
707 .item()
708 .global_id_for_version(self.version)
709 .expect("catalog corruption, missing version!")
710 }
711}
712
713impl Deref for CatalogCollectionEntry {
714 type Target = CatalogEntry;
715
716 fn deref(&self) -> &CatalogEntry {
717 &self.entry
718 }
719}
720
721impl mz_sql::catalog::CatalogItem for CatalogCollectionEntry {
722 fn name(&self) -> &QualifiedItemName {
723 self.entry.name()
724 }
725
726 fn id(&self) -> CatalogItemId {
727 self.entry.id()
728 }
729
730 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_> {
731 Box::new(self.entry.global_ids())
732 }
733
734 fn oid(&self) -> u32 {
735 self.entry.oid()
736 }
737
738 fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
739 self.entry.func()
740 }
741
742 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
743 self.entry.source_desc()
744 }
745
746 fn connection(
747 &self,
748 ) -> Result<mz_storage_types::connections::Connection<ReferencedConnection>, SqlCatalogError>
749 {
750 mz_sql::catalog::CatalogItem::connection(&self.entry)
751 }
752
753 fn create_sql(&self) -> &str {
754 self.entry.create_sql()
755 }
756
757 fn item_type(&self) -> SqlCatalogItemType {
758 self.entry.item_type()
759 }
760
761 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)> {
762 self.entry.index_details()
763 }
764
765 fn writable_table_details(&self) -> Option<&[Expr<Aug>]> {
766 self.entry.writable_table_details()
767 }
768
769 fn replacement_target(&self) -> Option<CatalogItemId> {
770 self.entry.replacement_target()
771 }
772
773 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>> {
774 self.entry.type_details()
775 }
776
777 fn references(&self) -> &ResolvedIds {
778 self.entry.references()
779 }
780
781 fn uses(&self) -> BTreeSet<CatalogItemId> {
782 self.entry.uses()
783 }
784
785 fn referenced_by(&self) -> &[CatalogItemId] {
786 self.entry.referenced_by()
787 }
788
789 fn used_by(&self) -> &[CatalogItemId] {
790 self.entry.used_by()
791 }
792
793 fn subsource_details(
794 &self,
795 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
796 self.entry.subsource_details()
797 }
798
799 fn source_export_details(
800 &self,
801 ) -> Option<(
802 CatalogItemId,
803 &UnresolvedItemName,
804 &SourceExportDetails,
805 &SourceExportDataConfig<ReferencedConnection>,
806 )> {
807 self.entry.source_export_details()
808 }
809
810 fn is_progress_source(&self) -> bool {
811 self.entry.is_progress_source()
812 }
813
814 fn progress_id(&self) -> Option<CatalogItemId> {
815 self.entry.progress_id()
816 }
817
818 fn owner_id(&self) -> RoleId {
819 *self.entry.owner_id()
820 }
821
822 fn privileges(&self) -> &PrivilegeMap {
823 self.entry.privileges()
824 }
825
826 fn cluster_id(&self) -> Option<ClusterId> {
827 self.entry.item().cluster_id()
828 }
829
830 fn at_version(
831 &self,
832 version: RelationVersionSelector,
833 ) -> Box<dyn mz_sql::catalog::CatalogCollectionItem> {
834 Box::new(CatalogCollectionEntry {
835 entry: self.entry.clone(),
836 version,
837 })
838 }
839
840 fn latest_version(&self) -> Option<RelationVersion> {
841 self.entry.latest_version()
842 }
843}
844
845#[derive(Debug, Clone, Serialize)]
846pub enum CatalogItem {
847 Table(Table),
848 Source(Source),
849 Log(Log),
850 View(View),
851 MaterializedView(MaterializedView),
852 Sink(Sink),
853 Index(Index),
854 Type(Type),
855 Func(Func),
856 Secret(Secret),
857 Connection(Connection),
858}
859
860impl From<CatalogEntry> for durable::Item {
861 fn from(entry: CatalogEntry) -> durable::Item {
862 let (create_sql, global_id, extra_versions) = entry.item.into_serialized();
863 durable::Item {
864 id: entry.id,
865 oid: entry.oid,
866 global_id,
867 schema_id: entry.name.qualifiers.schema_spec.into(),
868 name: entry.name.item,
869 create_sql,
870 owner_id: entry.owner_id,
871 privileges: entry.privileges.into_all_values().collect(),
872 extra_versions,
873 }
874 }
875}
876
877#[derive(Debug, Clone, Serialize)]
878pub struct Table {
879 pub create_sql: Option<String>,
881 pub desc: VersionedRelationDesc,
883 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
885 pub collections: BTreeMap<RelationVersion, GlobalId>,
886 #[serde(skip)]
888 pub conn_id: Option<ConnectionId>,
889 pub resolved_ids: ResolvedIds,
891 pub custom_logical_compaction_window: Option<CompactionWindow>,
893 pub is_retained_metrics_object: bool,
898 pub data_source: TableDataSource,
900}
901
902impl Table {
903 pub fn timeline(&self) -> Timeline {
904 match &self.data_source {
905 TableDataSource::TableWrites { .. } => Timeline::EpochMilliseconds,
908 TableDataSource::DataSource { timeline, .. } => timeline.clone(),
909 }
910 }
911
912 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
914 self.collections.values().copied()
915 }
916
917 pub fn global_id_writes(&self) -> GlobalId {
919 *self
920 .collections
921 .last_key_value()
922 .expect("at least one version of a table")
923 .1
924 }
925
926 pub fn collection_descs(
928 &self,
929 ) -> impl Iterator<Item = (GlobalId, RelationVersion, RelationDesc)> + '_ {
930 self.collections.iter().map(|(version, gid)| {
931 let desc = self
932 .desc
933 .at_version(RelationVersionSelector::Specific(*version));
934 (*gid, *version, desc)
935 })
936 }
937
938 pub fn desc_for(&self, id: &GlobalId) -> RelationDesc {
940 let (version, _gid) = self
941 .collections
942 .iter()
943 .find(|(_version, gid)| *gid == id)
944 .expect("GlobalId to exist");
945 self.desc
946 .at_version(RelationVersionSelector::Specific(*version))
947 }
948}
949
950#[derive(Clone, Debug, Serialize)]
951pub enum TableDataSource {
952 TableWrites {
954 #[serde(skip)]
955 defaults: Vec<Expr<Aug>>,
956 },
957
958 DataSource {
961 desc: DataSourceDesc,
962 timeline: Timeline,
963 },
964}
965
966#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
967pub enum DataSourceDesc {
968 Ingestion {
970 desc: SourceDesc<ReferencedConnection>,
971 cluster_id: ClusterId,
972 },
973 OldSyntaxIngestion {
975 desc: SourceDesc<ReferencedConnection>,
976 cluster_id: ClusterId,
977 progress_subsource: CatalogItemId,
980 data_config: SourceExportDataConfig<ReferencedConnection>,
981 details: SourceExportDetails,
982 },
983 IngestionExport {
991 ingestion_id: CatalogItemId,
992 external_reference: UnresolvedItemName,
993 details: SourceExportDetails,
994 data_config: SourceExportDataConfig<ReferencedConnection>,
995 },
996 Introspection(IntrospectionType),
998 Progress,
1000 Webhook {
1002 validate_using: Option<WebhookValidation>,
1004 body_format: WebhookBodyFormat,
1006 headers: WebhookHeaders,
1008 cluster_id: ClusterId,
1010 },
1011 Catalog,
1013}
1014
1015impl From<IntrospectionType> for DataSourceDesc {
1016 fn from(typ: IntrospectionType) -> Self {
1017 Self::Introspection(typ)
1018 }
1019}
1020
1021impl DataSourceDesc {
1022 pub fn formats(&self) -> (Option<&str>, Option<&str>) {
1024 match &self {
1025 DataSourceDesc::Ingestion { .. } => (None, None),
1026 DataSourceDesc::OldSyntaxIngestion { data_config, .. } => {
1027 match &data_config.encoding.as_ref() {
1028 Some(encoding) => match &encoding.key {
1029 Some(key) => (Some(key.type_()), Some(encoding.value.type_())),
1030 None => (None, Some(encoding.value.type_())),
1031 },
1032 None => (None, None),
1033 }
1034 }
1035 DataSourceDesc::IngestionExport { data_config, .. } => match &data_config.encoding {
1036 Some(encoding) => match &encoding.key {
1037 Some(key) => (Some(key.type_()), Some(encoding.value.type_())),
1038 None => (None, Some(encoding.value.type_())),
1039 },
1040 None => (None, None),
1041 },
1042 DataSourceDesc::Introspection(_)
1043 | DataSourceDesc::Webhook { .. }
1044 | DataSourceDesc::Progress
1045 | DataSourceDesc::Catalog => (None, None),
1046 }
1047 }
1048
1049 pub fn envelope(&self) -> Option<&str> {
1051 fn envelope_string(envelope: &SourceEnvelope) -> &str {
1056 match envelope {
1057 SourceEnvelope::None(_) => "none",
1058 SourceEnvelope::Upsert(upsert_envelope) => match upsert_envelope.style {
1059 mz_storage_types::sources::envelope::UpsertStyle::Default(_) => "upsert",
1060 mz_storage_types::sources::envelope::UpsertStyle::Debezium { .. } => {
1061 "debezium"
1065 }
1066 mz_storage_types::sources::envelope::UpsertStyle::ValueErrInline { .. } => {
1067 "upsert-value-err-inline"
1068 }
1069 },
1070 SourceEnvelope::CdcV2 => {
1071 "materialize"
1074 }
1075 }
1076 }
1077
1078 match self {
1079 DataSourceDesc::Ingestion { .. } => None,
1084 DataSourceDesc::OldSyntaxIngestion { data_config, .. } => {
1085 Some(envelope_string(&data_config.envelope))
1086 }
1087 DataSourceDesc::IngestionExport { data_config, .. } => {
1088 Some(envelope_string(&data_config.envelope))
1089 }
1090 DataSourceDesc::Introspection(_)
1091 | DataSourceDesc::Webhook { .. }
1092 | DataSourceDesc::Progress
1093 | DataSourceDesc::Catalog => None,
1094 }
1095 }
1096}
1097
1098#[derive(Debug, Clone, Serialize)]
1099pub struct Source {
1100 pub create_sql: Option<String>,
1102 pub global_id: GlobalId,
1104 #[serde(skip)]
1106 pub data_source: DataSourceDesc,
1107 pub desc: RelationDesc,
1109 pub timeline: Timeline,
1111 pub resolved_ids: ResolvedIds,
1113 pub custom_logical_compaction_window: Option<CompactionWindow>,
1117 pub is_retained_metrics_object: bool,
1120}
1121
1122impl Source {
1123 pub fn new(
1130 plan: CreateSourcePlan,
1131 global_id: GlobalId,
1132 resolved_ids: ResolvedIds,
1133 custom_logical_compaction_window: Option<CompactionWindow>,
1134 is_retained_metrics_object: bool,
1135 ) -> Source {
1136 Source {
1137 create_sql: Some(plan.source.create_sql),
1138 data_source: match plan.source.data_source {
1139 mz_sql::plan::DataSourceDesc::Ingestion(desc) => DataSourceDesc::Ingestion {
1140 desc,
1141 cluster_id: plan
1142 .in_cluster
1143 .expect("ingestion-based sources must be given a cluster ID"),
1144 },
1145 mz_sql::plan::DataSourceDesc::OldSyntaxIngestion {
1146 desc,
1147 progress_subsource,
1148 data_config,
1149 details,
1150 } => DataSourceDesc::OldSyntaxIngestion {
1151 desc,
1152 cluster_id: plan
1153 .in_cluster
1154 .expect("ingestion-based sources must be given a cluster ID"),
1155 progress_subsource,
1156 data_config,
1157 details,
1158 },
1159 mz_sql::plan::DataSourceDesc::Progress => {
1160 assert!(
1161 plan.in_cluster.is_none(),
1162 "subsources must not have a host config or cluster_id defined"
1163 );
1164 DataSourceDesc::Progress
1165 }
1166 mz_sql::plan::DataSourceDesc::IngestionExport {
1167 ingestion_id,
1168 external_reference,
1169 details,
1170 data_config,
1171 } => {
1172 assert!(
1173 plan.in_cluster.is_none(),
1174 "subsources must not have a host config or cluster_id defined"
1175 );
1176 DataSourceDesc::IngestionExport {
1177 ingestion_id,
1178 external_reference,
1179 details,
1180 data_config,
1181 }
1182 }
1183 mz_sql::plan::DataSourceDesc::Webhook {
1184 validate_using,
1185 body_format,
1186 headers,
1187 cluster_id,
1188 } => {
1189 mz_ore::soft_assert_or_log!(
1190 cluster_id.is_none(),
1191 "cluster_id set at Source level for Webhooks"
1192 );
1193 DataSourceDesc::Webhook {
1194 validate_using,
1195 body_format,
1196 headers,
1197 cluster_id: plan
1198 .in_cluster
1199 .expect("webhook sources must be given a cluster ID"),
1200 }
1201 }
1202 },
1203 desc: plan.source.desc,
1204 global_id,
1205 timeline: plan.timeline,
1206 resolved_ids,
1207 custom_logical_compaction_window: plan
1208 .source
1209 .compaction_window
1210 .or(custom_logical_compaction_window),
1211 is_retained_metrics_object,
1212 }
1213 }
1214
1215 pub fn source_type(&self) -> &str {
1217 match &self.data_source {
1218 DataSourceDesc::Ingestion { desc, .. }
1219 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => desc.connection.name(),
1220 DataSourceDesc::Progress => "progress",
1221 DataSourceDesc::IngestionExport { .. } => "subsource",
1222 DataSourceDesc::Introspection(_) | DataSourceDesc::Catalog => "source",
1223 DataSourceDesc::Webhook { .. } => "webhook",
1224 }
1225 }
1226
1227 pub fn connection_id(&self) -> Option<CatalogItemId> {
1229 match &self.data_source {
1230 DataSourceDesc::Ingestion { desc, .. }
1231 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => desc.connection.connection_id(),
1232 DataSourceDesc::IngestionExport { .. }
1233 | DataSourceDesc::Introspection(_)
1234 | DataSourceDesc::Webhook { .. }
1235 | DataSourceDesc::Progress
1236 | DataSourceDesc::Catalog => None,
1237 }
1238 }
1239
1240 pub fn global_id(&self) -> GlobalId {
1242 self.global_id
1243 }
1244
1245 pub fn user_controllable_persist_shard_count(&self) -> i64 {
1253 match &self.data_source {
1254 DataSourceDesc::Ingestion { .. } => 0,
1255 DataSourceDesc::OldSyntaxIngestion { desc, .. } => {
1256 match &desc.connection {
1257 GenericSourceConnection::Postgres(_)
1261 | GenericSourceConnection::MySql(_)
1262 | GenericSourceConnection::SqlServer(_) => 0,
1263 GenericSourceConnection::LoadGenerator(lg) => match lg.load_generator {
1264 LoadGenerator::Clock
1266 | LoadGenerator::Counter { .. }
1267 | LoadGenerator::Datums
1268 | LoadGenerator::KeyValue(_) => 1,
1269 LoadGenerator::Auction
1270 | LoadGenerator::Marketing
1271 | LoadGenerator::Tpch { .. } => 0,
1272 },
1273 GenericSourceConnection::Kafka(_) => 1,
1274 }
1275 }
1276 DataSourceDesc::IngestionExport { .. } => 1,
1279 DataSourceDesc::Webhook { .. } => 1,
1280 DataSourceDesc::Introspection(_)
1283 | DataSourceDesc::Progress
1284 | DataSourceDesc::Catalog => 0,
1285 }
1286 }
1287}
1288
1289#[derive(Debug, Clone, Serialize)]
1290pub struct Log {
1291 pub variant: LogVariant,
1293 pub global_id: GlobalId,
1295}
1296
1297impl Log {
1298 pub fn global_id(&self) -> GlobalId {
1300 self.global_id
1301 }
1302}
1303
1304#[derive(Debug, Clone, Serialize)]
1305pub struct Sink {
1306 pub create_sql: String,
1308 pub global_id: GlobalId,
1310 pub from: GlobalId,
1312 pub connection: StorageSinkConnection<ReferencedConnection>,
1314 pub envelope: SinkEnvelope,
1318 pub with_snapshot: bool,
1320 pub version: u64,
1322 pub resolved_ids: ResolvedIds,
1324 pub cluster_id: ClusterId,
1326 pub commit_interval: Option<Duration>,
1328}
1329
1330impl Sink {
1331 pub fn sink_type(&self) -> &str {
1332 self.connection.name()
1333 }
1334
1335 pub fn envelope(&self) -> Option<&str> {
1337 match &self.envelope {
1338 SinkEnvelope::Debezium => Some("debezium"),
1339 SinkEnvelope::Upsert => Some("upsert"),
1340 SinkEnvelope::Append => Some("append"),
1341 }
1342 }
1343
1344 pub fn combined_format(&self) -> Option<Cow<'_, str>> {
1349 match &self.connection {
1350 StorageSinkConnection::Kafka(connection) => Some(connection.format.get_format_name()),
1351 StorageSinkConnection::Iceberg(_) => None,
1352 }
1353 }
1354
1355 pub fn formats(&self) -> Option<(Option<&str>, &str)> {
1357 match &self.connection {
1358 StorageSinkConnection::Kafka(connection) => {
1359 let key_format = connection
1360 .format
1361 .key_format
1362 .as_ref()
1363 .map(|f| f.get_format_name());
1364 let value_format = connection.format.value_format.get_format_name();
1365 Some((key_format, value_format))
1366 }
1367 StorageSinkConnection::Iceberg(_) => None,
1368 }
1369 }
1370
1371 pub fn connection_id(&self) -> Option<CatalogItemId> {
1372 self.connection.connection_id()
1373 }
1374
1375 pub fn global_id(&self) -> GlobalId {
1377 self.global_id
1378 }
1379}
1380
1381#[derive(Debug, Clone, Serialize)]
1382pub struct View {
1383 pub create_sql: String,
1385 pub global_id: GlobalId,
1387 pub raw_expr: Arc<HirRelationExpr>,
1389 pub locally_optimized_expr: Arc<OptimizedMirRelationExpr>,
1391 pub desc: RelationDesc,
1393 pub conn_id: Option<ConnectionId>,
1395 pub resolved_ids: ResolvedIds,
1397 pub dependencies: DependencyIds,
1399}
1400
1401impl View {
1402 pub fn global_id(&self) -> GlobalId {
1404 self.global_id
1405 }
1406}
1407
1408#[derive(Debug, Clone, Serialize)]
1409pub struct MaterializedView {
1410 pub create_sql: String,
1412 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
1414 pub collections: BTreeMap<RelationVersion, GlobalId>,
1415 pub raw_expr: Arc<HirRelationExpr>,
1417 pub locally_optimized_expr: Arc<OptimizedMirRelationExpr>,
1419 pub desc: VersionedRelationDesc,
1421 pub resolved_ids: ResolvedIds,
1423 pub dependencies: DependencyIds,
1425 pub replacement_target: Option<CatalogItemId>,
1427 pub cluster_id: ClusterId,
1429 pub target_replica: Option<ReplicaId>,
1431 pub non_null_assertions: Vec<usize>,
1435 pub custom_logical_compaction_window: Option<CompactionWindow>,
1437 pub refresh_schedule: Option<RefreshSchedule>,
1439 pub initial_as_of: Option<Antichain<mz_repr::Timestamp>>,
1444 #[serde(skip)]
1450 pub optimized_plan: Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1451 #[serde(skip)]
1453 pub physical_plan: Option<Arc<DataflowDescription<ComputePlan>>>,
1454 #[serde(skip)]
1456 pub dataflow_metainfo: Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1457}
1458
1459impl MaterializedView {
1460 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
1462 self.collections.values().copied()
1463 }
1464
1465 pub fn global_id_writes(&self) -> GlobalId {
1468 *self
1469 .collections
1470 .last_key_value()
1471 .expect("at least one version of a materialized view")
1472 .1
1473 }
1474
1475 pub fn collection_descs(
1477 &self,
1478 ) -> impl Iterator<Item = (GlobalId, RelationVersion, RelationDesc)> + '_ {
1479 self.collections.iter().map(|(version, gid)| {
1480 let desc = self
1481 .desc
1482 .at_version(RelationVersionSelector::Specific(*version));
1483 (*gid, *version, desc)
1484 })
1485 }
1486
1487 pub fn desc_for(&self, id: &GlobalId) -> RelationDesc {
1489 let (version, _gid) = self
1490 .collections
1491 .iter()
1492 .find(|(_version, gid)| *gid == id)
1493 .expect("GlobalId to exist");
1494 self.desc
1495 .at_version(RelationVersionSelector::Specific(*version))
1496 }
1497
1498 pub fn apply_replacement(&mut self, replacement: Self) {
1500 let target_id = replacement
1501 .replacement_target
1502 .expect("replacement has target");
1503
1504 fn parse(create_sql: &str) -> mz_sql::ast::CreateMaterializedViewStatement<Raw> {
1505 let res = mz_sql::parse::parse(create_sql).unwrap_or_else(|e| {
1506 panic!("invalid create_sql persisted in catalog: {e}\n{create_sql}");
1507 });
1508 if let Statement::CreateMaterializedView(cmvs) = res.into_element().ast {
1509 cmvs
1510 } else {
1511 panic!("invalid MV create_sql persisted in catalog\n{create_sql}");
1512 }
1513 }
1514
1515 let old_stmt = parse(&self.create_sql);
1516 let rpl_stmt = parse(&replacement.create_sql);
1517 let new_stmt = mz_sql::ast::CreateMaterializedViewStatement {
1518 if_exists: old_stmt.if_exists,
1519 name: old_stmt.name,
1520 columns: rpl_stmt.columns,
1521 replacement_for: None,
1522 in_cluster: rpl_stmt.in_cluster,
1523 in_cluster_replica: rpl_stmt.in_cluster_replica,
1524 query: rpl_stmt.query,
1525 as_of: rpl_stmt.as_of,
1526 with_options: rpl_stmt.with_options,
1527 };
1528 let create_sql = new_stmt.to_ast_string_stable();
1529
1530 let mut collections = std::mem::take(&mut self.collections);
1531 let latest_version = collections.keys().max().expect("at least one version");
1535 let new_version = latest_version.bump();
1536 collections.insert(new_version, replacement.global_id_writes());
1537
1538 let mut resolved_ids = replacement.resolved_ids;
1539 resolved_ids.remove_item(&target_id);
1540 let mut dependencies = replacement.dependencies;
1541 dependencies.0.remove(&target_id);
1542
1543 *self = Self {
1544 create_sql,
1545 collections,
1546 raw_expr: replacement.raw_expr,
1547 locally_optimized_expr: replacement.locally_optimized_expr,
1548 desc: replacement.desc,
1549 resolved_ids,
1550 dependencies,
1551 replacement_target: None,
1552 cluster_id: replacement.cluster_id,
1553 target_replica: replacement.target_replica,
1554 non_null_assertions: replacement.non_null_assertions,
1555 custom_logical_compaction_window: replacement.custom_logical_compaction_window,
1556 refresh_schedule: replacement.refresh_schedule,
1557 initial_as_of: replacement.initial_as_of,
1558 optimized_plan: replacement.optimized_plan,
1559 physical_plan: replacement.physical_plan,
1560 dataflow_metainfo: replacement.dataflow_metainfo,
1561 };
1562 }
1563}
1564
1565#[derive(Debug, Clone, Serialize)]
1566pub struct Index {
1567 pub create_sql: String,
1569 pub global_id: GlobalId,
1571 pub on: GlobalId,
1573 pub keys: Arc<[MirScalarExpr]>,
1575 pub conn_id: Option<ConnectionId>,
1577 pub resolved_ids: ResolvedIds,
1579 pub cluster_id: ClusterId,
1581 pub custom_logical_compaction_window: Option<CompactionWindow>,
1583 pub is_retained_metrics_object: bool,
1588 #[serde(skip)]
1594 pub optimized_plan: Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1595 #[serde(skip)]
1597 pub physical_plan: Option<Arc<DataflowDescription<ComputePlan>>>,
1598 #[serde(skip)]
1600 pub dataflow_metainfo: Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1601}
1602
1603impl Index {
1604 pub fn global_id(&self) -> GlobalId {
1606 self.global_id
1607 }
1608}
1609
1610#[derive(Debug, Clone, Serialize)]
1611pub struct Type {
1612 pub create_sql: Option<String>,
1614 pub global_id: GlobalId,
1616 #[serde(skip)]
1617 pub details: CatalogTypeDetails<IdReference>,
1618 pub resolved_ids: ResolvedIds,
1620}
1621
1622#[derive(Debug, Clone, Serialize)]
1623pub struct Func {
1624 #[serde(skip)]
1626 pub inner: &'static mz_sql::func::Func,
1627 pub global_id: GlobalId,
1629}
1630
1631#[derive(Debug, Clone, Serialize)]
1632pub struct Secret {
1633 pub create_sql: String,
1635 pub global_id: GlobalId,
1637}
1638
1639#[derive(Debug, Clone, Serialize)]
1640pub struct Connection {
1641 pub create_sql: String,
1643 pub global_id: GlobalId,
1645 pub details: ConnectionDetails,
1647 pub resolved_ids: ResolvedIds,
1649}
1650
1651impl Connection {
1652 pub fn global_id(&self) -> GlobalId {
1654 self.global_id
1655 }
1656}
1657
1658#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1659pub struct NetworkPolicy {
1660 pub name: String,
1661 pub id: NetworkPolicyId,
1662 pub oid: u32,
1663 pub rules: Vec<NetworkPolicyRule>,
1664 pub owner_id: RoleId,
1665 pub privileges: PrivilegeMap,
1666}
1667
1668impl From<NetworkPolicy> for durable::NetworkPolicy {
1669 fn from(policy: NetworkPolicy) -> durable::NetworkPolicy {
1670 durable::NetworkPolicy {
1671 id: policy.id,
1672 oid: policy.oid,
1673 name: policy.name,
1674 rules: policy.rules,
1675 owner_id: policy.owner_id,
1676 privileges: policy.privileges.into_all_values().collect(),
1677 }
1678 }
1679}
1680
1681impl From<durable::NetworkPolicy> for NetworkPolicy {
1682 fn from(
1683 durable::NetworkPolicy {
1684 id,
1685 oid,
1686 name,
1687 rules,
1688 owner_id,
1689 privileges,
1690 }: durable::NetworkPolicy,
1691 ) -> Self {
1692 NetworkPolicy {
1693 id,
1694 oid,
1695 name,
1696 rules,
1697 owner_id,
1698 privileges: PrivilegeMap::from_mz_acl_items(privileges),
1699 }
1700 }
1701}
1702
1703impl UpdateFrom<durable::NetworkPolicy> for NetworkPolicy {
1704 fn update_from(
1705 &mut self,
1706 durable::NetworkPolicy {
1707 id,
1708 oid,
1709 name,
1710 rules,
1711 owner_id,
1712 privileges,
1713 }: durable::NetworkPolicy,
1714 ) {
1715 self.id = id;
1716 self.oid = oid;
1717 self.name = name;
1718 self.rules = rules;
1719 self.owner_id = owner_id;
1720 self.privileges = PrivilegeMap::from_mz_acl_items(privileges);
1721 }
1722}
1723
1724impl CatalogItem {
1725 pub fn typ(&self) -> mz_sql::catalog::CatalogItemType {
1727 match self {
1728 CatalogItem::Table(_) => CatalogItemType::Table,
1729 CatalogItem::Source(_) => CatalogItemType::Source,
1730 CatalogItem::Log(_) => CatalogItemType::Source,
1731 CatalogItem::Sink(_) => CatalogItemType::Sink,
1732 CatalogItem::View(_) => CatalogItemType::View,
1733 CatalogItem::MaterializedView(_) => CatalogItemType::MaterializedView,
1734 CatalogItem::Index(_) => CatalogItemType::Index,
1735 CatalogItem::Type(_) => CatalogItemType::Type,
1736 CatalogItem::Func(_) => CatalogItemType::Func,
1737 CatalogItem::Secret(_) => CatalogItemType::Secret,
1738 CatalogItem::Connection(_) => CatalogItemType::Connection,
1739 }
1740 }
1741
1742 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
1744 let gid = match self {
1745 CatalogItem::Source(source) => source.global_id,
1746 CatalogItem::Log(log) => log.global_id,
1747 CatalogItem::Sink(sink) => sink.global_id,
1748 CatalogItem::View(view) => view.global_id,
1749 CatalogItem::MaterializedView(mv) => {
1750 return itertools::Either::Left(mv.collections.values().copied());
1751 }
1752 CatalogItem::Index(index) => index.global_id,
1753 CatalogItem::Func(func) => func.global_id,
1754 CatalogItem::Type(ty) => ty.global_id,
1755 CatalogItem::Secret(secret) => secret.global_id,
1756 CatalogItem::Connection(conn) => conn.global_id,
1757 CatalogItem::Table(table) => {
1758 return itertools::Either::Left(table.collections.values().copied());
1759 }
1760 };
1761 itertools::Either::Right(std::iter::once(gid))
1762 }
1763
1764 pub fn latest_global_id(&self) -> GlobalId {
1768 match self {
1769 CatalogItem::Source(source) => source.global_id,
1770 CatalogItem::Log(log) => log.global_id,
1771 CatalogItem::Sink(sink) => sink.global_id,
1772 CatalogItem::View(view) => view.global_id,
1773 CatalogItem::MaterializedView(mv) => mv.global_id_writes(),
1774 CatalogItem::Index(index) => index.global_id,
1775 CatalogItem::Func(func) => func.global_id,
1776 CatalogItem::Type(ty) => ty.global_id,
1777 CatalogItem::Secret(secret) => secret.global_id,
1778 CatalogItem::Connection(conn) => conn.global_id,
1779 CatalogItem::Table(table) => table.global_id_writes(),
1780 }
1781 }
1782
1783 pub fn optimized_plan(&self) -> Option<&Arc<DataflowDescription<OptimizedMirRelationExpr>>> {
1785 match self {
1786 CatalogItem::Index(idx) => idx.optimized_plan.as_ref(),
1787 CatalogItem::MaterializedView(mv) => mv.optimized_plan.as_ref(),
1788 _ => None,
1789 }
1790 }
1791
1792 pub fn physical_plan(&self) -> Option<&Arc<DataflowDescription<ComputePlan>>> {
1794 match self {
1795 CatalogItem::Index(idx) => idx.physical_plan.as_ref(),
1796 CatalogItem::MaterializedView(mv) => mv.physical_plan.as_ref(),
1797 _ => None,
1798 }
1799 }
1800
1801 pub fn dataflow_metainfo(&self) -> Option<&DataflowMetainfo<Arc<OptimizerNotice>>> {
1803 match self {
1804 CatalogItem::Index(idx) => idx.dataflow_metainfo.as_ref(),
1805 CatalogItem::MaterializedView(mv) => mv.dataflow_metainfo.as_ref(),
1806 _ => None,
1807 }
1808 }
1809
1810 pub fn plan_fields_mut(
1815 &mut self,
1816 ) -> Option<(
1817 &mut Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1818 &mut Option<Arc<DataflowDescription<ComputePlan>>>,
1819 &mut Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1820 )> {
1821 match self {
1822 CatalogItem::Index(idx) => Some((
1823 &mut idx.optimized_plan,
1824 &mut idx.physical_plan,
1825 &mut idx.dataflow_metainfo,
1826 )),
1827 CatalogItem::MaterializedView(mv) => Some((
1828 &mut mv.optimized_plan,
1829 &mut mv.physical_plan,
1830 &mut mv.dataflow_metainfo,
1831 )),
1832 _ => None,
1833 }
1834 }
1835
1836 pub fn is_storage_collection(&self) -> bool {
1838 match self {
1839 CatalogItem::Table(_)
1840 | CatalogItem::Source(_)
1841 | CatalogItem::MaterializedView(_)
1842 | CatalogItem::Sink(_) => true,
1843 CatalogItem::Log(_)
1844 | CatalogItem::View(_)
1845 | CatalogItem::Index(_)
1846 | CatalogItem::Type(_)
1847 | CatalogItem::Func(_)
1848 | CatalogItem::Secret(_)
1849 | CatalogItem::Connection(_) => false,
1850 }
1851 }
1852
1853 pub fn relation_desc(&self, version: RelationVersionSelector) -> Option<Cow<'_, RelationDesc>> {
1862 match &self {
1863 CatalogItem::Source(src) => Some(Cow::Borrowed(&src.desc)),
1864 CatalogItem::Log(log) => Some(Cow::Owned(log.variant.desc())),
1865 CatalogItem::Table(tbl) => Some(Cow::Owned(tbl.desc.at_version(version))),
1866 CatalogItem::View(view) => Some(Cow::Borrowed(&view.desc)),
1867 CatalogItem::MaterializedView(mview) => {
1868 Some(Cow::Owned(mview.desc.at_version(version)))
1869 }
1870 CatalogItem::Func(_)
1871 | CatalogItem::Index(_)
1872 | CatalogItem::Sink(_)
1873 | CatalogItem::Secret(_)
1874 | CatalogItem::Connection(_)
1875 | CatalogItem::Type(_) => None,
1876 }
1877 }
1878
1879 pub fn func(
1880 &self,
1881 entry: &CatalogEntry,
1882 ) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
1883 match &self {
1884 CatalogItem::Func(func) => Ok(func.inner),
1885 _ => Err(SqlCatalogError::UnexpectedType {
1886 name: entry.name().item.to_string(),
1887 actual_type: entry.item_type(),
1888 expected_type: CatalogItemType::Func,
1889 }),
1890 }
1891 }
1892
1893 pub fn source_desc(
1894 &self,
1895 entry: &CatalogEntry,
1896 ) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
1897 match &self {
1898 CatalogItem::Source(source) => match &source.data_source {
1899 DataSourceDesc::Ingestion { desc, .. }
1900 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => Ok(Some(desc)),
1901 DataSourceDesc::IngestionExport { .. }
1902 | DataSourceDesc::Introspection(_)
1903 | DataSourceDesc::Webhook { .. }
1904 | DataSourceDesc::Progress
1905 | DataSourceDesc::Catalog => Ok(None),
1906 },
1907 _ => Err(SqlCatalogError::UnexpectedType {
1908 name: entry.name().item.to_string(),
1909 actual_type: entry.item_type(),
1910 expected_type: CatalogItemType::Source,
1911 }),
1912 }
1913 }
1914
1915 pub fn is_progress_source(&self) -> bool {
1917 matches!(
1918 self,
1919 CatalogItem::Source(Source {
1920 data_source: DataSourceDesc::Progress,
1921 ..
1922 })
1923 )
1924 }
1925
1926 pub fn references(&self) -> &ResolvedIds {
1929 static EMPTY: LazyLock<ResolvedIds> = LazyLock::new(ResolvedIds::empty);
1930 match self {
1931 CatalogItem::Func(_) => &*EMPTY,
1932 CatalogItem::Index(idx) => &idx.resolved_ids,
1933 CatalogItem::Sink(sink) => &sink.resolved_ids,
1934 CatalogItem::Source(source) => &source.resolved_ids,
1935 CatalogItem::Log(_) => &*EMPTY,
1936 CatalogItem::Table(table) => &table.resolved_ids,
1937 CatalogItem::Type(typ) => &typ.resolved_ids,
1938 CatalogItem::View(view) => &view.resolved_ids,
1939 CatalogItem::MaterializedView(mview) => &mview.resolved_ids,
1940 CatalogItem::Secret(_) => &*EMPTY,
1941 CatalogItem::Connection(connection) => &connection.resolved_ids,
1942 }
1943 }
1944
1945 pub fn uses(&self) -> BTreeSet<CatalogItemId> {
1951 let mut uses: BTreeSet<_> = self.references().items().copied().collect();
1952 match self {
1953 CatalogItem::Func(_) => {}
1956 CatalogItem::Index(_) => {}
1957 CatalogItem::Sink(_) => {}
1958 CatalogItem::Source(_) => {}
1959 CatalogItem::Log(_) => {}
1960 CatalogItem::Table(_) => {}
1961 CatalogItem::Type(_) => {}
1962 CatalogItem::View(view) => uses.extend(view.dependencies.0.iter().copied()),
1963 CatalogItem::MaterializedView(mview) => {
1964 uses.extend(mview.dependencies.0.iter().copied())
1965 }
1966 CatalogItem::Secret(_) => {}
1967 CatalogItem::Connection(_) => {}
1968 }
1969 uses
1970 }
1971
1972 pub fn conn_id(&self) -> Option<&ConnectionId> {
1975 match self {
1976 CatalogItem::View(view) => view.conn_id.as_ref(),
1977 CatalogItem::Index(index) => index.conn_id.as_ref(),
1978 CatalogItem::Table(table) => table.conn_id.as_ref(),
1979 CatalogItem::Log(_)
1980 | CatalogItem::Source(_)
1981 | CatalogItem::Sink(_)
1982 | CatalogItem::MaterializedView(_)
1983 | CatalogItem::Secret(_)
1984 | CatalogItem::Type(_)
1985 | CatalogItem::Func(_)
1986 | CatalogItem::Connection(_) => None,
1987 }
1988 }
1989
1990 pub fn set_conn_id(&mut self, conn_id: Option<ConnectionId>) {
1993 match self {
1994 CatalogItem::View(view) => view.conn_id = conn_id,
1995 CatalogItem::Index(index) => index.conn_id = conn_id,
1996 CatalogItem::Table(table) => table.conn_id = conn_id,
1997 CatalogItem::Log(_)
1998 | CatalogItem::Source(_)
1999 | CatalogItem::Sink(_)
2000 | CatalogItem::MaterializedView(_)
2001 | CatalogItem::Secret(_)
2002 | CatalogItem::Type(_)
2003 | CatalogItem::Func(_)
2004 | CatalogItem::Connection(_) => (),
2005 }
2006 }
2007
2008 pub fn set_create_sql(&mut self, create_sql: String) {
2017 match self {
2018 CatalogItem::View(view) => view.create_sql = create_sql,
2019 CatalogItem::Index(index) => index.create_sql = create_sql,
2020 CatalogItem::Table(table) => table.create_sql = Some(create_sql),
2021 CatalogItem::Log(_)
2022 | CatalogItem::Source(_)
2023 | CatalogItem::Sink(_)
2024 | CatalogItem::MaterializedView(_)
2025 | CatalogItem::Secret(_)
2026 | CatalogItem::Type(_)
2027 | CatalogItem::Func(_)
2028 | CatalogItem::Connection(_) => {
2029 unreachable!("only views, indexes, and tables can be temporary")
2030 }
2031 }
2032 }
2033
2034 pub fn is_temporary(&self) -> bool {
2036 self.conn_id().is_some()
2037 }
2038
2039 pub fn rename_schema_refs(
2040 &self,
2041 database_name: &str,
2042 cur_schema_name: &str,
2043 new_schema_name: &str,
2044 ) -> Result<CatalogItem, (String, String)> {
2045 let do_rewrite = |create_sql: String| -> Result<String, (String, String)> {
2046 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2047 .expect("invalid create sql persisted to catalog")
2048 .into_element()
2049 .ast;
2050
2051 mz_sql::ast::transform::create_stmt_rename_schema_refs(
2053 &mut create_stmt,
2054 database_name,
2055 cur_schema_name,
2056 new_schema_name,
2057 )?;
2058
2059 Ok(create_stmt.to_ast_string_stable())
2060 };
2061
2062 match self {
2063 CatalogItem::Table(i) => {
2064 let mut i = i.clone();
2065 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2066 Ok(CatalogItem::Table(i))
2067 }
2068 CatalogItem::Log(i) => Ok(CatalogItem::Log(i.clone())),
2069 CatalogItem::Source(i) => {
2070 let mut i = i.clone();
2071 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2072 Ok(CatalogItem::Source(i))
2073 }
2074 CatalogItem::Sink(i) => {
2075 let mut i = i.clone();
2076 i.create_sql = do_rewrite(i.create_sql)?;
2077 Ok(CatalogItem::Sink(i))
2078 }
2079 CatalogItem::View(i) => {
2080 let mut i = i.clone();
2081 i.create_sql = do_rewrite(i.create_sql)?;
2082 Ok(CatalogItem::View(i))
2083 }
2084 CatalogItem::MaterializedView(i) => {
2085 let mut i = i.clone();
2086 i.create_sql = do_rewrite(i.create_sql)?;
2087 Ok(CatalogItem::MaterializedView(i))
2088 }
2089 CatalogItem::Index(i) => {
2090 let mut i = i.clone();
2091 i.create_sql = do_rewrite(i.create_sql)?;
2092 Ok(CatalogItem::Index(i))
2093 }
2094 CatalogItem::Secret(i) => {
2095 let mut i = i.clone();
2096 i.create_sql = do_rewrite(i.create_sql)?;
2097 Ok(CatalogItem::Secret(i))
2098 }
2099 CatalogItem::Connection(i) => {
2100 let mut i = i.clone();
2101 i.create_sql = do_rewrite(i.create_sql)?;
2102 Ok(CatalogItem::Connection(i))
2103 }
2104 CatalogItem::Type(i) => {
2105 let mut i = i.clone();
2106 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2107 Ok(CatalogItem::Type(i))
2108 }
2109 CatalogItem::Func(i) => Ok(CatalogItem::Func(i.clone())),
2110 }
2111 }
2112
2113 pub fn rename_item_refs(
2117 &self,
2118 from: FullItemName,
2119 to_item_name: String,
2120 rename_self: bool,
2121 ) -> Result<CatalogItem, String> {
2122 let do_rewrite = |create_sql: String| -> Result<String, String> {
2123 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2124 .expect("invalid create sql persisted to catalog")
2125 .into_element()
2126 .ast;
2127 if rename_self {
2128 mz_sql::ast::transform::create_stmt_rename(&mut create_stmt, to_item_name.clone());
2129 }
2130 mz_sql::ast::transform::create_stmt_rename_refs(&mut create_stmt, from, to_item_name)?;
2132 Ok(create_stmt.to_ast_string_stable())
2133 };
2134
2135 match self {
2136 CatalogItem::Table(i) => {
2137 let mut i = i.clone();
2138 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2139 Ok(CatalogItem::Table(i))
2140 }
2141 CatalogItem::Log(i) => Ok(CatalogItem::Log(i.clone())),
2142 CatalogItem::Source(i) => {
2143 let mut i = i.clone();
2144 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2145 Ok(CatalogItem::Source(i))
2146 }
2147 CatalogItem::Sink(i) => {
2148 let mut i = i.clone();
2149 i.create_sql = do_rewrite(i.create_sql)?;
2150 Ok(CatalogItem::Sink(i))
2151 }
2152 CatalogItem::View(i) => {
2153 let mut i = i.clone();
2154 i.create_sql = do_rewrite(i.create_sql)?;
2155 Ok(CatalogItem::View(i))
2156 }
2157 CatalogItem::MaterializedView(i) => {
2158 let mut i = i.clone();
2159 i.create_sql = do_rewrite(i.create_sql)?;
2160 Ok(CatalogItem::MaterializedView(i))
2161 }
2162 CatalogItem::Index(i) => {
2163 let mut i = i.clone();
2164 i.create_sql = do_rewrite(i.create_sql)?;
2165 Ok(CatalogItem::Index(i))
2166 }
2167 CatalogItem::Secret(i) => {
2168 let mut i = i.clone();
2169 i.create_sql = do_rewrite(i.create_sql)?;
2170 Ok(CatalogItem::Secret(i))
2171 }
2172 CatalogItem::Func(_) | CatalogItem::Type(_) => {
2173 unreachable!("{}s cannot be renamed", self.typ())
2174 }
2175 CatalogItem::Connection(i) => {
2176 let mut i = i.clone();
2177 i.create_sql = do_rewrite(i.create_sql)?;
2178 Ok(CatalogItem::Connection(i))
2179 }
2180 }
2181 }
2182
2183 pub fn replace_item_refs(&self, old_id: CatalogItemId, new_id: CatalogItemId) -> CatalogItem {
2185 let do_rewrite = |create_sql: String| -> String {
2186 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2187 .expect("invalid create sql persisted to catalog")
2188 .into_element()
2189 .ast;
2190 mz_sql::ast::transform::create_stmt_replace_ids(
2191 &mut create_stmt,
2192 &[(old_id, new_id)].into(),
2193 );
2194 create_stmt.to_ast_string_stable()
2195 };
2196
2197 match self {
2198 CatalogItem::Table(i) => {
2199 let mut i = i.clone();
2200 i.create_sql = i.create_sql.map(do_rewrite);
2201 CatalogItem::Table(i)
2202 }
2203 CatalogItem::Log(i) => CatalogItem::Log(i.clone()),
2204 CatalogItem::Source(i) => {
2205 let mut i = i.clone();
2206 i.create_sql = i.create_sql.map(do_rewrite);
2207 CatalogItem::Source(i)
2208 }
2209 CatalogItem::Sink(i) => {
2210 let mut i = i.clone();
2211 i.create_sql = do_rewrite(i.create_sql);
2212 CatalogItem::Sink(i)
2213 }
2214 CatalogItem::View(i) => {
2215 let mut i = i.clone();
2216 i.create_sql = do_rewrite(i.create_sql);
2217 CatalogItem::View(i)
2218 }
2219 CatalogItem::MaterializedView(i) => {
2220 let mut i = i.clone();
2221 i.create_sql = do_rewrite(i.create_sql);
2222 CatalogItem::MaterializedView(i)
2223 }
2224 CatalogItem::Index(i) => {
2225 let mut i = i.clone();
2226 i.create_sql = do_rewrite(i.create_sql);
2227 CatalogItem::Index(i)
2228 }
2229 CatalogItem::Secret(i) => {
2230 let mut i = i.clone();
2231 i.create_sql = do_rewrite(i.create_sql);
2232 CatalogItem::Secret(i)
2233 }
2234 CatalogItem::Func(_) | CatalogItem::Type(_) => {
2235 unreachable!("references of {}s cannot be replaced", self.typ())
2236 }
2237 CatalogItem::Connection(i) => {
2238 let mut i = i.clone();
2239 i.create_sql = do_rewrite(i.create_sql);
2240 CatalogItem::Connection(i)
2241 }
2242 }
2243 }
2244 pub fn update_retain_history(
2247 &mut self,
2248 value: Option<Value>,
2249 window: CompactionWindow,
2250 ) -> Result<Option<WithOptionValue<Raw>>, ()> {
2251 let update = |mut ast: &mut Statement<Raw>| {
2252 macro_rules! update_retain_history {
2254 ( $stmt:ident, $opt:ident, $name:ident ) => {{
2255 let pos = $stmt
2257 .with_options
2258 .iter()
2259 .rposition(|o| o.name == mz_sql_parser::ast::$name::RetainHistory);
2261 if let Some(value) = value {
2262 let next = mz_sql_parser::ast::$opt {
2263 name: mz_sql_parser::ast::$name::RetainHistory,
2264 value: Some(WithOptionValue::RetainHistoryFor(value)),
2265 };
2266 if let Some(idx) = pos {
2267 let previous = $stmt.with_options[idx].clone();
2268 $stmt.with_options[idx] = next;
2269 previous.value
2270 } else {
2271 $stmt.with_options.push(next);
2272 None
2273 }
2274 } else {
2275 if let Some(idx) = pos {
2276 $stmt.with_options.swap_remove(idx).value
2277 } else {
2278 None
2279 }
2280 }
2281 }};
2282 }
2283 let previous = match &mut ast {
2284 Statement::CreateTable(stmt) => {
2285 update_retain_history!(stmt, TableOption, TableOptionName)
2286 }
2287 Statement::CreateIndex(stmt) => {
2288 update_retain_history!(stmt, IndexOption, IndexOptionName)
2289 }
2290 Statement::CreateSource(stmt) => {
2291 update_retain_history!(stmt, CreateSourceOption, CreateSourceOptionName)
2292 }
2293 Statement::CreateMaterializedView(stmt) => {
2294 update_retain_history!(stmt, MaterializedViewOption, MaterializedViewOptionName)
2295 }
2296 _ => {
2297 return Err(());
2298 }
2299 };
2300 Ok(previous)
2301 };
2302
2303 let res = self.update_sql(update)?;
2304 let cw = self
2305 .custom_logical_compaction_window_mut()
2306 .expect("item must have compaction window");
2307 *cw = Some(window);
2308 Ok(res)
2309 }
2310
2311 pub fn update_timestamp_interval(
2314 &mut self,
2315 value: Option<Value>,
2316 interval: Duration,
2317 ) -> Result<Option<WithOptionValue<Raw>>, ()> {
2318 let update = |ast: &mut Statement<Raw>| match ast {
2319 Statement::CreateSource(stmt) => {
2320 let pos = stmt.with_options.iter().rposition(|o| {
2321 o.name == mz_sql_parser::ast::CreateSourceOptionName::TimestampInterval
2322 });
2323 let previous = if let Some(value) = value {
2324 let next = mz_sql_parser::ast::CreateSourceOption {
2325 name: mz_sql_parser::ast::CreateSourceOptionName::TimestampInterval,
2326 value: Some(WithOptionValue::Value(value)),
2327 };
2328 if let Some(idx) = pos {
2329 let previous = stmt.with_options[idx].clone();
2330 stmt.with_options[idx] = next;
2331 previous.value
2332 } else {
2333 stmt.with_options.push(next);
2334 None
2335 }
2336 } else if let Some(idx) = pos {
2337 stmt.with_options.swap_remove(idx).value
2338 } else {
2339 None
2340 };
2341 Ok(previous)
2342 }
2343 _ => Err(()),
2344 };
2345
2346 let previous = self.update_sql(update)?;
2347
2348 match self {
2350 CatalogItem::Source(source) => {
2351 match &mut source.data_source {
2352 DataSourceDesc::Ingestion { desc, .. }
2353 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => {
2354 desc.timestamp_interval = interval;
2355 }
2356 _ => return Err(()),
2357 }
2358 Ok(previous)
2359 }
2360 _ => Err(()),
2361 }
2362 }
2363
2364 pub fn add_column(
2365 &mut self,
2366 name: ColumnName,
2367 typ: SqlColumnType,
2368 sql: RawDataType,
2369 ) -> Result<RelationVersion, PlanError> {
2370 let CatalogItem::Table(table) = self else {
2371 return Err(PlanError::Unsupported {
2372 feature: "adding columns to a non-Table".to_string(),
2373 discussion_no: None,
2374 });
2375 };
2376 let next_version = table.desc.add_column(name.clone(), typ);
2377
2378 let update = |mut ast: &mut Statement<Raw>| match &mut ast {
2379 Statement::CreateTable(stmt) => {
2380 let version = ColumnOptionDef {
2381 name: None,
2382 option: ColumnOption::Versioned {
2383 action: ColumnVersioned::Added,
2384 version: next_version.into(),
2385 },
2386 };
2387 let column = ColumnDef {
2388 name: name.into(),
2389 data_type: sql,
2390 collation: None,
2391 options: vec![version],
2392 };
2393 stmt.columns.push(column);
2394 Ok(())
2395 }
2396 _ => Err(()),
2397 };
2398
2399 self.update_sql(update)
2400 .map_err(|()| PlanError::Unstructured("expected CREATE TABLE statement".to_string()))?;
2401 Ok(next_version)
2402 }
2403
2404 pub fn update_sql<F, T>(&mut self, f: F) -> Result<T, ()>
2407 where
2408 F: FnOnce(&mut Statement<Raw>) -> Result<T, ()>,
2409 {
2410 let create_sql = match self {
2411 CatalogItem::Table(Table { create_sql, .. })
2412 | CatalogItem::Type(Type { create_sql, .. })
2413 | CatalogItem::Source(Source { create_sql, .. }) => create_sql.as_mut(),
2414 CatalogItem::Sink(Sink { create_sql, .. })
2415 | CatalogItem::View(View { create_sql, .. })
2416 | CatalogItem::MaterializedView(MaterializedView { create_sql, .. })
2417 | CatalogItem::Index(Index { create_sql, .. })
2418 | CatalogItem::Secret(Secret { create_sql, .. })
2419 | CatalogItem::Connection(Connection { create_sql, .. }) => Some(create_sql),
2420 CatalogItem::Func(_) | CatalogItem::Log(_) => None,
2421 };
2422 let Some(create_sql) = create_sql else {
2423 return Err(());
2424 };
2425 let mut ast = mz_sql_parser::parser::parse_statements(create_sql)
2426 .expect("non-system items must be parseable")
2427 .into_element()
2428 .ast;
2429 debug!("rewrite: {}", ast.to_ast_string_redacted());
2430 let t = f(&mut ast)?;
2431 *create_sql = ast.to_ast_string_stable();
2432 debug!("rewrote: {}", ast.to_ast_string_redacted());
2433 Ok(t)
2434 }
2435
2436 pub fn is_compute_object_on_cluster(&self) -> Option<ClusterId> {
2443 match self {
2444 CatalogItem::Index(index) => Some(index.cluster_id),
2445 CatalogItem::Table(_)
2446 | CatalogItem::Source(_)
2447 | CatalogItem::Log(_)
2448 | CatalogItem::View(_)
2449 | CatalogItem::MaterializedView(_)
2450 | CatalogItem::Sink(_)
2451 | CatalogItem::Type(_)
2452 | CatalogItem::Func(_)
2453 | CatalogItem::Secret(_)
2454 | CatalogItem::Connection(_) => None,
2455 }
2456 }
2457
2458 pub fn is_hydratable(&self) -> bool {
2463 match self {
2464 CatalogItem::Index(_) | CatalogItem::MaterializedView(_) | CatalogItem::Sink(_) => true,
2465 CatalogItem::Source(source) => matches!(
2466 source.data_source,
2467 DataSourceDesc::Ingestion { .. } | DataSourceDesc::OldSyntaxIngestion { .. }
2468 ),
2469 CatalogItem::Table(_)
2470 | CatalogItem::Log(_)
2471 | CatalogItem::View(_)
2472 | CatalogItem::Type(_)
2473 | CatalogItem::Func(_)
2474 | CatalogItem::Secret(_)
2475 | CatalogItem::Connection(_) => false,
2476 }
2477 }
2478
2479 pub fn cluster_id(&self) -> Option<ClusterId> {
2480 match self {
2481 CatalogItem::MaterializedView(mv) => Some(mv.cluster_id),
2482 CatalogItem::Index(index) => Some(index.cluster_id),
2483 CatalogItem::Source(source) => match &source.data_source {
2484 DataSourceDesc::Ingestion { cluster_id, .. }
2485 | DataSourceDesc::OldSyntaxIngestion { cluster_id, .. } => Some(*cluster_id),
2486 DataSourceDesc::IngestionExport { .. } => None,
2490 DataSourceDesc::Webhook { cluster_id, .. } => Some(*cluster_id),
2491 DataSourceDesc::Introspection(_)
2492 | DataSourceDesc::Progress
2493 | DataSourceDesc::Catalog => None,
2494 },
2495 CatalogItem::Sink(sink) => Some(sink.cluster_id),
2496 CatalogItem::Table(_)
2497 | CatalogItem::Log(_)
2498 | CatalogItem::View(_)
2499 | CatalogItem::Type(_)
2500 | CatalogItem::Func(_)
2501 | CatalogItem::Secret(_)
2502 | CatalogItem::Connection(_) => None,
2503 }
2504 }
2505
2506 pub fn custom_logical_compaction_window(&self) -> Option<CompactionWindow> {
2509 match self {
2510 CatalogItem::Table(table) => table.custom_logical_compaction_window,
2511 CatalogItem::Source(source) => source.custom_logical_compaction_window,
2512 CatalogItem::Index(index) => index.custom_logical_compaction_window,
2513 CatalogItem::MaterializedView(mview) => mview.custom_logical_compaction_window,
2514 CatalogItem::Log(_)
2515 | CatalogItem::View(_)
2516 | CatalogItem::Sink(_)
2517 | CatalogItem::Type(_)
2518 | CatalogItem::Func(_)
2519 | CatalogItem::Secret(_)
2520 | CatalogItem::Connection(_) => None,
2521 }
2522 }
2523
2524 pub fn custom_logical_compaction_window_mut(
2528 &mut self,
2529 ) -> Option<&mut Option<CompactionWindow>> {
2530 let cw = match self {
2531 CatalogItem::Table(table) => &mut table.custom_logical_compaction_window,
2532 CatalogItem::Source(source) => &mut source.custom_logical_compaction_window,
2533 CatalogItem::Index(index) => &mut index.custom_logical_compaction_window,
2534 CatalogItem::MaterializedView(mview) => &mut mview.custom_logical_compaction_window,
2535 CatalogItem::Log(_)
2536 | CatalogItem::View(_)
2537 | CatalogItem::Sink(_)
2538 | CatalogItem::Type(_)
2539 | CatalogItem::Func(_)
2540 | CatalogItem::Secret(_)
2541 | CatalogItem::Connection(_) => return None,
2542 };
2543 Some(cw)
2544 }
2545
2546 pub fn initial_logical_compaction_window(&self) -> Option<CompactionWindow> {
2554 let custom_logical_compaction_window = match self {
2555 CatalogItem::Table(_)
2556 | CatalogItem::Source(_)
2557 | CatalogItem::Index(_)
2558 | CatalogItem::MaterializedView(_) => self.custom_logical_compaction_window(),
2559 CatalogItem::Log(_)
2560 | CatalogItem::View(_)
2561 | CatalogItem::Sink(_)
2562 | CatalogItem::Type(_)
2563 | CatalogItem::Func(_)
2564 | CatalogItem::Secret(_)
2565 | CatalogItem::Connection(_) => return None,
2566 };
2567 Some(custom_logical_compaction_window.unwrap_or(CompactionWindow::Default))
2568 }
2569
2570 pub fn is_retained_metrics_object(&self) -> bool {
2574 match self {
2575 CatalogItem::Table(table) => table.is_retained_metrics_object,
2576 CatalogItem::Source(source) => source.is_retained_metrics_object,
2577 CatalogItem::Index(index) => index.is_retained_metrics_object,
2578 CatalogItem::Log(_)
2579 | CatalogItem::View(_)
2580 | CatalogItem::MaterializedView(_)
2581 | CatalogItem::Sink(_)
2582 | CatalogItem::Type(_)
2583 | CatalogItem::Func(_)
2584 | CatalogItem::Secret(_)
2585 | CatalogItem::Connection(_) => false,
2586 }
2587 }
2588
2589 pub fn to_serialized(&self) -> (String, GlobalId, BTreeMap<RelationVersion, GlobalId>) {
2590 match self {
2591 CatalogItem::Table(table) => {
2592 let create_sql = table
2593 .create_sql
2594 .clone()
2595 .expect("builtin tables cannot be serialized");
2596 let mut collections = table.collections.clone();
2597 let global_id = collections
2598 .remove(&RelationVersion::root())
2599 .expect("at least one version");
2600 (create_sql, global_id, collections)
2601 }
2602 CatalogItem::Log(_) => unreachable!("builtin logs cannot be serialized"),
2603 CatalogItem::Source(source) => {
2604 assert!(
2605 !matches!(source.data_source, DataSourceDesc::Introspection(_)),
2606 "cannot serialize introspection/builtin sources",
2607 );
2608 let create_sql = source
2609 .create_sql
2610 .clone()
2611 .expect("builtin sources cannot be serialized");
2612 (create_sql, source.global_id, BTreeMap::new())
2613 }
2614 CatalogItem::View(view) => (view.create_sql.clone(), view.global_id, BTreeMap::new()),
2615 CatalogItem::MaterializedView(mview) => {
2616 let mut collections = mview.collections.clone();
2617 let global_id = collections
2618 .remove(&RelationVersion::root())
2619 .expect("at least one version");
2620 (mview.create_sql.clone(), global_id, collections)
2621 }
2622 CatalogItem::Index(index) => {
2623 (index.create_sql.clone(), index.global_id, BTreeMap::new())
2624 }
2625 CatalogItem::Sink(sink) => (sink.create_sql.clone(), sink.global_id, BTreeMap::new()),
2626 CatalogItem::Type(typ) => {
2627 let create_sql = typ
2628 .create_sql
2629 .clone()
2630 .expect("builtin types cannot be serialized");
2631 (create_sql, typ.global_id, BTreeMap::new())
2632 }
2633 CatalogItem::Secret(secret) => {
2634 (secret.create_sql.clone(), secret.global_id, BTreeMap::new())
2635 }
2636 CatalogItem::Connection(connection) => (
2637 connection.create_sql.clone(),
2638 connection.global_id,
2639 BTreeMap::new(),
2640 ),
2641 CatalogItem::Func(_) => unreachable!("cannot serialize functions yet"),
2642 }
2643 }
2644
2645 pub fn into_serialized(self) -> (String, GlobalId, BTreeMap<RelationVersion, GlobalId>) {
2646 match self {
2647 CatalogItem::Table(mut table) => {
2648 let create_sql = table
2649 .create_sql
2650 .expect("builtin tables cannot be serialized");
2651 let global_id = table
2652 .collections
2653 .remove(&RelationVersion::root())
2654 .expect("at least one version");
2655 (create_sql, global_id, table.collections)
2656 }
2657 CatalogItem::Log(_) => unreachable!("builtin logs cannot be serialized"),
2658 CatalogItem::Source(source) => {
2659 assert!(
2660 !matches!(source.data_source, DataSourceDesc::Introspection(_)),
2661 "cannot serialize introspection/builtin sources",
2662 );
2663 let create_sql = source
2664 .create_sql
2665 .expect("builtin sources cannot be serialized");
2666 (create_sql, source.global_id, BTreeMap::new())
2667 }
2668 CatalogItem::View(view) => (view.create_sql, view.global_id, BTreeMap::new()),
2669 CatalogItem::MaterializedView(mut mview) => {
2670 let global_id = mview
2671 .collections
2672 .remove(&RelationVersion::root())
2673 .expect("at least one version");
2674 (mview.create_sql, global_id, mview.collections)
2675 }
2676 CatalogItem::Index(index) => (index.create_sql, index.global_id, BTreeMap::new()),
2677 CatalogItem::Sink(sink) => (sink.create_sql, sink.global_id, BTreeMap::new()),
2678 CatalogItem::Type(typ) => {
2679 let create_sql = typ.create_sql.expect("builtin types cannot be serialized");
2680 (create_sql, typ.global_id, BTreeMap::new())
2681 }
2682 CatalogItem::Secret(secret) => (secret.create_sql, secret.global_id, BTreeMap::new()),
2683 CatalogItem::Connection(connection) => {
2684 (connection.create_sql, connection.global_id, BTreeMap::new())
2685 }
2686 CatalogItem::Func(_) => unreachable!("cannot serialize functions yet"),
2687 }
2688 }
2689
2690 pub fn global_id_for_version(&self, version: RelationVersionSelector) -> Option<GlobalId> {
2693 let collections = match self {
2694 CatalogItem::MaterializedView(mv) => &mv.collections,
2695 CatalogItem::Table(table) => &table.collections,
2696 CatalogItem::Source(source) => return Some(source.global_id),
2697 CatalogItem::Log(log) => return Some(log.global_id),
2698 CatalogItem::View(view) => return Some(view.global_id),
2699 CatalogItem::Sink(sink) => return Some(sink.global_id),
2700 CatalogItem::Index(index) => return Some(index.global_id),
2701 CatalogItem::Type(ty) => return Some(ty.global_id),
2702 CatalogItem::Func(func) => return Some(func.global_id),
2703 CatalogItem::Secret(secret) => return Some(secret.global_id),
2704 CatalogItem::Connection(conn) => return Some(conn.global_id),
2705 };
2706 match version {
2707 RelationVersionSelector::Latest => collections.values().last().copied(),
2708 RelationVersionSelector::Specific(version) => collections.get(&version).copied(),
2709 }
2710 }
2711}
2712
2713impl CatalogEntry {
2714 pub fn relation_desc_latest(&self) -> Option<Cow<'_, RelationDesc>> {
2717 self.item.relation_desc(RelationVersionSelector::Latest)
2718 }
2719
2720 pub fn has_columns(&self) -> bool {
2722 match self.item() {
2723 CatalogItem::Type(Type { details, .. }) => {
2724 matches!(details.typ, CatalogType::Record { .. })
2725 }
2726 _ => self.relation_desc_latest().is_some(),
2727 }
2728 }
2729
2730 pub fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
2732 self.item.func(self)
2733 }
2734
2735 pub fn index(&self) -> Option<&Index> {
2737 match self.item() {
2738 CatalogItem::Index(idx) => Some(idx),
2739 _ => None,
2740 }
2741 }
2742
2743 pub fn materialized_view(&self) -> Option<&MaterializedView> {
2745 match self.item() {
2746 CatalogItem::MaterializedView(mv) => Some(mv),
2747 _ => None,
2748 }
2749 }
2750
2751 pub fn table(&self) -> Option<&Table> {
2753 match self.item() {
2754 CatalogItem::Table(tbl) => Some(tbl),
2755 _ => None,
2756 }
2757 }
2758
2759 pub fn source(&self) -> Option<&Source> {
2761 match self.item() {
2762 CatalogItem::Source(src) => Some(src),
2763 _ => None,
2764 }
2765 }
2766
2767 pub fn sink(&self) -> Option<&Sink> {
2769 match self.item() {
2770 CatalogItem::Sink(sink) => Some(sink),
2771 _ => None,
2772 }
2773 }
2774
2775 pub fn secret(&self) -> Option<&Secret> {
2777 match self.item() {
2778 CatalogItem::Secret(secret) => Some(secret),
2779 _ => None,
2780 }
2781 }
2782
2783 pub fn connection(&self) -> Result<&Connection, SqlCatalogError> {
2784 match self.item() {
2785 CatalogItem::Connection(connection) => Ok(connection),
2786 _ => {
2787 let db_name = match self.name().qualifiers.database_spec {
2788 ResolvedDatabaseSpecifier::Ambient => "".to_string(),
2789 ResolvedDatabaseSpecifier::Id(id) => format!("{id}."),
2790 };
2791 Err(SqlCatalogError::UnknownConnection(format!(
2792 "{}{}.{}",
2793 db_name,
2794 self.name().qualifiers.schema_spec,
2795 self.name().item
2796 )))
2797 }
2798 }
2799 }
2800
2801 pub fn source_desc(
2804 &self,
2805 ) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
2806 self.item.source_desc(self)
2807 }
2808
2809 pub fn is_connection(&self) -> bool {
2811 matches!(self.item(), CatalogItem::Connection(_))
2812 }
2813
2814 pub fn is_table(&self) -> bool {
2816 matches!(self.item(), CatalogItem::Table(_))
2817 }
2818
2819 pub fn is_source(&self) -> bool {
2822 matches!(self.item(), CatalogItem::Source(_))
2823 }
2824
2825 pub fn subsource_details(
2828 &self,
2829 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
2830 match &self.item() {
2831 CatalogItem::Source(source) => match &source.data_source {
2832 DataSourceDesc::IngestionExport {
2833 ingestion_id,
2834 external_reference,
2835 details,
2836 data_config: _,
2837 } => Some((*ingestion_id, external_reference, details)),
2838 _ => None,
2839 },
2840 _ => None,
2841 }
2842 }
2843
2844 pub fn source_export_details(
2847 &self,
2848 ) -> Option<(
2849 CatalogItemId,
2850 &UnresolvedItemName,
2851 &SourceExportDetails,
2852 &SourceExportDataConfig<ReferencedConnection>,
2853 )> {
2854 match &self.item() {
2855 CatalogItem::Source(source) => match &source.data_source {
2856 DataSourceDesc::IngestionExport {
2857 ingestion_id,
2858 external_reference,
2859 details,
2860 data_config,
2861 } => Some((*ingestion_id, external_reference, details, data_config)),
2862 _ => None,
2863 },
2864 CatalogItem::Table(table) => match &table.data_source {
2865 TableDataSource::DataSource {
2866 desc:
2867 DataSourceDesc::IngestionExport {
2868 ingestion_id,
2869 external_reference,
2870 details,
2871 data_config,
2872 },
2873 timeline: _,
2874 } => Some((*ingestion_id, external_reference, details, data_config)),
2875 _ => None,
2876 },
2877 _ => None,
2878 }
2879 }
2880
2881 pub fn is_progress_source(&self) -> bool {
2883 self.item().is_progress_source()
2884 }
2885
2886 pub fn progress_id(&self) -> Option<CatalogItemId> {
2888 match &self.item() {
2889 CatalogItem::Source(source) => match &source.data_source {
2890 DataSourceDesc::Ingestion { .. } => Some(self.id),
2891 DataSourceDesc::OldSyntaxIngestion {
2892 progress_subsource, ..
2893 } => Some(*progress_subsource),
2894 DataSourceDesc::IngestionExport { .. }
2895 | DataSourceDesc::Introspection(_)
2896 | DataSourceDesc::Progress
2897 | DataSourceDesc::Webhook { .. }
2898 | DataSourceDesc::Catalog => None,
2899 },
2900 CatalogItem::Table(_)
2901 | CatalogItem::Log(_)
2902 | CatalogItem::View(_)
2903 | CatalogItem::MaterializedView(_)
2904 | CatalogItem::Sink(_)
2905 | CatalogItem::Index(_)
2906 | CatalogItem::Type(_)
2907 | CatalogItem::Func(_)
2908 | CatalogItem::Secret(_)
2909 | CatalogItem::Connection(_) => None,
2910 }
2911 }
2912
2913 pub fn is_sink(&self) -> bool {
2915 matches!(self.item(), CatalogItem::Sink(_))
2916 }
2917
2918 pub fn is_materialized_view(&self) -> bool {
2920 matches!(self.item(), CatalogItem::MaterializedView(_))
2921 }
2922
2923 pub fn is_view(&self) -> bool {
2925 matches!(self.item(), CatalogItem::View(_))
2926 }
2927
2928 pub fn is_secret(&self) -> bool {
2930 matches!(self.item(), CatalogItem::Secret(_))
2931 }
2932
2933 pub fn is_introspection_source(&self) -> bool {
2935 matches!(self.item(), CatalogItem::Log(_))
2936 }
2937
2938 pub fn is_index(&self) -> bool {
2940 matches!(self.item(), CatalogItem::Index(_))
2941 }
2942
2943 pub fn is_relation(&self) -> bool {
2945 mz_sql::catalog::ObjectType::from(self.item_type()).is_relation()
2946 }
2947
2948 pub fn references(&self) -> &ResolvedIds {
2951 self.item.references()
2952 }
2953
2954 pub fn uses(&self) -> BTreeSet<CatalogItemId> {
2960 self.item.uses()
2961 }
2962
2963 pub fn item(&self) -> &CatalogItem {
2965 &self.item
2966 }
2967
2968 pub fn item_mut(&mut self) -> &mut CatalogItem {
2971 &mut self.item
2972 }
2973
2974 pub fn id(&self) -> CatalogItemId {
2976 self.id
2977 }
2978
2979 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
2981 self.item().global_ids()
2982 }
2983
2984 pub fn latest_global_id(&self) -> GlobalId {
2985 self.item().latest_global_id()
2986 }
2987
2988 pub fn oid(&self) -> u32 {
2990 self.oid
2991 }
2992
2993 pub fn name(&self) -> &QualifiedItemName {
2995 &self.name
2996 }
2997
2998 pub fn referenced_by(&self) -> &[CatalogItemId] {
3000 &self.referenced_by
3001 }
3002
3003 pub fn used_by(&self) -> &[CatalogItemId] {
3005 &self.used_by
3006 }
3007
3008 pub fn conn_id(&self) -> Option<&ConnectionId> {
3011 self.item.conn_id()
3012 }
3013
3014 pub fn owner_id(&self) -> &RoleId {
3016 &self.owner_id
3017 }
3018
3019 pub fn privileges(&self) -> &PrivilegeMap {
3021 &self.privileges
3022 }
3023
3024 pub fn comment_object_id(&self) -> CommentObjectId {
3026 use CatalogItemType::*;
3027 match self.item_type() {
3028 Table => CommentObjectId::Table(self.id),
3029 Source => CommentObjectId::Source(self.id),
3030 Sink => CommentObjectId::Sink(self.id),
3031 View => CommentObjectId::View(self.id),
3032 MaterializedView => CommentObjectId::MaterializedView(self.id),
3033 Index => CommentObjectId::Index(self.id),
3034 Func => CommentObjectId::Func(self.id),
3035 Connection => CommentObjectId::Connection(self.id),
3036 Type => CommentObjectId::Type(self.id),
3037 Secret => CommentObjectId::Secret(self.id),
3038 }
3039 }
3040}
3041
3042#[derive(Debug, Clone, Default)]
3043pub struct CommentsMap {
3044 map: BTreeMap<CommentObjectId, BTreeMap<Option<usize>, String>>,
3045}
3046
3047impl CommentsMap {
3048 pub fn update_comment(
3049 &mut self,
3050 object_id: CommentObjectId,
3051 sub_component: Option<usize>,
3052 comment: Option<String>,
3053 ) -> Option<String> {
3054 let object_comments = self.map.entry(object_id).or_default();
3055
3056 let (empty, prev) = if let Some(comment) = comment {
3058 let prev = object_comments.insert(sub_component, comment);
3059 (false, prev)
3060 } else {
3061 let prev = object_comments.remove(&sub_component);
3062 (object_comments.is_empty(), prev)
3063 };
3064
3065 if empty {
3067 self.map.remove(&object_id);
3068 }
3069
3070 prev
3072 }
3073
3074 pub fn drop_comments(
3080 &mut self,
3081 object_ids: &BTreeSet<CommentObjectId>,
3082 ) -> Vec<(CommentObjectId, Option<usize>, String)> {
3083 let mut removed_comments = Vec::new();
3084
3085 for object_id in object_ids {
3086 if let Some(comments) = self.map.remove(object_id) {
3087 let removed = comments
3088 .into_iter()
3089 .map(|(sub_comp, comment)| (object_id.clone(), sub_comp, comment));
3090 removed_comments.extend(removed);
3091 }
3092 }
3093
3094 removed_comments
3095 }
3096
3097 pub fn iter(&self) -> impl Iterator<Item = (CommentObjectId, Option<usize>, &str)> {
3098 self.map
3099 .iter()
3100 .map(|(id, comments)| {
3101 comments
3102 .iter()
3103 .map(|(pos, comment)| (*id, *pos, comment.as_str()))
3104 })
3105 .flatten()
3106 }
3107
3108 pub fn get_object_comments(
3109 &self,
3110 object_id: CommentObjectId,
3111 ) -> Option<&BTreeMap<Option<usize>, String>> {
3112 self.map.get(&object_id)
3113 }
3114}
3115
3116impl Serialize for CommentsMap {
3117 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3118 where
3119 S: serde::Serializer,
3120 {
3121 let comment_count = self
3122 .map
3123 .iter()
3124 .map(|(_object_id, comments)| comments.len())
3125 .sum();
3126
3127 let mut seq = serializer.serialize_seq(Some(comment_count))?;
3128 for (object_id, sub) in &self.map {
3129 for (sub_component, comment) in sub {
3130 seq.serialize_element(&(
3131 format!("{object_id:?}"),
3132 format!("{sub_component:?}"),
3133 comment,
3134 ))?;
3135 }
3136 }
3137 seq.end()
3138 }
3139}
3140
3141#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Default)]
3142pub struct DefaultPrivileges {
3143 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
3144 privileges: BTreeMap<DefaultPrivilegeObject, RoleDefaultPrivileges>,
3145}
3146
3147#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Default)]
3150struct RoleDefaultPrivileges(
3151 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
3153 BTreeMap<RoleId, DefaultPrivilegeAclItem>,
3154);
3155
3156impl Deref for RoleDefaultPrivileges {
3157 type Target = BTreeMap<RoleId, DefaultPrivilegeAclItem>;
3158
3159 fn deref(&self) -> &Self::Target {
3160 &self.0
3161 }
3162}
3163
3164impl DerefMut for RoleDefaultPrivileges {
3165 fn deref_mut(&mut self) -> &mut Self::Target {
3166 &mut self.0
3167 }
3168}
3169
3170impl DefaultPrivileges {
3171 pub fn grant(&mut self, object: DefaultPrivilegeObject, privilege: DefaultPrivilegeAclItem) {
3173 if privilege.acl_mode.is_empty() {
3174 return;
3175 }
3176
3177 let privileges = self.privileges.entry(object).or_default();
3178 if let Some(default_privilege) = privileges.get_mut(&privilege.grantee) {
3179 default_privilege.acl_mode |= privilege.acl_mode;
3180 } else {
3181 privileges.insert(privilege.grantee, privilege);
3182 }
3183 }
3184
3185 pub fn revoke(&mut self, object: &DefaultPrivilegeObject, privilege: &DefaultPrivilegeAclItem) {
3187 if let Some(privileges) = self.privileges.get_mut(object) {
3188 if let Some(default_privilege) = privileges.get_mut(&privilege.grantee) {
3189 default_privilege.acl_mode =
3190 default_privilege.acl_mode.difference(privilege.acl_mode);
3191 if default_privilege.acl_mode.is_empty() {
3192 privileges.remove(&privilege.grantee);
3193 }
3194 }
3195 if privileges.is_empty() {
3196 self.privileges.remove(object);
3197 }
3198 }
3199 }
3200
3201 pub fn get_privileges_for_grantee(
3204 &self,
3205 object: &DefaultPrivilegeObject,
3206 grantee: &RoleId,
3207 ) -> Option<&AclMode> {
3208 self.privileges
3209 .get(object)
3210 .and_then(|privileges| privileges.get(grantee))
3211 .map(|privilege| &privilege.acl_mode)
3212 }
3213
3214 pub fn get_applicable_privileges(
3216 &self,
3217 role_id: RoleId,
3218 database_id: Option<DatabaseId>,
3219 schema_id: Option<SchemaId>,
3220 object_type: mz_sql::catalog::ObjectType,
3221 ) -> impl Iterator<Item = DefaultPrivilegeAclItem> + '_ {
3222 let privilege_object_type = if object_type.is_relation() {
3226 mz_sql::catalog::ObjectType::Table
3227 } else {
3228 object_type
3229 };
3230 let valid_acl_mode = rbac::all_object_privileges(SystemObjectType::Object(object_type));
3231
3232 [
3236 DefaultPrivilegeObject {
3237 role_id,
3238 database_id,
3239 schema_id,
3240 object_type: privilege_object_type,
3241 },
3242 DefaultPrivilegeObject {
3243 role_id,
3244 database_id,
3245 schema_id: None,
3246 object_type: privilege_object_type,
3247 },
3248 DefaultPrivilegeObject {
3249 role_id,
3250 database_id: None,
3251 schema_id: None,
3252 object_type: privilege_object_type,
3253 },
3254 DefaultPrivilegeObject {
3255 role_id: RoleId::Public,
3256 database_id,
3257 schema_id,
3258 object_type: privilege_object_type,
3259 },
3260 DefaultPrivilegeObject {
3261 role_id: RoleId::Public,
3262 database_id,
3263 schema_id: None,
3264 object_type: privilege_object_type,
3265 },
3266 DefaultPrivilegeObject {
3267 role_id: RoleId::Public,
3268 database_id: None,
3269 schema_id: None,
3270 object_type: privilege_object_type,
3271 },
3272 ]
3273 .into_iter()
3274 .filter_map(|object| self.privileges.get(&object))
3275 .flat_map(|acl_map| acl_map.values())
3276 .fold(
3278 BTreeMap::new(),
3279 |mut accum, DefaultPrivilegeAclItem { grantee, acl_mode }| {
3280 let accum_acl_mode = accum.entry(grantee).or_insert_with(AclMode::empty);
3281 *accum_acl_mode |= *acl_mode;
3282 accum
3283 },
3284 )
3285 .into_iter()
3286 .map(move |(grantee, acl_mode)| (grantee, acl_mode & valid_acl_mode))
3291 .filter(|(_, acl_mode)| !acl_mode.is_empty())
3293 .map(|(grantee, acl_mode)| DefaultPrivilegeAclItem {
3294 grantee: *grantee,
3295 acl_mode,
3296 })
3297 }
3298
3299 pub fn iter(
3300 &self,
3301 ) -> impl Iterator<
3302 Item = (
3303 &DefaultPrivilegeObject,
3304 impl Iterator<Item = &DefaultPrivilegeAclItem>,
3305 ),
3306 > {
3307 self.privileges
3308 .iter()
3309 .map(|(object, acl_map)| (object, acl_map.values()))
3310 }
3311}
3312
3313#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3314pub struct ClusterConfig {
3315 pub variant: ClusterVariant,
3316 pub workload_class: Option<String>,
3317}
3318
3319impl ClusterConfig {
3320 pub fn features(&self) -> Option<&OptimizerFeatureOverrides> {
3321 match &self.variant {
3322 ClusterVariant::Managed(managed) => Some(&managed.optimizer_feature_overrides),
3323 ClusterVariant::Unmanaged => None,
3324 }
3325 }
3326}
3327
3328impl From<ClusterConfig> for durable::ClusterConfig {
3329 fn from(config: ClusterConfig) -> Self {
3330 Self {
3331 variant: config.variant.into(),
3332 workload_class: config.workload_class,
3333 }
3334 }
3335}
3336
3337impl From<durable::ClusterConfig> for ClusterConfig {
3338 fn from(config: durable::ClusterConfig) -> Self {
3339 Self {
3340 variant: config.variant.into(),
3341 workload_class: config.workload_class,
3342 }
3343 }
3344}
3345
3346#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3347pub struct ClusterVariantManaged {
3348 pub size: String,
3349 pub availability_zones: Vec<String>,
3350 pub logging: ReplicaLogging,
3351 pub arrangement_compression: bool,
3353 pub replication_factor: u32,
3354 pub optimizer_feature_overrides: OptimizerFeatureOverrides,
3355 pub schedule: ClusterSchedule,
3356 pub auto_scaling_strategy: Option<AutoScalingStrategy>,
3359 pub reconfiguration: Option<ReconfigurationState>,
3361 pub burst: Option<BurstState>,
3363}
3364
3365#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3367pub struct ManagedReplicaConfigShape<'a> {
3368 pub size: &'a str,
3369 pub availability_zones: &'a [String],
3370 pub logging: &'a ReplicaLogging,
3371 pub arrangement_compression: bool,
3372}
3373
3374impl<'a> ManagedReplicaConfigShape<'a> {
3375 pub fn new(
3377 size: &'a str,
3378 availability_zones: &'a [String],
3379 logging: &'a ReplicaLogging,
3380 arrangement_compression: bool,
3381 ) -> Self {
3382 Self {
3383 size,
3384 availability_zones,
3385 logging,
3386 arrangement_compression,
3387 }
3388 }
3389}
3390
3391impl ClusterVariantManaged {
3392 pub fn replica_config_shape(&self) -> ManagedReplicaConfigShape<'_> {
3394 let ClusterVariantManaged {
3395 size,
3396 availability_zones,
3397 logging,
3398 arrangement_compression,
3399 replication_factor: _,
3400 optimizer_feature_overrides: _,
3401 schedule: _,
3402 auto_scaling_strategy: _,
3403 reconfiguration: _,
3404 burst: _,
3405 } = self;
3406 ManagedReplicaConfigShape::new(size, availability_zones, logging, *arrangement_compression)
3407 }
3408
3409 pub fn realized_reconfiguration_target(&self) -> ReconfigurationTarget {
3411 let ClusterVariantManaged {
3412 size,
3413 availability_zones,
3414 logging,
3415 arrangement_compression,
3416 replication_factor,
3417 optimizer_feature_overrides: _,
3418 schedule: _,
3419 auto_scaling_strategy: _,
3420 reconfiguration: _,
3421 burst: _,
3422 } = self;
3423 ReconfigurationTarget {
3424 size: size.clone(),
3425 replication_factor: *replication_factor,
3426 availability_zones: availability_zones.clone(),
3427 logging: logging.clone(),
3428 arrangement_compression: *arrangement_compression,
3429 }
3430 }
3431
3432 pub fn has_unwarranted_burst_record(&self) -> bool {
3437 let Some(record) = &self.burst else {
3438 return false;
3439 };
3440 let hydration_size = self
3441 .auto_scaling_strategy
3442 .as_ref()
3443 .and_then(|strategy| strategy.on_hydration.as_ref())
3444 .map(|policy| policy.hydration_size.as_str());
3445 !mz_adapter_types::cluster_state::burst_record_warranted(
3446 &record.burst_size,
3447 self.replication_factor,
3448 hydration_size,
3449 )
3450 }
3451}
3452
3453impl From<ClusterVariantManaged> for durable::ClusterVariantManaged {
3454 fn from(managed: ClusterVariantManaged) -> Self {
3455 let ClusterVariantManaged {
3458 size,
3459 availability_zones,
3460 logging,
3461 arrangement_compression,
3462 replication_factor,
3463 optimizer_feature_overrides,
3464 schedule,
3465 auto_scaling_strategy,
3466 reconfiguration,
3467 burst,
3468 } = managed;
3469 Self {
3470 size,
3471 availability_zones,
3472 logging,
3473 arrangement_compression,
3474 replication_factor,
3475 optimizer_feature_overrides: optimizer_feature_overrides.into(),
3476 schedule,
3477 auto_scaling_strategy,
3478 reconfiguration: reconfiguration.map(Into::into),
3479 burst: burst.map(Into::into),
3480 }
3481 }
3482}
3483
3484impl From<durable::ClusterVariantManaged> for ClusterVariantManaged {
3485 fn from(managed: durable::ClusterVariantManaged) -> Self {
3486 let durable::ClusterVariantManaged {
3489 size,
3490 availability_zones,
3491 logging,
3492 arrangement_compression,
3493 replication_factor,
3494 optimizer_feature_overrides,
3495 schedule,
3496 auto_scaling_strategy,
3497 reconfiguration,
3498 burst,
3499 } = managed;
3500 Self {
3501 size,
3502 availability_zones,
3503 logging,
3504 arrangement_compression,
3505 replication_factor,
3506 optimizer_feature_overrides: optimizer_feature_overrides.into(),
3507 schedule,
3508 auto_scaling_strategy,
3509 reconfiguration: reconfiguration.map(Into::into),
3510 burst: burst.map(Into::into),
3511 }
3512 }
3513}
3514
3515#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3522pub struct ReconfigurationState {
3523 pub target: ReconfigurationTarget,
3524 pub deadline: Timestamp,
3525 pub on_timeout: OnTimeoutAction,
3526 pub status: ReconfigurationStatus,
3527}
3528
3529#[derive(
3548 Clone,
3549 Copy,
3550 Debug,
3551 Deserialize,
3552 Serialize,
3553 PartialOrd,
3554 PartialEq,
3555 Eq,
3556 Ord
3557)]
3558pub enum ReconfigurationStatus {
3559 InProgress,
3560 Finalized,
3561 TimedOut,
3562 Cancelled,
3563 ResourceExhausted,
3564}
3565
3566impl From<ReconfigurationStatus> for durable::ReconfigurationStatus {
3567 fn from(status: ReconfigurationStatus) -> Self {
3568 match status {
3569 ReconfigurationStatus::InProgress => durable::ReconfigurationStatus::InProgress,
3570 ReconfigurationStatus::Finalized => durable::ReconfigurationStatus::Finalized,
3571 ReconfigurationStatus::TimedOut => durable::ReconfigurationStatus::TimedOut,
3572 ReconfigurationStatus::Cancelled => durable::ReconfigurationStatus::Cancelled,
3573 ReconfigurationStatus::ResourceExhausted => {
3574 durable::ReconfigurationStatus::ResourceExhausted
3575 }
3576 }
3577 }
3578}
3579
3580impl From<durable::ReconfigurationStatus> for ReconfigurationStatus {
3581 fn from(status: durable::ReconfigurationStatus) -> Self {
3582 match status {
3583 durable::ReconfigurationStatus::InProgress => ReconfigurationStatus::InProgress,
3584 durable::ReconfigurationStatus::Finalized => ReconfigurationStatus::Finalized,
3585 durable::ReconfigurationStatus::TimedOut => ReconfigurationStatus::TimedOut,
3586 durable::ReconfigurationStatus::Cancelled => ReconfigurationStatus::Cancelled,
3587 durable::ReconfigurationStatus::ResourceExhausted => {
3588 ReconfigurationStatus::ResourceExhausted
3589 }
3590 }
3591 }
3592}
3593
3594impl ReconfigurationState {
3595 pub fn is_in_progress(&self) -> bool {
3596 matches!(self.status, ReconfigurationStatus::InProgress)
3597 }
3598}
3599
3600impl From<ReconfigurationState> for durable::ReconfigurationState {
3601 fn from(state: ReconfigurationState) -> Self {
3602 let ReconfigurationState {
3605 target,
3606 deadline,
3607 on_timeout,
3608 status,
3609 } = state;
3610 Self {
3611 target: target.into(),
3612 deadline,
3613 on_timeout,
3614 status: status.into(),
3615 }
3616 }
3617}
3618
3619impl From<durable::ReconfigurationState> for ReconfigurationState {
3620 fn from(state: durable::ReconfigurationState) -> Self {
3621 let durable::ReconfigurationState {
3624 target,
3625 deadline,
3626 on_timeout,
3627 status,
3628 } = state;
3629 Self {
3630 target: target.into(),
3631 deadline,
3632 on_timeout,
3633 status: status.into(),
3634 }
3635 }
3636}
3637
3638#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3640pub struct ReconfigurationTarget {
3641 pub size: String,
3642 pub replication_factor: u32,
3643 pub availability_zones: Vec<String>,
3644 pub logging: ReplicaLogging,
3645 pub arrangement_compression: bool,
3646}
3647
3648impl ReconfigurationTarget {
3649 pub fn matches_realized_config(&self, managed: &ClusterVariantManaged) -> bool {
3651 self == &managed.realized_reconfiguration_target()
3652 }
3653}
3654
3655impl From<ReconfigurationTarget> for durable::ReconfigurationTarget {
3656 fn from(target: ReconfigurationTarget) -> Self {
3657 let ReconfigurationTarget {
3660 size,
3661 replication_factor,
3662 availability_zones,
3663 logging,
3664 arrangement_compression,
3665 } = target;
3666 Self {
3667 size,
3668 replication_factor,
3669 availability_zones,
3670 logging,
3671 arrangement_compression,
3672 }
3673 }
3674}
3675
3676impl From<durable::ReconfigurationTarget> for ReconfigurationTarget {
3677 fn from(target: durable::ReconfigurationTarget) -> Self {
3678 let durable::ReconfigurationTarget {
3681 size,
3682 replication_factor,
3683 availability_zones,
3684 logging,
3685 arrangement_compression,
3686 } = target;
3687 Self {
3688 size,
3689 replication_factor,
3690 availability_zones,
3691 logging,
3692 arrangement_compression,
3693 }
3694 }
3695}
3696
3697#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3699pub struct BurstState {
3700 pub burst_size: String,
3701 pub linger_duration: Duration,
3702 pub steady_hydrated_at: Option<Timestamp>,
3703}
3704
3705impl From<BurstState> for durable::BurstState {
3706 fn from(burst: BurstState) -> Self {
3707 let BurstState {
3710 burst_size,
3711 linger_duration,
3712 steady_hydrated_at,
3713 } = burst;
3714 Self {
3715 burst_size,
3716 linger_duration,
3717 steady_hydrated_at,
3718 }
3719 }
3720}
3721
3722impl From<durable::BurstState> for BurstState {
3723 fn from(burst: durable::BurstState) -> Self {
3724 let durable::BurstState {
3727 burst_size,
3728 linger_duration,
3729 steady_hydrated_at,
3730 } = burst;
3731 Self {
3732 burst_size,
3733 linger_duration,
3734 steady_hydrated_at,
3735 }
3736 }
3737}
3738
3739#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3740pub enum ClusterVariant {
3741 Managed(ClusterVariantManaged),
3742 Unmanaged,
3743}
3744
3745impl From<ClusterVariant> for durable::ClusterVariant {
3746 fn from(variant: ClusterVariant) -> Self {
3747 match variant {
3748 ClusterVariant::Managed(managed) => Self::Managed(managed.into()),
3749 ClusterVariant::Unmanaged => Self::Unmanaged,
3750 }
3751 }
3752}
3753
3754impl From<durable::ClusterVariant> for ClusterVariant {
3755 fn from(variant: durable::ClusterVariant) -> Self {
3756 match variant {
3757 durable::ClusterVariant::Managed(managed) => Self::Managed(managed.into()),
3758 durable::ClusterVariant::Unmanaged => Self::Unmanaged,
3759 }
3760 }
3761}
3762
3763impl mz_sql::catalog::CatalogDatabase for Database {
3764 fn name(&self) -> &str {
3765 &self.name
3766 }
3767
3768 fn id(&self) -> DatabaseId {
3769 self.id
3770 }
3771
3772 fn has_schemas(&self) -> bool {
3773 !self.schemas_by_name.is_empty()
3774 }
3775
3776 fn schema_ids(&self) -> &BTreeMap<String, SchemaId> {
3777 &self.schemas_by_name
3778 }
3779
3780 #[allow(clippy::as_conversions)]
3782 fn schemas(&self) -> Vec<&dyn CatalogSchema> {
3783 self.schemas_by_id
3784 .values()
3785 .map(|schema| schema as &dyn CatalogSchema)
3786 .collect()
3787 }
3788
3789 fn owner_id(&self) -> RoleId {
3790 self.owner_id
3791 }
3792
3793 fn privileges(&self) -> &PrivilegeMap {
3794 &self.privileges
3795 }
3796}
3797
3798impl mz_sql::catalog::CatalogSchema for Schema {
3799 fn database(&self) -> &ResolvedDatabaseSpecifier {
3800 &self.name.database
3801 }
3802
3803 fn name(&self) -> &QualifiedSchemaName {
3804 &self.name
3805 }
3806
3807 fn id(&self) -> &SchemaSpecifier {
3808 &self.id
3809 }
3810
3811 fn has_items(&self) -> bool {
3812 !self.items.is_empty() || !self.types.is_empty() || !self.functions.is_empty()
3816 }
3817
3818 fn item_ids(&self) -> Box<dyn Iterator<Item = CatalogItemId> + '_> {
3819 Box::new(
3820 self.items
3821 .values()
3822 .chain(self.functions.values())
3823 .chain(self.types.values())
3824 .copied(),
3825 )
3826 }
3827
3828 fn owner_id(&self) -> RoleId {
3829 self.owner_id
3830 }
3831
3832 fn privileges(&self) -> &PrivilegeMap {
3833 &self.privileges
3834 }
3835}
3836
3837impl mz_sql::catalog::CatalogRole for Role {
3838 fn name(&self) -> &str {
3839 &self.name
3840 }
3841
3842 fn id(&self) -> RoleId {
3843 self.id
3844 }
3845
3846 fn membership(&self) -> &BTreeMap<RoleId, RoleId> {
3847 &self.membership.map
3848 }
3849
3850 fn attributes(&self) -> &RoleAttributes {
3851 &self.attributes
3852 }
3853
3854 fn vars(&self) -> &BTreeMap<String, OwnedVarInput> {
3855 &self.vars.map
3856 }
3857}
3858
3859impl mz_sql::catalog::CatalogNetworkPolicy for NetworkPolicy {
3860 fn name(&self) -> &str {
3861 &self.name
3862 }
3863
3864 fn id(&self) -> NetworkPolicyId {
3865 self.id
3866 }
3867
3868 fn owner_id(&self) -> RoleId {
3869 self.owner_id
3870 }
3871
3872 fn privileges(&self) -> &PrivilegeMap {
3873 &self.privileges
3874 }
3875}
3876
3877impl mz_sql::catalog::CatalogCluster<'_> for Cluster {
3878 fn name(&self) -> &str {
3879 &self.name
3880 }
3881
3882 fn id(&self) -> ClusterId {
3883 self.id
3884 }
3885
3886 fn bound_objects(&self) -> &BTreeSet<CatalogItemId> {
3887 &self.bound_objects
3888 }
3889
3890 fn replica_ids(&self) -> &BTreeMap<String, ReplicaId> {
3891 &self.replica_id_by_name_
3892 }
3893
3894 #[allow(clippy::as_conversions)]
3896 fn replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>> {
3897 self.replicas()
3898 .map(|replica| replica as &dyn CatalogClusterReplica)
3899 .collect()
3900 }
3901
3902 fn replica(&self, id: ReplicaId) -> &dyn CatalogClusterReplica<'_> {
3903 self.replica(id).expect("catalog out of sync")
3904 }
3905
3906 fn owner_id(&self) -> RoleId {
3907 self.owner_id
3908 }
3909
3910 fn privileges(&self) -> &PrivilegeMap {
3911 &self.privileges
3912 }
3913
3914 fn is_managed(&self) -> bool {
3915 self.is_managed()
3916 }
3917
3918 fn managed_size(&self) -> Option<&str> {
3919 match &self.config.variant {
3920 ClusterVariant::Managed(ClusterVariantManaged { size, .. }) => Some(size),
3921 ClusterVariant::Unmanaged => None,
3922 }
3923 }
3924
3925 fn schedule(&self) -> Option<&ClusterSchedule> {
3926 match &self.config.variant {
3927 ClusterVariant::Managed(ClusterVariantManaged { schedule, .. }) => Some(schedule),
3928 ClusterVariant::Unmanaged => None,
3929 }
3930 }
3931
3932 fn replication_factor(&self) -> Option<u32> {
3933 match &self.config.variant {
3934 ClusterVariant::Managed(ClusterVariantManaged {
3935 replication_factor, ..
3936 }) => Some(*replication_factor),
3937 ClusterVariant::Unmanaged => None,
3938 }
3939 }
3940
3941 fn auto_scaling_strategy(&self) -> Option<&AutoScalingStrategy> {
3942 match &self.config.variant {
3943 ClusterVariant::Managed(ClusterVariantManaged {
3944 auto_scaling_strategy,
3945 ..
3946 }) => auto_scaling_strategy.as_ref(),
3947 ClusterVariant::Unmanaged => None,
3948 }
3949 }
3950 fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError> {
3951 self.try_to_plan()
3952 }
3953}
3954
3955impl mz_sql::catalog::CatalogClusterReplica<'_> for ClusterReplica {
3956 fn name(&self) -> &str {
3957 &self.name
3958 }
3959
3960 fn cluster_id(&self) -> ClusterId {
3961 self.cluster_id
3962 }
3963
3964 fn replica_id(&self) -> ReplicaId {
3965 self.replica_id
3966 }
3967
3968 fn owner_id(&self) -> RoleId {
3969 self.owner_id
3970 }
3971
3972 fn internal(&self) -> bool {
3973 self.config.location.internal()
3974 }
3975}
3976
3977impl mz_sql::catalog::CatalogItem for CatalogEntry {
3978 fn name(&self) -> &QualifiedItemName {
3979 self.name()
3980 }
3981
3982 fn id(&self) -> CatalogItemId {
3983 self.id()
3984 }
3985
3986 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_> {
3987 Box::new(self.global_ids())
3988 }
3989
3990 fn oid(&self) -> u32 {
3991 self.oid()
3992 }
3993
3994 fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
3995 self.func()
3996 }
3997
3998 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
3999 self.source_desc()
4000 }
4001
4002 fn connection(
4003 &self,
4004 ) -> Result<mz_storage_types::connections::Connection<ReferencedConnection>, SqlCatalogError>
4005 {
4006 Ok(self.connection()?.details.to_connection())
4007 }
4008
4009 fn create_sql(&self) -> &str {
4010 match self.item() {
4011 CatalogItem::Table(Table { create_sql, .. }) => {
4012 create_sql.as_deref().unwrap_or("<builtin>")
4013 }
4014 CatalogItem::Source(Source { create_sql, .. }) => {
4015 create_sql.as_deref().unwrap_or("<builtin>")
4016 }
4017 CatalogItem::Sink(Sink { create_sql, .. }) => create_sql,
4018 CatalogItem::View(View { create_sql, .. }) => create_sql,
4019 CatalogItem::MaterializedView(MaterializedView { create_sql, .. }) => create_sql,
4020 CatalogItem::Index(Index { create_sql, .. }) => create_sql,
4021 CatalogItem::Type(Type { create_sql, .. }) => {
4022 create_sql.as_deref().unwrap_or("<builtin>")
4023 }
4024 CatalogItem::Secret(Secret { create_sql, .. }) => create_sql,
4025 CatalogItem::Connection(Connection { create_sql, .. }) => create_sql,
4026 CatalogItem::Func(_) => "<builtin>",
4027 CatalogItem::Log(_) => "<builtin>",
4028 }
4029 }
4030
4031 fn item_type(&self) -> SqlCatalogItemType {
4032 self.item().typ()
4033 }
4034
4035 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)> {
4036 if let CatalogItem::Index(Index { keys, on, .. }) = self.item() {
4037 Some((keys, *on))
4038 } else {
4039 None
4040 }
4041 }
4042
4043 fn writable_table_details(&self) -> Option<&[Expr<Aug>]> {
4044 if let CatalogItem::Table(Table {
4045 data_source: TableDataSource::TableWrites { defaults },
4046 ..
4047 }) = self.item()
4048 {
4049 Some(defaults.as_slice())
4050 } else {
4051 None
4052 }
4053 }
4054
4055 fn replacement_target(&self) -> Option<CatalogItemId> {
4056 if let CatalogItem::MaterializedView(mv) = self.item() {
4057 mv.replacement_target
4058 } else {
4059 None
4060 }
4061 }
4062
4063 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>> {
4064 if let CatalogItem::Type(Type { details, .. }) = self.item() {
4065 Some(details)
4066 } else {
4067 None
4068 }
4069 }
4070
4071 fn references(&self) -> &ResolvedIds {
4072 self.references()
4073 }
4074
4075 fn uses(&self) -> BTreeSet<CatalogItemId> {
4076 self.uses()
4077 }
4078
4079 fn referenced_by(&self) -> &[CatalogItemId] {
4080 self.referenced_by()
4081 }
4082
4083 fn used_by(&self) -> &[CatalogItemId] {
4084 self.used_by()
4085 }
4086
4087 fn subsource_details(
4088 &self,
4089 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
4090 self.subsource_details()
4091 }
4092
4093 fn source_export_details(
4094 &self,
4095 ) -> Option<(
4096 CatalogItemId,
4097 &UnresolvedItemName,
4098 &SourceExportDetails,
4099 &SourceExportDataConfig<ReferencedConnection>,
4100 )> {
4101 self.source_export_details()
4102 }
4103
4104 fn is_progress_source(&self) -> bool {
4105 self.is_progress_source()
4106 }
4107
4108 fn progress_id(&self) -> Option<CatalogItemId> {
4109 self.progress_id()
4110 }
4111
4112 fn owner_id(&self) -> RoleId {
4113 self.owner_id
4114 }
4115
4116 fn privileges(&self) -> &PrivilegeMap {
4117 &self.privileges
4118 }
4119
4120 fn cluster_id(&self) -> Option<ClusterId> {
4121 self.item().cluster_id()
4122 }
4123
4124 fn at_version(
4125 &self,
4126 version: RelationVersionSelector,
4127 ) -> Box<dyn mz_sql::catalog::CatalogCollectionItem> {
4128 Box::new(CatalogCollectionEntry {
4129 entry: self.clone(),
4130 version,
4131 })
4132 }
4133
4134 fn latest_version(&self) -> Option<RelationVersion> {
4135 self.table().map(|t| t.desc.latest_version())
4136 }
4137}
4138
4139#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4141pub struct StateUpdate {
4142 pub kind: StateUpdateKind,
4143 pub ts: Timestamp,
4144 pub diff: StateDiff,
4145}
4146
4147#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4151pub enum StateUpdateKind {
4152 Role(durable::objects::Role),
4153 RoleAuth(durable::objects::RoleAuth),
4154 Database(durable::objects::Database),
4155 Schema(durable::objects::Schema),
4156 DefaultPrivilege(durable::objects::DefaultPrivilege),
4157 SystemPrivilege(MzAclItem),
4158 SystemConfiguration(durable::objects::SystemConfiguration),
4159 Cluster(durable::objects::Cluster),
4160 ClusterSystemConfiguration(durable::objects::ClusterSystemConfiguration),
4161 NetworkPolicy(durable::objects::NetworkPolicy),
4162 IntrospectionSourceIndex(durable::objects::IntrospectionSourceIndex),
4163 ClusterReplica(durable::objects::ClusterReplica),
4164 ReplicaSystemConfiguration(durable::objects::ReplicaSystemConfiguration),
4165 SourceReferences(durable::objects::SourceReferences),
4166 SystemObjectMapping(durable::objects::SystemObjectMapping),
4167 TemporaryItem(TemporaryItem),
4171 Item(durable::objects::Item),
4172 Comment(durable::objects::Comment),
4173 AuditLog(durable::objects::AuditLog),
4174 StorageCollectionMetadata(durable::objects::StorageCollectionMetadata),
4176 UnfinalizedShard(durable::objects::UnfinalizedShard),
4177}
4178
4179#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
4181pub enum StateDiff {
4182 Retraction,
4183 Addition,
4184}
4185
4186impl From<StateDiff> for Diff {
4187 fn from(diff: StateDiff) -> Self {
4188 match diff {
4189 StateDiff::Retraction => Diff::MINUS_ONE,
4190 StateDiff::Addition => Diff::ONE,
4191 }
4192 }
4193}
4194impl TryFrom<Diff> for StateDiff {
4195 type Error = String;
4196
4197 fn try_from(diff: Diff) -> Result<Self, Self::Error> {
4198 match diff {
4199 Diff::MINUS_ONE => Ok(Self::Retraction),
4200 Diff::ONE => Ok(Self::Addition),
4201 diff => Err(format!("invalid diff {diff}")),
4202 }
4203 }
4204}
4205
4206#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
4208pub struct TemporaryItem {
4209 pub id: CatalogItemId,
4210 pub oid: u32,
4211 pub global_id: GlobalId,
4212 pub schema_id: SchemaId,
4213 pub name: String,
4214 pub conn_id: Option<ConnectionId>,
4215 pub create_sql: String,
4216 pub owner_id: RoleId,
4217 pub privileges: Vec<MzAclItem>,
4218 pub extra_versions: BTreeMap<RelationVersion, GlobalId>,
4219}
4220
4221impl From<CatalogEntry> for TemporaryItem {
4222 fn from(entry: CatalogEntry) -> Self {
4223 let conn_id = entry.conn_id().cloned();
4224 let (create_sql, global_id, extra_versions) = entry.item.to_serialized();
4225
4226 TemporaryItem {
4227 id: entry.id,
4228 oid: entry.oid,
4229 global_id,
4230 schema_id: entry.name.qualifiers.schema_spec.into(),
4231 name: entry.name.item,
4232 conn_id,
4233 create_sql,
4234 owner_id: entry.owner_id,
4235 privileges: entry.privileges.into_all_values().collect(),
4236 extra_versions,
4237 }
4238 }
4239}
4240
4241impl TemporaryItem {
4242 pub fn item_type(&self) -> CatalogItemType {
4243 item_type(&self.create_sql)
4244 }
4245}
4246
4247#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
4249pub enum BootstrapStateUpdateKind {
4250 Role(durable::objects::Role),
4251 RoleAuth(durable::objects::RoleAuth),
4252 Database(durable::objects::Database),
4253 Schema(durable::objects::Schema),
4254 DefaultPrivilege(durable::objects::DefaultPrivilege),
4255 SystemPrivilege(MzAclItem),
4256 SystemConfiguration(durable::objects::SystemConfiguration),
4257 Cluster(durable::objects::Cluster),
4258 ClusterSystemConfiguration(durable::objects::ClusterSystemConfiguration),
4259 NetworkPolicy(durable::objects::NetworkPolicy),
4260 IntrospectionSourceIndex(durable::objects::IntrospectionSourceIndex),
4261 ClusterReplica(durable::objects::ClusterReplica),
4262 ReplicaSystemConfiguration(durable::objects::ReplicaSystemConfiguration),
4263 SourceReferences(durable::objects::SourceReferences),
4264 SystemObjectMapping(durable::objects::SystemObjectMapping),
4265 Item(durable::objects::Item),
4266 Comment(durable::objects::Comment),
4267 AuditLog(durable::objects::AuditLog),
4268 StorageCollectionMetadata(durable::objects::StorageCollectionMetadata),
4270 UnfinalizedShard(durable::objects::UnfinalizedShard),
4271}
4272
4273impl From<BootstrapStateUpdateKind> for StateUpdateKind {
4274 fn from(value: BootstrapStateUpdateKind) -> Self {
4275 match value {
4276 BootstrapStateUpdateKind::Role(kind) => StateUpdateKind::Role(kind),
4277 BootstrapStateUpdateKind::RoleAuth(kind) => StateUpdateKind::RoleAuth(kind),
4278 BootstrapStateUpdateKind::Database(kind) => StateUpdateKind::Database(kind),
4279 BootstrapStateUpdateKind::Schema(kind) => StateUpdateKind::Schema(kind),
4280 BootstrapStateUpdateKind::DefaultPrivilege(kind) => {
4281 StateUpdateKind::DefaultPrivilege(kind)
4282 }
4283 BootstrapStateUpdateKind::SystemPrivilege(kind) => {
4284 StateUpdateKind::SystemPrivilege(kind)
4285 }
4286 BootstrapStateUpdateKind::SystemConfiguration(kind) => {
4287 StateUpdateKind::SystemConfiguration(kind)
4288 }
4289 BootstrapStateUpdateKind::ClusterSystemConfiguration(kind) => {
4290 StateUpdateKind::ClusterSystemConfiguration(kind)
4291 }
4292 BootstrapStateUpdateKind::ReplicaSystemConfiguration(kind) => {
4293 StateUpdateKind::ReplicaSystemConfiguration(kind)
4294 }
4295 BootstrapStateUpdateKind::SourceReferences(kind) => {
4296 StateUpdateKind::SourceReferences(kind)
4297 }
4298 BootstrapStateUpdateKind::Cluster(kind) => StateUpdateKind::Cluster(kind),
4299 BootstrapStateUpdateKind::NetworkPolicy(kind) => StateUpdateKind::NetworkPolicy(kind),
4300 BootstrapStateUpdateKind::IntrospectionSourceIndex(kind) => {
4301 StateUpdateKind::IntrospectionSourceIndex(kind)
4302 }
4303 BootstrapStateUpdateKind::ClusterReplica(kind) => StateUpdateKind::ClusterReplica(kind),
4304 BootstrapStateUpdateKind::SystemObjectMapping(kind) => {
4305 StateUpdateKind::SystemObjectMapping(kind)
4306 }
4307 BootstrapStateUpdateKind::Item(kind) => StateUpdateKind::Item(kind),
4308 BootstrapStateUpdateKind::Comment(kind) => StateUpdateKind::Comment(kind),
4309 BootstrapStateUpdateKind::AuditLog(kind) => StateUpdateKind::AuditLog(kind),
4310 BootstrapStateUpdateKind::StorageCollectionMetadata(kind) => {
4311 StateUpdateKind::StorageCollectionMetadata(kind)
4312 }
4313 BootstrapStateUpdateKind::UnfinalizedShard(kind) => {
4314 StateUpdateKind::UnfinalizedShard(kind)
4315 }
4316 }
4317 }
4318}
4319
4320impl TryFrom<StateUpdateKind> for BootstrapStateUpdateKind {
4321 type Error = TemporaryItem;
4322
4323 fn try_from(value: StateUpdateKind) -> Result<Self, Self::Error> {
4324 match value {
4325 StateUpdateKind::Role(kind) => Ok(BootstrapStateUpdateKind::Role(kind)),
4326 StateUpdateKind::RoleAuth(kind) => Ok(BootstrapStateUpdateKind::RoleAuth(kind)),
4327 StateUpdateKind::Database(kind) => Ok(BootstrapStateUpdateKind::Database(kind)),
4328 StateUpdateKind::Schema(kind) => Ok(BootstrapStateUpdateKind::Schema(kind)),
4329 StateUpdateKind::DefaultPrivilege(kind) => {
4330 Ok(BootstrapStateUpdateKind::DefaultPrivilege(kind))
4331 }
4332 StateUpdateKind::SystemPrivilege(kind) => {
4333 Ok(BootstrapStateUpdateKind::SystemPrivilege(kind))
4334 }
4335 StateUpdateKind::SystemConfiguration(kind) => {
4336 Ok(BootstrapStateUpdateKind::SystemConfiguration(kind))
4337 }
4338 StateUpdateKind::ClusterSystemConfiguration(kind) => {
4339 Ok(BootstrapStateUpdateKind::ClusterSystemConfiguration(kind))
4340 }
4341 StateUpdateKind::ReplicaSystemConfiguration(kind) => {
4342 Ok(BootstrapStateUpdateKind::ReplicaSystemConfiguration(kind))
4343 }
4344 StateUpdateKind::Cluster(kind) => Ok(BootstrapStateUpdateKind::Cluster(kind)),
4345 StateUpdateKind::NetworkPolicy(kind) => {
4346 Ok(BootstrapStateUpdateKind::NetworkPolicy(kind))
4347 }
4348 StateUpdateKind::IntrospectionSourceIndex(kind) => {
4349 Ok(BootstrapStateUpdateKind::IntrospectionSourceIndex(kind))
4350 }
4351 StateUpdateKind::ClusterReplica(kind) => {
4352 Ok(BootstrapStateUpdateKind::ClusterReplica(kind))
4353 }
4354 StateUpdateKind::SourceReferences(kind) => {
4355 Ok(BootstrapStateUpdateKind::SourceReferences(kind))
4356 }
4357 StateUpdateKind::SystemObjectMapping(kind) => {
4358 Ok(BootstrapStateUpdateKind::SystemObjectMapping(kind))
4359 }
4360 StateUpdateKind::TemporaryItem(kind) => Err(kind),
4361 StateUpdateKind::Item(kind) => Ok(BootstrapStateUpdateKind::Item(kind)),
4362 StateUpdateKind::Comment(kind) => Ok(BootstrapStateUpdateKind::Comment(kind)),
4363 StateUpdateKind::AuditLog(kind) => Ok(BootstrapStateUpdateKind::AuditLog(kind)),
4364 StateUpdateKind::StorageCollectionMetadata(kind) => {
4365 Ok(BootstrapStateUpdateKind::StorageCollectionMetadata(kind))
4366 }
4367 StateUpdateKind::UnfinalizedShard(kind) => {
4368 Ok(BootstrapStateUpdateKind::UnfinalizedShard(kind))
4369 }
4370 }
4371 }
4372}