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