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 from_plan(
901 plan: mz_sql::plan::Table,
902 global_id: GlobalId,
903 connection_id: ConnectionId,
904 resolved_ids: ResolvedIds,
905 ) -> Self {
906 let conn_id = plan.temporary.then_some(connection_id);
907 let collections = [(RelationVersion::root(), global_id)].into_iter().collect();
908 let data_source = match plan.data_source {
909 mz_sql::plan::TableDataSource::TableWrites { defaults } => {
910 TableDataSource::TableWrites { defaults }
911 }
912 mz_sql::plan::TableDataSource::DataSource {
913 desc: data_source_plan,
914 timeline,
915 } => match data_source_plan {
916 mz_sql::plan::DataSourceDesc::IngestionExport {
917 ingestion_id,
918 external_reference,
919 details,
920 data_config,
921 } => TableDataSource::DataSource {
922 desc: DataSourceDesc::IngestionExport {
923 ingestion_id,
924 external_reference,
925 details,
926 data_config,
927 },
928 timeline,
929 },
930 mz_sql::plan::DataSourceDesc::Webhook {
931 validate_using,
932 body_format,
933 headers,
934 cluster_id,
935 } => TableDataSource::DataSource {
936 desc: DataSourceDesc::Webhook {
937 validate_using,
938 body_format,
939 headers,
940 cluster_id: cluster_id.expect("Webhook Tables must have cluster_id set"),
941 },
942 timeline,
943 },
944 o => {
945 unreachable!("CREATE TABLE data source got {:?}", o)
946 }
947 },
948 };
949
950 Self {
951 create_sql: Some(plan.create_sql),
952 desc: plan.desc,
953 collections,
954 conn_id,
955 resolved_ids,
956 custom_logical_compaction_window: plan.compaction_window,
957 is_retained_metrics_object: false,
958 data_source,
959 }
960 }
961
962 pub fn timeline(&self) -> Timeline {
963 match &self.data_source {
964 TableDataSource::TableWrites { .. } => Timeline::EpochMilliseconds,
967 TableDataSource::DataSource { timeline, .. } => timeline.clone(),
968 }
969 }
970
971 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
973 self.collections.values().copied()
974 }
975
976 pub fn global_id_writes(&self) -> GlobalId {
978 *self
979 .collections
980 .last_key_value()
981 .expect("at least one version of a table")
982 .1
983 }
984
985 pub fn collection_descs(
987 &self,
988 ) -> impl Iterator<Item = (GlobalId, RelationVersion, RelationDesc)> + '_ {
989 self.collections.iter().map(|(version, gid)| {
990 let desc = self
991 .desc
992 .at_version(RelationVersionSelector::Specific(*version));
993 (*gid, *version, desc)
994 })
995 }
996
997 pub fn desc_for(&self, id: &GlobalId) -> RelationDesc {
999 let (version, _gid) = self
1000 .collections
1001 .iter()
1002 .find(|(_version, gid)| *gid == id)
1003 .expect("GlobalId to exist");
1004 self.desc
1005 .at_version(RelationVersionSelector::Specific(*version))
1006 }
1007}
1008
1009#[derive(Clone, Debug, Serialize)]
1010pub enum TableDataSource {
1011 TableWrites {
1013 #[serde(skip)]
1014 defaults: Vec<Expr<Aug>>,
1015 },
1016
1017 DataSource {
1020 desc: DataSourceDesc,
1021 timeline: Timeline,
1022 },
1023}
1024
1025#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
1026pub enum DataSourceDesc {
1027 Ingestion {
1029 desc: SourceDesc<ReferencedConnection>,
1030 cluster_id: ClusterId,
1031 },
1032 OldSyntaxIngestion {
1034 desc: SourceDesc<ReferencedConnection>,
1035 cluster_id: ClusterId,
1036 progress_subsource: CatalogItemId,
1039 data_config: SourceExportDataConfig<ReferencedConnection>,
1040 details: SourceExportDetails,
1041 },
1042 IngestionExport {
1050 ingestion_id: CatalogItemId,
1051 external_reference: UnresolvedItemName,
1052 details: SourceExportDetails,
1053 data_config: SourceExportDataConfig<ReferencedConnection>,
1054 },
1055 Introspection(IntrospectionType),
1057 Progress,
1059 Webhook {
1061 validate_using: Option<WebhookValidation>,
1063 body_format: WebhookBodyFormat,
1065 headers: WebhookHeaders,
1067 cluster_id: ClusterId,
1069 },
1070 Catalog,
1072}
1073
1074impl From<IntrospectionType> for DataSourceDesc {
1075 fn from(typ: IntrospectionType) -> Self {
1076 Self::Introspection(typ)
1077 }
1078}
1079
1080impl DataSourceDesc {
1081 pub fn formats(&self) -> (Option<&str>, Option<&str>) {
1083 match &self {
1084 DataSourceDesc::Ingestion { .. } => (None, None),
1085 DataSourceDesc::OldSyntaxIngestion { data_config, .. } => {
1086 match &data_config.encoding.as_ref() {
1087 Some(encoding) => match &encoding.key {
1088 Some(key) => (Some(key.type_()), Some(encoding.value.type_())),
1089 None => (None, Some(encoding.value.type_())),
1090 },
1091 None => (None, None),
1092 }
1093 }
1094 DataSourceDesc::IngestionExport { data_config, .. } => match &data_config.encoding {
1095 Some(encoding) => match &encoding.key {
1096 Some(key) => (Some(key.type_()), Some(encoding.value.type_())),
1097 None => (None, Some(encoding.value.type_())),
1098 },
1099 None => (None, None),
1100 },
1101 DataSourceDesc::Introspection(_)
1102 | DataSourceDesc::Webhook { .. }
1103 | DataSourceDesc::Progress
1104 | DataSourceDesc::Catalog => (None, None),
1105 }
1106 }
1107
1108 pub fn envelope(&self) -> Option<&str> {
1110 fn envelope_string(envelope: &SourceEnvelope) -> &str {
1115 match envelope {
1116 SourceEnvelope::None(_) => "none",
1117 SourceEnvelope::Upsert(upsert_envelope) => match upsert_envelope.style {
1118 mz_storage_types::sources::envelope::UpsertStyle::Default(_) => "upsert",
1119 mz_storage_types::sources::envelope::UpsertStyle::Debezium { .. } => {
1120 "debezium"
1124 }
1125 mz_storage_types::sources::envelope::UpsertStyle::ValueErrInline { .. } => {
1126 "upsert-value-err-inline"
1127 }
1128 },
1129 SourceEnvelope::CdcV2 => {
1130 "materialize"
1133 }
1134 }
1135 }
1136
1137 match self {
1138 DataSourceDesc::Ingestion { .. } => None,
1143 DataSourceDesc::OldSyntaxIngestion { data_config, .. } => {
1144 Some(envelope_string(&data_config.envelope))
1145 }
1146 DataSourceDesc::IngestionExport { data_config, .. } => {
1147 Some(envelope_string(&data_config.envelope))
1148 }
1149 DataSourceDesc::Introspection(_)
1150 | DataSourceDesc::Webhook { .. }
1151 | DataSourceDesc::Progress
1152 | DataSourceDesc::Catalog => None,
1153 }
1154 }
1155}
1156
1157#[derive(Debug, Clone, Serialize)]
1158pub struct Source {
1159 pub create_sql: Option<String>,
1161 pub global_id: GlobalId,
1163 #[serde(skip)]
1165 pub data_source: DataSourceDesc,
1166 pub desc: RelationDesc,
1168 pub timeline: Timeline,
1170 pub resolved_ids: ResolvedIds,
1172 pub custom_logical_compaction_window: Option<CompactionWindow>,
1176 pub is_retained_metrics_object: bool,
1179}
1180
1181impl Source {
1182 pub fn new(
1189 plan: CreateSourcePlan,
1190 global_id: GlobalId,
1191 resolved_ids: ResolvedIds,
1192 custom_logical_compaction_window: Option<CompactionWindow>,
1193 is_retained_metrics_object: bool,
1194 ) -> Source {
1195 Source {
1196 create_sql: Some(plan.source.create_sql),
1197 data_source: match plan.source.data_source {
1198 mz_sql::plan::DataSourceDesc::Ingestion(desc) => DataSourceDesc::Ingestion {
1199 desc,
1200 cluster_id: plan
1201 .in_cluster
1202 .expect("ingestion-based sources must be given a cluster ID"),
1203 },
1204 mz_sql::plan::DataSourceDesc::OldSyntaxIngestion {
1205 desc,
1206 progress_subsource,
1207 data_config,
1208 details,
1209 } => DataSourceDesc::OldSyntaxIngestion {
1210 desc,
1211 cluster_id: plan
1212 .in_cluster
1213 .expect("ingestion-based sources must be given a cluster ID"),
1214 progress_subsource,
1215 data_config,
1216 details,
1217 },
1218 mz_sql::plan::DataSourceDesc::Progress => {
1219 assert!(
1220 plan.in_cluster.is_none(),
1221 "subsources must not have a host config or cluster_id defined"
1222 );
1223 DataSourceDesc::Progress
1224 }
1225 mz_sql::plan::DataSourceDesc::IngestionExport {
1226 ingestion_id,
1227 external_reference,
1228 details,
1229 data_config,
1230 } => {
1231 assert!(
1232 plan.in_cluster.is_none(),
1233 "subsources must not have a host config or cluster_id defined"
1234 );
1235 DataSourceDesc::IngestionExport {
1236 ingestion_id,
1237 external_reference,
1238 details,
1239 data_config,
1240 }
1241 }
1242 mz_sql::plan::DataSourceDesc::Webhook {
1243 validate_using,
1244 body_format,
1245 headers,
1246 cluster_id,
1247 } => {
1248 mz_ore::soft_assert_or_log!(
1249 cluster_id.is_none(),
1250 "cluster_id set at Source level for Webhooks"
1251 );
1252 DataSourceDesc::Webhook {
1253 validate_using,
1254 body_format,
1255 headers,
1256 cluster_id: plan
1257 .in_cluster
1258 .expect("webhook sources must be given a cluster ID"),
1259 }
1260 }
1261 },
1262 desc: plan.source.desc,
1263 global_id,
1264 timeline: plan.timeline,
1265 resolved_ids,
1266 custom_logical_compaction_window: plan
1267 .source
1268 .compaction_window
1269 .or(custom_logical_compaction_window),
1270 is_retained_metrics_object,
1271 }
1272 }
1273
1274 pub fn source_type(&self) -> &str {
1276 match &self.data_source {
1277 DataSourceDesc::Ingestion { desc, .. }
1278 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => desc.connection.name(),
1279 DataSourceDesc::Progress => "progress",
1280 DataSourceDesc::IngestionExport { .. } => "subsource",
1281 DataSourceDesc::Introspection(_) | DataSourceDesc::Catalog => "source",
1282 DataSourceDesc::Webhook { .. } => "webhook",
1283 }
1284 }
1285
1286 pub fn connection_id(&self) -> Option<CatalogItemId> {
1288 match &self.data_source {
1289 DataSourceDesc::Ingestion { desc, .. }
1290 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => desc.connection.connection_id(),
1291 DataSourceDesc::IngestionExport { .. }
1292 | DataSourceDesc::Introspection(_)
1293 | DataSourceDesc::Webhook { .. }
1294 | DataSourceDesc::Progress
1295 | DataSourceDesc::Catalog => None,
1296 }
1297 }
1298
1299 pub fn global_id(&self) -> GlobalId {
1301 self.global_id
1302 }
1303
1304 pub fn user_controllable_persist_shard_count(&self) -> i64 {
1312 match &self.data_source {
1313 DataSourceDesc::Ingestion { .. } => 0,
1314 DataSourceDesc::OldSyntaxIngestion { desc, .. } => {
1315 match &desc.connection {
1316 GenericSourceConnection::Postgres(_)
1320 | GenericSourceConnection::MySql(_)
1321 | GenericSourceConnection::SqlServer(_) => 0,
1322 GenericSourceConnection::LoadGenerator(lg) => match lg.load_generator {
1323 LoadGenerator::Clock
1325 | LoadGenerator::Counter { .. }
1326 | LoadGenerator::Datums
1327 | LoadGenerator::KeyValue(_) => 1,
1328 LoadGenerator::Auction
1329 | LoadGenerator::Marketing
1330 | LoadGenerator::Tpch { .. } => 0,
1331 },
1332 GenericSourceConnection::Kafka(_) => 1,
1333 }
1334 }
1335 DataSourceDesc::IngestionExport { .. } => 1,
1338 DataSourceDesc::Webhook { .. } => 1,
1339 DataSourceDesc::Introspection(_)
1342 | DataSourceDesc::Progress
1343 | DataSourceDesc::Catalog => 0,
1344 }
1345 }
1346}
1347
1348#[derive(Debug, Clone, Serialize)]
1349pub struct Log {
1350 pub variant: LogVariant,
1352 pub global_id: GlobalId,
1354}
1355
1356impl Log {
1357 pub fn global_id(&self) -> GlobalId {
1359 self.global_id
1360 }
1361}
1362
1363#[derive(Debug, Clone, Serialize)]
1364pub struct Sink {
1365 pub create_sql: String,
1367 pub global_id: GlobalId,
1369 pub from: GlobalId,
1371 pub connection: StorageSinkConnection<ReferencedConnection>,
1373 pub envelope: SinkEnvelope,
1377 pub with_snapshot: bool,
1379 pub version: u64,
1381 pub resolved_ids: ResolvedIds,
1383 pub cluster_id: ClusterId,
1385 pub commit_interval: Option<Duration>,
1387}
1388
1389impl Sink {
1390 pub fn sink_type(&self) -> &str {
1391 self.connection.name()
1392 }
1393
1394 pub fn envelope(&self) -> Option<&str> {
1400 match &self.envelope {
1401 SinkEnvelope::Debezium => Some("debezium"),
1402 SinkEnvelope::Upsert => Some("upsert"),
1403 SinkEnvelope::Append => Some("append"),
1404 }
1405 }
1406
1407 pub fn combined_format(&self) -> Option<Cow<'_, str>> {
1415 match &self.connection {
1416 StorageSinkConnection::Kafka(connection) => Some(connection.format.get_format_name()),
1417 StorageSinkConnection::Iceberg(_) => None,
1418 }
1419 }
1420
1421 pub fn formats(&self) -> Option<(Option<&str>, &str)> {
1427 match &self.connection {
1428 StorageSinkConnection::Kafka(connection) => {
1429 let key_format = connection
1430 .format
1431 .key_format
1432 .as_ref()
1433 .map(|f| f.get_format_name());
1434 let value_format = connection.format.value_format.get_format_name();
1435 Some((key_format, value_format))
1436 }
1437 StorageSinkConnection::Iceberg(_) => None,
1438 }
1439 }
1440
1441 pub fn connection_id(&self) -> Option<CatalogItemId> {
1442 self.connection.connection_id()
1443 }
1444
1445 pub fn global_id(&self) -> GlobalId {
1447 self.global_id
1448 }
1449}
1450
1451#[derive(Debug, Clone, Serialize)]
1452pub struct View {
1453 pub create_sql: String,
1455 pub global_id: GlobalId,
1457 pub raw_expr: Arc<HirRelationExpr>,
1459 pub locally_optimized_expr: Arc<OptimizedMirRelationExpr>,
1461 pub desc: RelationDesc,
1463 pub conn_id: Option<ConnectionId>,
1465 pub resolved_ids: ResolvedIds,
1467 pub dependencies: DependencyIds,
1469}
1470
1471impl View {
1472 pub fn global_id(&self) -> GlobalId {
1474 self.global_id
1475 }
1476}
1477
1478#[derive(Debug, Clone, Serialize)]
1479pub struct MaterializedView {
1480 pub create_sql: String,
1482 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
1484 pub collections: BTreeMap<RelationVersion, GlobalId>,
1485 pub raw_expr: Arc<HirRelationExpr>,
1487 pub locally_optimized_expr: Arc<OptimizedMirRelationExpr>,
1489 pub desc: VersionedRelationDesc,
1491 pub resolved_ids: ResolvedIds,
1493 pub dependencies: DependencyIds,
1495 pub replacement_target: Option<CatalogItemId>,
1497 pub cluster_id: ClusterId,
1499 pub target_replica: Option<ReplicaId>,
1501 pub non_null_assertions: Vec<usize>,
1505 pub custom_logical_compaction_window: Option<CompactionWindow>,
1507 pub refresh_schedule: Option<RefreshSchedule>,
1509 pub initial_as_of: Option<Antichain<mz_repr::Timestamp>>,
1514 #[serde(skip)]
1520 pub optimized_plan: Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1521 #[serde(skip)]
1523 pub physical_plan: Option<Arc<LirDataflowDescription>>,
1524 #[serde(skip)]
1526 pub dataflow_metainfo: Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1527}
1528
1529impl MaterializedView {
1530 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
1532 self.collections.values().copied()
1533 }
1534
1535 pub fn global_id_writes(&self) -> GlobalId {
1538 *self
1539 .collections
1540 .last_key_value()
1541 .expect("at least one version of a materialized view")
1542 .1
1543 }
1544
1545 pub fn collection_descs(
1547 &self,
1548 ) -> impl Iterator<Item = (GlobalId, RelationVersion, RelationDesc)> + '_ {
1549 self.collections.iter().map(|(version, gid)| {
1550 let desc = self
1551 .desc
1552 .at_version(RelationVersionSelector::Specific(*version));
1553 (*gid, *version, desc)
1554 })
1555 }
1556
1557 pub fn desc_for(&self, id: &GlobalId) -> RelationDesc {
1559 let (version, _gid) = self
1560 .collections
1561 .iter()
1562 .find(|(_version, gid)| *gid == id)
1563 .expect("GlobalId to exist");
1564 self.desc
1565 .at_version(RelationVersionSelector::Specific(*version))
1566 }
1567
1568 pub fn apply_replacement(&mut self, replacement: Self) {
1570 let target_id = replacement
1571 .replacement_target
1572 .expect("replacement has target");
1573
1574 fn parse(create_sql: &str) -> mz_sql::ast::CreateMaterializedViewStatement<Raw> {
1575 let res = mz_sql::parse::parse(create_sql).unwrap_or_else(|e| {
1576 panic!("invalid create_sql persisted in catalog: {e}\n{create_sql}");
1577 });
1578 if let Statement::CreateMaterializedView(cmvs) = res.into_element().ast {
1579 cmvs
1580 } else {
1581 panic!("invalid MV create_sql persisted in catalog\n{create_sql}");
1582 }
1583 }
1584
1585 let old_stmt = parse(&self.create_sql);
1586 let rpl_stmt = parse(&replacement.create_sql);
1587 let new_stmt = mz_sql::ast::CreateMaterializedViewStatement {
1588 if_exists: old_stmt.if_exists,
1589 name: old_stmt.name,
1590 columns: rpl_stmt.columns,
1591 replacement_for: None,
1592 in_cluster: rpl_stmt.in_cluster,
1593 in_cluster_replica: rpl_stmt.in_cluster_replica,
1594 query: rpl_stmt.query,
1595 as_of: rpl_stmt.as_of,
1596 with_options: rpl_stmt.with_options,
1597 };
1598 let create_sql = new_stmt.to_ast_string_stable();
1599
1600 let mut collections = std::mem::take(&mut self.collections);
1601 let latest_version = collections.keys().max().expect("at least one version");
1605 let new_version = latest_version.bump();
1606 collections.insert(new_version, replacement.global_id_writes());
1607
1608 let mut resolved_ids = replacement.resolved_ids;
1609 resolved_ids.remove_item(&target_id);
1610 let mut dependencies = replacement.dependencies;
1611 dependencies.0.remove(&target_id);
1612
1613 *self = Self {
1614 create_sql,
1615 collections,
1616 raw_expr: replacement.raw_expr,
1617 locally_optimized_expr: replacement.locally_optimized_expr,
1618 desc: replacement.desc,
1619 resolved_ids,
1620 dependencies,
1621 replacement_target: None,
1622 cluster_id: replacement.cluster_id,
1623 target_replica: replacement.target_replica,
1624 non_null_assertions: replacement.non_null_assertions,
1625 custom_logical_compaction_window: replacement.custom_logical_compaction_window,
1626 refresh_schedule: replacement.refresh_schedule,
1627 initial_as_of: replacement.initial_as_of,
1628 optimized_plan: replacement.optimized_plan,
1629 physical_plan: replacement.physical_plan,
1630 dataflow_metainfo: replacement.dataflow_metainfo,
1631 };
1632 }
1633}
1634
1635#[derive(Debug, Clone, Serialize)]
1636pub struct Index {
1637 pub create_sql: String,
1639 pub global_id: GlobalId,
1641 pub on: GlobalId,
1643 pub keys: Arc<[MirScalarExpr]>,
1645 pub conn_id: Option<ConnectionId>,
1647 pub resolved_ids: ResolvedIds,
1649 pub cluster_id: ClusterId,
1651 pub custom_logical_compaction_window: Option<CompactionWindow>,
1653 pub is_retained_metrics_object: bool,
1658 #[serde(skip)]
1664 pub optimized_plan: Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1665 #[serde(skip)]
1667 pub physical_plan: Option<Arc<LirDataflowDescription>>,
1668 #[serde(skip)]
1670 pub dataflow_metainfo: Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1671}
1672
1673impl Index {
1674 pub fn global_id(&self) -> GlobalId {
1676 self.global_id
1677 }
1678}
1679
1680#[derive(Debug, Clone, Serialize)]
1682pub struct MetricSink {
1683 pub create_sql: String,
1685 pub global_id: GlobalId,
1687 pub from: GlobalId,
1689 pub resolved_ids: ResolvedIds,
1691 pub cluster_id: ClusterId,
1693 pub prefix: String,
1696 #[serde(skip)]
1698 pub optimized_plan: Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1699 #[serde(skip)]
1701 pub physical_plan: Option<Arc<LirDataflowDescription>>,
1702 #[serde(skip)]
1704 pub dataflow_metainfo: Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1705}
1706
1707impl MetricSink {
1708 pub fn global_id(&self) -> GlobalId {
1710 self.global_id
1711 }
1712}
1713
1714#[derive(Debug, Clone, Serialize)]
1715pub struct Type {
1716 pub create_sql: Option<String>,
1718 pub global_id: GlobalId,
1720 #[serde(skip)]
1721 pub details: CatalogTypeDetails<IdReference>,
1722 pub resolved_ids: ResolvedIds,
1724}
1725
1726#[derive(Debug, Clone, Serialize)]
1727pub struct Func {
1728 #[serde(skip)]
1730 pub inner: &'static mz_sql::func::Func,
1731 pub global_id: GlobalId,
1733}
1734
1735#[derive(Debug, Clone, Serialize)]
1736pub struct Secret {
1737 pub create_sql: String,
1739 pub global_id: GlobalId,
1741}
1742
1743#[derive(Debug, Clone, Serialize)]
1744pub struct Connection {
1745 pub create_sql: String,
1747 pub global_id: GlobalId,
1749 pub details: ConnectionDetails,
1751 pub resolved_ids: ResolvedIds,
1753}
1754
1755impl Connection {
1756 pub fn global_id(&self) -> GlobalId {
1758 self.global_id
1759 }
1760}
1761
1762#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1763pub struct NetworkPolicy {
1764 pub name: String,
1765 pub id: NetworkPolicyId,
1766 pub oid: u32,
1767 pub rules: Vec<NetworkPolicyRule>,
1768 pub owner_id: RoleId,
1769 pub privileges: PrivilegeMap,
1770}
1771
1772impl From<NetworkPolicy> for durable::NetworkPolicy {
1773 fn from(policy: NetworkPolicy) -> durable::NetworkPolicy {
1774 durable::NetworkPolicy {
1775 id: policy.id,
1776 oid: policy.oid,
1777 name: policy.name,
1778 rules: policy.rules,
1779 owner_id: policy.owner_id,
1780 privileges: policy.privileges.into_all_values().collect(),
1781 }
1782 }
1783}
1784
1785impl From<durable::NetworkPolicy> for NetworkPolicy {
1786 fn from(
1787 durable::NetworkPolicy {
1788 id,
1789 oid,
1790 name,
1791 rules,
1792 owner_id,
1793 privileges,
1794 }: durable::NetworkPolicy,
1795 ) -> Self {
1796 NetworkPolicy {
1797 id,
1798 oid,
1799 name,
1800 rules,
1801 owner_id,
1802 privileges: PrivilegeMap::from_mz_acl_items(privileges),
1803 }
1804 }
1805}
1806
1807impl UpdateFrom<durable::NetworkPolicy> for NetworkPolicy {
1808 fn update_from(
1809 &mut self,
1810 durable::NetworkPolicy {
1811 id,
1812 oid,
1813 name,
1814 rules,
1815 owner_id,
1816 privileges,
1817 }: durable::NetworkPolicy,
1818 ) {
1819 self.id = id;
1820 self.oid = oid;
1821 self.name = name;
1822 self.rules = rules;
1823 self.owner_id = owner_id;
1824 self.privileges = PrivilegeMap::from_mz_acl_items(privileges);
1825 }
1826}
1827
1828impl CatalogItem {
1829 pub fn typ(&self) -> mz_sql::catalog::CatalogItemType {
1831 match self {
1832 CatalogItem::Table(_) => CatalogItemType::Table,
1833 CatalogItem::Source(_) => CatalogItemType::Source,
1834 CatalogItem::Log(_) => CatalogItemType::Source,
1835 CatalogItem::Sink(_) => CatalogItemType::Sink,
1836 CatalogItem::View(_) => CatalogItemType::View,
1837 CatalogItem::MaterializedView(_) => CatalogItemType::MaterializedView,
1838 CatalogItem::Index(_) => CatalogItemType::Index,
1839 CatalogItem::Type(_) => CatalogItemType::Type,
1840 CatalogItem::Func(_) => CatalogItemType::Func,
1841 CatalogItem::Secret(_) => CatalogItemType::Secret,
1842 CatalogItem::Connection(_) => CatalogItemType::Connection,
1843 CatalogItem::MetricSink(_) => CatalogItemType::MetricSink,
1844 }
1845 }
1846
1847 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
1849 let gid = match self {
1850 CatalogItem::Source(source) => source.global_id,
1851 CatalogItem::Log(log) => log.global_id,
1852 CatalogItem::Sink(sink) => sink.global_id,
1853 CatalogItem::View(view) => view.global_id,
1854 CatalogItem::MaterializedView(mv) => {
1855 return itertools::Either::Left(mv.collections.values().copied());
1856 }
1857 CatalogItem::Index(index) => index.global_id,
1858 CatalogItem::Func(func) => func.global_id,
1859 CatalogItem::Type(ty) => ty.global_id,
1860 CatalogItem::Secret(secret) => secret.global_id,
1861 CatalogItem::Connection(conn) => conn.global_id,
1862 CatalogItem::MetricSink(metric_sink) => metric_sink.global_id,
1863 CatalogItem::Table(table) => {
1864 return itertools::Either::Left(table.collections.values().copied());
1865 }
1866 };
1867 itertools::Either::Right(std::iter::once(gid))
1868 }
1869
1870 pub fn latest_global_id(&self) -> GlobalId {
1874 match self {
1875 CatalogItem::Source(source) => source.global_id,
1876 CatalogItem::Log(log) => log.global_id,
1877 CatalogItem::Sink(sink) => sink.global_id,
1878 CatalogItem::View(view) => view.global_id,
1879 CatalogItem::MaterializedView(mv) => mv.global_id_writes(),
1880 CatalogItem::Index(index) => index.global_id,
1881 CatalogItem::Func(func) => func.global_id,
1882 CatalogItem::Type(ty) => ty.global_id,
1883 CatalogItem::Secret(secret) => secret.global_id,
1884 CatalogItem::Connection(conn) => conn.global_id,
1885 CatalogItem::MetricSink(metric_sink) => metric_sink.global_id,
1886 CatalogItem::Table(table) => table.global_id_writes(),
1887 }
1888 }
1889
1890 pub fn optimized_plan(&self) -> Option<&Arc<DataflowDescription<OptimizedMirRelationExpr>>> {
1892 match self {
1893 CatalogItem::Index(idx) => idx.optimized_plan.as_ref(),
1894 CatalogItem::MaterializedView(mv) => mv.optimized_plan.as_ref(),
1895 CatalogItem::MetricSink(ms) => ms.optimized_plan.as_ref(),
1896 _ => None,
1897 }
1898 }
1899
1900 pub fn physical_plan(&self) -> Option<&Arc<LirDataflowDescription>> {
1902 match self {
1903 CatalogItem::Index(idx) => idx.physical_plan.as_ref(),
1904 CatalogItem::MaterializedView(mv) => mv.physical_plan.as_ref(),
1905 CatalogItem::MetricSink(ms) => ms.physical_plan.as_ref(),
1906 _ => None,
1907 }
1908 }
1909
1910 pub fn dataflow_metainfo(&self) -> Option<&DataflowMetainfo<Arc<OptimizerNotice>>> {
1912 match self {
1913 CatalogItem::Index(idx) => idx.dataflow_metainfo.as_ref(),
1914 CatalogItem::MaterializedView(mv) => mv.dataflow_metainfo.as_ref(),
1915 CatalogItem::MetricSink(ms) => ms.dataflow_metainfo.as_ref(),
1916 _ => None,
1917 }
1918 }
1919
1920 pub fn dataflow_metainfo_mut(&mut self) -> Option<&mut DataflowMetainfo<Arc<OptimizerNotice>>> {
1922 match self {
1923 CatalogItem::Index(idx) => idx.dataflow_metainfo.as_mut(),
1924 CatalogItem::MaterializedView(mv) => mv.dataflow_metainfo.as_mut(),
1925 CatalogItem::MetricSink(ms) => ms.dataflow_metainfo.as_mut(),
1926 _ => None,
1927 }
1928 }
1929
1930 pub fn plan_fields_mut(
1935 &mut self,
1936 ) -> Option<(
1937 &mut Option<Arc<DataflowDescription<OptimizedMirRelationExpr>>>,
1938 &mut Option<Arc<LirDataflowDescription>>,
1939 &mut Option<DataflowMetainfo<Arc<OptimizerNotice>>>,
1940 )> {
1941 match self {
1942 CatalogItem::Index(idx) => Some((
1943 &mut idx.optimized_plan,
1944 &mut idx.physical_plan,
1945 &mut idx.dataflow_metainfo,
1946 )),
1947 CatalogItem::MaterializedView(mv) => Some((
1948 &mut mv.optimized_plan,
1949 &mut mv.physical_plan,
1950 &mut mv.dataflow_metainfo,
1951 )),
1952 CatalogItem::MetricSink(ms) => Some((
1953 &mut ms.optimized_plan,
1954 &mut ms.physical_plan,
1955 &mut ms.dataflow_metainfo,
1956 )),
1957 _ => None,
1958 }
1959 }
1960
1961 pub fn is_storage_collection(&self) -> bool {
1963 match self {
1964 CatalogItem::Table(_)
1965 | CatalogItem::Source(_)
1966 | CatalogItem::MaterializedView(_)
1967 | CatalogItem::Sink(_) => true,
1968 CatalogItem::Log(_)
1969 | CatalogItem::View(_)
1970 | CatalogItem::Index(_)
1971 | CatalogItem::Type(_)
1972 | CatalogItem::Func(_)
1973 | CatalogItem::Secret(_)
1974 | CatalogItem::Connection(_)
1975 | CatalogItem::MetricSink(_) => false,
1976 }
1977 }
1978
1979 pub fn relation_desc(&self, version: RelationVersionSelector) -> Option<Cow<'_, RelationDesc>> {
1988 match &self {
1989 CatalogItem::Source(src) => Some(Cow::Borrowed(&src.desc)),
1990 CatalogItem::Log(log) => Some(Cow::Owned(log.variant.desc())),
1991 CatalogItem::Table(tbl) => Some(Cow::Owned(tbl.desc.at_version(version))),
1992 CatalogItem::View(view) => Some(Cow::Borrowed(&view.desc)),
1993 CatalogItem::MaterializedView(mview) => {
1994 Some(Cow::Owned(mview.desc.at_version(version)))
1995 }
1996 CatalogItem::Func(_)
1997 | CatalogItem::Index(_)
1998 | CatalogItem::Sink(_)
1999 | CatalogItem::Secret(_)
2000 | CatalogItem::Connection(_)
2001 | CatalogItem::Type(_)
2002 | CatalogItem::MetricSink(_) => None,
2003 }
2004 }
2005
2006 pub fn func(
2007 &self,
2008 entry: &CatalogEntry,
2009 ) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
2010 match &self {
2011 CatalogItem::Func(func) => Ok(func.inner),
2012 _ => Err(SqlCatalogError::UnexpectedType {
2013 name: entry.name().item.to_string(),
2014 actual_type: entry.item_type(),
2015 expected_type: CatalogItemType::Func,
2016 }),
2017 }
2018 }
2019
2020 pub fn source_desc(
2021 &self,
2022 entry: &CatalogEntry,
2023 ) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
2024 match &self {
2025 CatalogItem::Source(source) => match &source.data_source {
2026 DataSourceDesc::Ingestion { desc, .. }
2027 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => Ok(Some(desc)),
2028 DataSourceDesc::IngestionExport { .. }
2029 | DataSourceDesc::Introspection(_)
2030 | DataSourceDesc::Webhook { .. }
2031 | DataSourceDesc::Progress
2032 | DataSourceDesc::Catalog => Ok(None),
2033 },
2034 _ => Err(SqlCatalogError::UnexpectedType {
2035 name: entry.name().item.to_string(),
2036 actual_type: entry.item_type(),
2037 expected_type: CatalogItemType::Source,
2038 }),
2039 }
2040 }
2041
2042 pub fn is_progress_source(&self) -> bool {
2044 matches!(
2045 self,
2046 CatalogItem::Source(Source {
2047 data_source: DataSourceDesc::Progress,
2048 ..
2049 })
2050 )
2051 }
2052
2053 pub fn references(&self) -> &ResolvedIds {
2056 static EMPTY: LazyLock<ResolvedIds> = LazyLock::new(ResolvedIds::empty);
2057 match self {
2058 CatalogItem::Func(_) => &*EMPTY,
2059 CatalogItem::Index(idx) => &idx.resolved_ids,
2060 CatalogItem::Sink(sink) => &sink.resolved_ids,
2061 CatalogItem::Source(source) => &source.resolved_ids,
2062 CatalogItem::Log(_) => &*EMPTY,
2063 CatalogItem::Table(table) => &table.resolved_ids,
2064 CatalogItem::Type(typ) => &typ.resolved_ids,
2065 CatalogItem::View(view) => &view.resolved_ids,
2066 CatalogItem::MaterializedView(mview) => &mview.resolved_ids,
2067 CatalogItem::Secret(_) => &*EMPTY,
2068 CatalogItem::Connection(connection) => &connection.resolved_ids,
2069 CatalogItem::MetricSink(metric_sink) => &metric_sink.resolved_ids,
2070 }
2071 }
2072
2073 pub fn uses(&self) -> BTreeSet<CatalogItemId> {
2079 let mut uses: BTreeSet<_> = self.references().items().copied().collect();
2080 match self {
2081 CatalogItem::Func(_) => {}
2084 CatalogItem::Index(_) => {}
2085 CatalogItem::Sink(_) => {}
2086 CatalogItem::Source(_) => {}
2087 CatalogItem::Log(_) => {}
2088 CatalogItem::Table(_) => {}
2089 CatalogItem::Type(_) => {}
2090 CatalogItem::View(view) => uses.extend(view.dependencies.0.iter().copied()),
2091 CatalogItem::MaterializedView(mview) => {
2092 uses.extend(mview.dependencies.0.iter().copied())
2093 }
2094 CatalogItem::Secret(_) => {}
2095 CatalogItem::Connection(_) => {}
2096 CatalogItem::MetricSink(_) => {}
2097 }
2098 uses
2099 }
2100
2101 pub fn query_dependencies(&self) -> BTreeSet<CatalogItemId> {
2107 match self {
2108 CatalogItem::Func(_) | CatalogItem::View(_) => self.uses(),
2109 CatalogItem::MaterializedView(mv) => {
2110 let mut dependencies = self.uses();
2111 if let Some(target) = mv.replacement_target {
2112 dependencies.remove(&target);
2113 }
2114 dependencies
2115 }
2116 CatalogItem::Index(_)
2117 | CatalogItem::Sink(_)
2118 | CatalogItem::Source(_)
2119 | CatalogItem::Log(_)
2120 | CatalogItem::Table(_)
2121 | CatalogItem::Type(_)
2122 | CatalogItem::Secret(_)
2123 | CatalogItem::Connection(_)
2124 | CatalogItem::MetricSink(_) => BTreeSet::new(),
2125 }
2126 }
2127
2128 pub fn conn_id(&self) -> Option<&ConnectionId> {
2131 match self {
2132 CatalogItem::View(view) => view.conn_id.as_ref(),
2133 CatalogItem::Index(index) => index.conn_id.as_ref(),
2134 CatalogItem::Table(table) => table.conn_id.as_ref(),
2135 CatalogItem::Log(_)
2136 | CatalogItem::Source(_)
2137 | CatalogItem::Sink(_)
2138 | CatalogItem::MaterializedView(_)
2139 | CatalogItem::Secret(_)
2140 | CatalogItem::Type(_)
2141 | CatalogItem::Func(_)
2142 | CatalogItem::Connection(_)
2143 | CatalogItem::MetricSink(_) => None,
2144 }
2145 }
2146
2147 pub fn set_conn_id(&mut self, conn_id: Option<ConnectionId>) {
2150 match self {
2151 CatalogItem::View(view) => view.conn_id = conn_id,
2152 CatalogItem::Index(index) => index.conn_id = conn_id,
2153 CatalogItem::Table(table) => table.conn_id = conn_id,
2154 CatalogItem::Log(_)
2155 | CatalogItem::Source(_)
2156 | CatalogItem::Sink(_)
2157 | CatalogItem::MaterializedView(_)
2158 | CatalogItem::Secret(_)
2159 | CatalogItem::Type(_)
2160 | CatalogItem::Func(_)
2161 | CatalogItem::Connection(_)
2162 | CatalogItem::MetricSink(_) => (),
2163 }
2164 }
2165
2166 pub fn set_create_sql(&mut self, create_sql: String) {
2175 match self {
2176 CatalogItem::View(view) => view.create_sql = create_sql,
2177 CatalogItem::Index(index) => index.create_sql = create_sql,
2178 CatalogItem::Table(table) => table.create_sql = Some(create_sql),
2179 CatalogItem::Log(_)
2180 | CatalogItem::Source(_)
2181 | CatalogItem::Sink(_)
2182 | CatalogItem::MaterializedView(_)
2183 | CatalogItem::Secret(_)
2184 | CatalogItem::Type(_)
2185 | CatalogItem::Func(_)
2186 | CatalogItem::Connection(_)
2187 | CatalogItem::MetricSink(_) => {
2188 unreachable!("only views, indexes, and tables can be temporary")
2189 }
2190 }
2191 }
2192
2193 pub fn is_temporary(&self) -> bool {
2195 self.conn_id().is_some()
2196 }
2197
2198 pub fn rename_schema_refs(
2199 &self,
2200 database_name: &str,
2201 cur_schema_name: &str,
2202 new_schema_name: &str,
2203 ) -> Result<CatalogItem, (String, String)> {
2204 let do_rewrite = |create_sql: String| -> Result<String, (String, String)> {
2205 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2206 .expect("invalid create sql persisted to catalog")
2207 .into_element()
2208 .ast;
2209
2210 mz_sql::ast::transform::create_stmt_rename_schema_refs(
2212 &mut create_stmt,
2213 database_name,
2214 cur_schema_name,
2215 new_schema_name,
2216 )?;
2217
2218 Ok(create_stmt.to_ast_string_stable())
2219 };
2220
2221 match self {
2222 CatalogItem::Table(i) => {
2223 let mut i = i.clone();
2224 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2225 Ok(CatalogItem::Table(i))
2226 }
2227 CatalogItem::Log(i) => Ok(CatalogItem::Log(i.clone())),
2228 CatalogItem::Source(i) => {
2229 let mut i = i.clone();
2230 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2231 Ok(CatalogItem::Source(i))
2232 }
2233 CatalogItem::Sink(i) => {
2234 let mut i = i.clone();
2235 i.create_sql = do_rewrite(i.create_sql)?;
2236 Ok(CatalogItem::Sink(i))
2237 }
2238 CatalogItem::View(i) => {
2239 let mut i = i.clone();
2240 i.create_sql = do_rewrite(i.create_sql)?;
2241 Ok(CatalogItem::View(i))
2242 }
2243 CatalogItem::MaterializedView(i) => {
2244 let mut i = i.clone();
2245 i.create_sql = do_rewrite(i.create_sql)?;
2246 Ok(CatalogItem::MaterializedView(i))
2247 }
2248 CatalogItem::Index(i) => {
2249 let mut i = i.clone();
2250 i.create_sql = do_rewrite(i.create_sql)?;
2251 Ok(CatalogItem::Index(i))
2252 }
2253 CatalogItem::Secret(i) => {
2254 let mut i = i.clone();
2255 i.create_sql = do_rewrite(i.create_sql)?;
2256 Ok(CatalogItem::Secret(i))
2257 }
2258 CatalogItem::Connection(i) => {
2259 let mut i = i.clone();
2260 i.create_sql = do_rewrite(i.create_sql)?;
2261 Ok(CatalogItem::Connection(i))
2262 }
2263 CatalogItem::Type(i) => {
2264 let mut i = i.clone();
2265 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2266 Ok(CatalogItem::Type(i))
2267 }
2268 CatalogItem::Func(i) => Ok(CatalogItem::Func(i.clone())),
2269 CatalogItem::MetricSink(i) => {
2270 let mut i = i.clone();
2271 i.create_sql = do_rewrite(i.create_sql)?;
2272 Ok(CatalogItem::MetricSink(i))
2273 }
2274 }
2275 }
2276
2277 pub fn rename_item_refs(
2281 &self,
2282 from: FullItemName,
2283 to_item_name: String,
2284 rename_self: bool,
2285 ) -> Result<CatalogItem, String> {
2286 let do_rewrite = |create_sql: String| -> Result<String, String> {
2287 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2288 .expect("invalid create sql persisted to catalog")
2289 .into_element()
2290 .ast;
2291 if rename_self {
2292 mz_sql::ast::transform::create_stmt_rename(&mut create_stmt, to_item_name.clone());
2293 }
2294 mz_sql::ast::transform::create_stmt_rename_refs(&mut create_stmt, from, to_item_name)?;
2296 Ok(create_stmt.to_ast_string_stable())
2297 };
2298
2299 match self {
2300 CatalogItem::Table(i) => {
2301 let mut i = i.clone();
2302 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2303 Ok(CatalogItem::Table(i))
2304 }
2305 CatalogItem::Log(i) => Ok(CatalogItem::Log(i.clone())),
2306 CatalogItem::Source(i) => {
2307 let mut i = i.clone();
2308 i.create_sql = i.create_sql.map(do_rewrite).transpose()?;
2309 Ok(CatalogItem::Source(i))
2310 }
2311 CatalogItem::Sink(i) => {
2312 let mut i = i.clone();
2313 i.create_sql = do_rewrite(i.create_sql)?;
2314 Ok(CatalogItem::Sink(i))
2315 }
2316 CatalogItem::View(i) => {
2317 let mut i = i.clone();
2318 i.create_sql = do_rewrite(i.create_sql)?;
2319 Ok(CatalogItem::View(i))
2320 }
2321 CatalogItem::MaterializedView(i) => {
2322 let mut i = i.clone();
2323 i.create_sql = do_rewrite(i.create_sql)?;
2324 Ok(CatalogItem::MaterializedView(i))
2325 }
2326 CatalogItem::Index(i) => {
2327 let mut i = i.clone();
2328 i.create_sql = do_rewrite(i.create_sql)?;
2329 Ok(CatalogItem::Index(i))
2330 }
2331 CatalogItem::Secret(i) => {
2332 let mut i = i.clone();
2333 i.create_sql = do_rewrite(i.create_sql)?;
2334 Ok(CatalogItem::Secret(i))
2335 }
2336 CatalogItem::Func(_) | CatalogItem::Type(_) => {
2337 unreachable!("{}s cannot be renamed", self.typ())
2338 }
2339 CatalogItem::Connection(i) => {
2340 let mut i = i.clone();
2341 i.create_sql = do_rewrite(i.create_sql)?;
2342 Ok(CatalogItem::Connection(i))
2343 }
2344 CatalogItem::MetricSink(i) => {
2345 let mut i = i.clone();
2346 i.create_sql = do_rewrite(i.create_sql)?;
2347 Ok(CatalogItem::MetricSink(i))
2348 }
2349 }
2350 }
2351
2352 pub fn replace_item_refs(&self, old_id: CatalogItemId, new_id: CatalogItemId) -> CatalogItem {
2354 let do_rewrite = |create_sql: String| -> String {
2355 let mut create_stmt = mz_sql::parse::parse(&create_sql)
2356 .expect("invalid create sql persisted to catalog")
2357 .into_element()
2358 .ast;
2359 mz_sql::ast::transform::create_stmt_replace_ids(
2360 &mut create_stmt,
2361 &[(old_id, new_id)].into(),
2362 );
2363 create_stmt.to_ast_string_stable()
2364 };
2365
2366 match self {
2367 CatalogItem::Table(i) => {
2368 let mut i = i.clone();
2369 i.create_sql = i.create_sql.map(do_rewrite);
2370 CatalogItem::Table(i)
2371 }
2372 CatalogItem::Log(i) => CatalogItem::Log(i.clone()),
2373 CatalogItem::Source(i) => {
2374 let mut i = i.clone();
2375 i.create_sql = i.create_sql.map(do_rewrite);
2376 CatalogItem::Source(i)
2377 }
2378 CatalogItem::Sink(i) => {
2379 let mut i = i.clone();
2380 i.create_sql = do_rewrite(i.create_sql);
2381 CatalogItem::Sink(i)
2382 }
2383 CatalogItem::View(i) => {
2384 let mut i = i.clone();
2385 i.create_sql = do_rewrite(i.create_sql);
2386 CatalogItem::View(i)
2387 }
2388 CatalogItem::MaterializedView(i) => {
2389 let mut i = i.clone();
2390 i.create_sql = do_rewrite(i.create_sql);
2391 CatalogItem::MaterializedView(i)
2392 }
2393 CatalogItem::Index(i) => {
2394 let mut i = i.clone();
2395 i.create_sql = do_rewrite(i.create_sql);
2396 CatalogItem::Index(i)
2397 }
2398 CatalogItem::Secret(i) => {
2399 let mut i = i.clone();
2400 i.create_sql = do_rewrite(i.create_sql);
2401 CatalogItem::Secret(i)
2402 }
2403 CatalogItem::Func(_) | CatalogItem::Type(_) => {
2404 unreachable!("references of {}s cannot be replaced", self.typ())
2405 }
2406 CatalogItem::Connection(i) => {
2407 let mut i = i.clone();
2408 i.create_sql = do_rewrite(i.create_sql);
2409 CatalogItem::Connection(i)
2410 }
2411 CatalogItem::MetricSink(i) => {
2412 let mut i = i.clone();
2413 i.create_sql = do_rewrite(i.create_sql);
2414 CatalogItem::MetricSink(i)
2415 }
2416 }
2417 }
2418 pub fn update_retain_history(
2421 &mut self,
2422 value: Option<Value>,
2423 window: CompactionWindow,
2424 ) -> Result<Option<WithOptionValue<Raw>>, ()> {
2425 let update = |mut ast: &mut Statement<Raw>| {
2426 macro_rules! update_retain_history {
2428 ( $stmt:ident, $opt:ident, $name:ident ) => {{
2429 let pos = $stmt
2431 .with_options
2432 .iter()
2433 .rposition(|o| o.name == mz_sql_parser::ast::$name::RetainHistory);
2435 if let Some(value) = value {
2436 let next = mz_sql_parser::ast::$opt {
2437 name: mz_sql_parser::ast::$name::RetainHistory,
2438 value: Some(WithOptionValue::RetainHistoryFor(value)),
2439 };
2440 if let Some(idx) = pos {
2441 let previous = $stmt.with_options[idx].clone();
2442 $stmt.with_options[idx] = next;
2443 previous.value
2444 } else {
2445 $stmt.with_options.push(next);
2446 None
2447 }
2448 } else {
2449 if let Some(idx) = pos {
2450 $stmt.with_options.swap_remove(idx).value
2451 } else {
2452 None
2453 }
2454 }
2455 }};
2456 }
2457 let previous = match &mut ast {
2458 Statement::CreateTable(stmt) => {
2459 update_retain_history!(stmt, TableOption, TableOptionName)
2460 }
2461 Statement::CreateIndex(stmt) => {
2462 update_retain_history!(stmt, IndexOption, IndexOptionName)
2463 }
2464 Statement::CreateSource(stmt) => {
2465 update_retain_history!(stmt, CreateSourceOption, CreateSourceOptionName)
2466 }
2467 Statement::CreateMaterializedView(stmt) => {
2468 update_retain_history!(stmt, MaterializedViewOption, MaterializedViewOptionName)
2469 }
2470 _ => {
2471 return Err(());
2472 }
2473 };
2474 Ok(previous)
2475 };
2476
2477 let res = self.update_sql(update)?;
2478 let cw = self
2479 .custom_logical_compaction_window_mut()
2480 .expect("item must have compaction window");
2481 *cw = Some(window);
2482 Ok(res)
2483 }
2484
2485 pub fn update_timestamp_interval(
2488 &mut self,
2489 value: Option<Value>,
2490 interval: Duration,
2491 ) -> Result<Option<WithOptionValue<Raw>>, ()> {
2492 let update = |ast: &mut Statement<Raw>| match ast {
2493 Statement::CreateSource(stmt) => {
2494 let pos = stmt.with_options.iter().rposition(|o| {
2495 o.name == mz_sql_parser::ast::CreateSourceOptionName::TimestampInterval
2496 });
2497 let previous = if let Some(value) = value {
2498 let next = mz_sql_parser::ast::CreateSourceOption {
2499 name: mz_sql_parser::ast::CreateSourceOptionName::TimestampInterval,
2500 value: Some(WithOptionValue::Value(value)),
2501 };
2502 if let Some(idx) = pos {
2503 let previous = stmt.with_options[idx].clone();
2504 stmt.with_options[idx] = next;
2505 previous.value
2506 } else {
2507 stmt.with_options.push(next);
2508 None
2509 }
2510 } else if let Some(idx) = pos {
2511 stmt.with_options.swap_remove(idx).value
2512 } else {
2513 None
2514 };
2515 Ok(previous)
2516 }
2517 _ => Err(()),
2518 };
2519
2520 let previous = self.update_sql(update)?;
2521
2522 match self {
2524 CatalogItem::Source(source) => {
2525 match &mut source.data_source {
2526 DataSourceDesc::Ingestion { desc, .. }
2527 | DataSourceDesc::OldSyntaxIngestion { desc, .. } => {
2528 desc.timestamp_interval = interval;
2529 }
2530 _ => return Err(()),
2531 }
2532 Ok(previous)
2533 }
2534 _ => Err(()),
2535 }
2536 }
2537
2538 pub fn add_column(
2539 &mut self,
2540 name: ColumnName,
2541 typ: SqlColumnType,
2542 sql: RawDataType,
2543 ) -> Result<RelationVersion, PlanError> {
2544 let CatalogItem::Table(table) = self else {
2545 return Err(PlanError::Unsupported {
2546 feature: "adding columns to a non-Table".to_string(),
2547 discussion_no: None,
2548 });
2549 };
2550 let next_version = table.desc.add_column(name.clone(), typ);
2551
2552 let update = |mut ast: &mut Statement<Raw>| match &mut ast {
2553 Statement::CreateTable(stmt) => {
2554 let version = ColumnOptionDef {
2555 name: None,
2556 option: ColumnOption::Versioned {
2557 action: ColumnVersioned::Added,
2558 version: mz_sql::normalize::ast_version(next_version),
2559 },
2560 };
2561 let column = ColumnDef {
2562 name: mz_sql::normalize::column_name_ident(&name),
2563 data_type: sql,
2564 collation: None,
2565 options: vec![version],
2566 };
2567 stmt.columns.push(column);
2568 Ok(())
2569 }
2570 _ => Err(()),
2571 };
2572
2573 self.update_sql(update)
2574 .map_err(|()| PlanError::Unstructured("expected CREATE TABLE statement".to_string()))?;
2575 Ok(next_version)
2576 }
2577
2578 pub fn update_sql<F, T>(&mut self, f: F) -> Result<T, ()>
2581 where
2582 F: FnOnce(&mut Statement<Raw>) -> Result<T, ()>,
2583 {
2584 let create_sql = match self {
2585 CatalogItem::Table(Table { create_sql, .. })
2586 | CatalogItem::Type(Type { create_sql, .. })
2587 | CatalogItem::Source(Source { create_sql, .. }) => create_sql.as_mut(),
2588 CatalogItem::Sink(Sink { create_sql, .. })
2589 | CatalogItem::View(View { create_sql, .. })
2590 | CatalogItem::MaterializedView(MaterializedView { create_sql, .. })
2591 | CatalogItem::Index(Index { create_sql, .. })
2592 | CatalogItem::Secret(Secret { create_sql, .. })
2593 | CatalogItem::Connection(Connection { create_sql, .. })
2594 | CatalogItem::MetricSink(MetricSink { create_sql, .. }) => Some(create_sql),
2595 CatalogItem::Func(_) | CatalogItem::Log(_) => None,
2596 };
2597 let Some(create_sql) = create_sql else {
2598 return Err(());
2599 };
2600 let mut ast = mz_sql_parser::parser::parse_statements(create_sql)
2601 .expect("non-system items must be parseable")
2602 .into_element()
2603 .ast;
2604 debug!("rewrite: {}", ast.to_ast_string_redacted());
2605 let t = f(&mut ast)?;
2606 *create_sql = ast.to_ast_string_stable();
2607 debug!("rewrote: {}", ast.to_ast_string_redacted());
2608 Ok(t)
2609 }
2610
2611 pub fn is_compute_object_on_cluster(&self) -> Option<ClusterId> {
2618 match self {
2619 CatalogItem::Index(index) => Some(index.cluster_id),
2620 CatalogItem::MetricSink(metric_sink) => Some(metric_sink.cluster_id),
2621 CatalogItem::Table(_)
2622 | CatalogItem::Source(_)
2623 | CatalogItem::Log(_)
2624 | CatalogItem::View(_)
2625 | CatalogItem::MaterializedView(_)
2626 | CatalogItem::Sink(_)
2627 | CatalogItem::Type(_)
2628 | CatalogItem::Func(_)
2629 | CatalogItem::Secret(_)
2630 | CatalogItem::Connection(_) => None,
2631 }
2632 }
2633
2634 pub fn is_hydratable(&self) -> bool {
2644 match self {
2645 CatalogItem::Index(_)
2646 | CatalogItem::MaterializedView(_)
2647 | CatalogItem::Sink(_)
2648 | CatalogItem::MetricSink(_) => true,
2649 CatalogItem::Source(source) => matches!(
2650 source.data_source,
2651 DataSourceDesc::Ingestion { .. } | DataSourceDesc::OldSyntaxIngestion { .. }
2652 ),
2653 CatalogItem::Table(_)
2654 | CatalogItem::Log(_)
2655 | CatalogItem::View(_)
2656 | CatalogItem::Type(_)
2657 | CatalogItem::Func(_)
2658 | CatalogItem::Secret(_)
2659 | CatalogItem::Connection(_) => false,
2660 }
2661 }
2662
2663 pub fn cluster_id(&self) -> Option<ClusterId> {
2664 match self {
2665 CatalogItem::MaterializedView(mv) => Some(mv.cluster_id),
2666 CatalogItem::Index(index) => Some(index.cluster_id),
2667 CatalogItem::MetricSink(metric_sink) => Some(metric_sink.cluster_id),
2668 CatalogItem::Source(source) => match &source.data_source {
2669 DataSourceDesc::Ingestion { cluster_id, .. }
2670 | DataSourceDesc::OldSyntaxIngestion { cluster_id, .. } => Some(*cluster_id),
2671 DataSourceDesc::IngestionExport { .. } => None,
2675 DataSourceDesc::Webhook { cluster_id, .. } => Some(*cluster_id),
2676 DataSourceDesc::Introspection(_)
2677 | DataSourceDesc::Progress
2678 | DataSourceDesc::Catalog => None,
2679 },
2680 CatalogItem::Sink(sink) => Some(sink.cluster_id),
2681 CatalogItem::Table(_)
2682 | CatalogItem::Log(_)
2683 | CatalogItem::View(_)
2684 | CatalogItem::Type(_)
2685 | CatalogItem::Func(_)
2686 | CatalogItem::Secret(_)
2687 | CatalogItem::Connection(_) => None,
2688 }
2689 }
2690
2691 pub fn custom_logical_compaction_window(&self) -> Option<CompactionWindow> {
2694 match self {
2695 CatalogItem::Table(table) => table.custom_logical_compaction_window,
2696 CatalogItem::Source(source) => source.custom_logical_compaction_window,
2697 CatalogItem::Index(index) => index.custom_logical_compaction_window,
2698 CatalogItem::MaterializedView(mview) => mview.custom_logical_compaction_window,
2699 CatalogItem::Log(_)
2700 | CatalogItem::View(_)
2701 | CatalogItem::Sink(_)
2702 | CatalogItem::Type(_)
2703 | CatalogItem::Func(_)
2704 | CatalogItem::Secret(_)
2705 | CatalogItem::Connection(_)
2706 | CatalogItem::MetricSink(_) => None,
2707 }
2708 }
2709
2710 pub fn custom_logical_compaction_window_mut(
2714 &mut self,
2715 ) -> Option<&mut Option<CompactionWindow>> {
2716 let cw = match self {
2717 CatalogItem::Table(table) => &mut table.custom_logical_compaction_window,
2718 CatalogItem::Source(source) => &mut source.custom_logical_compaction_window,
2719 CatalogItem::Index(index) => &mut index.custom_logical_compaction_window,
2720 CatalogItem::MaterializedView(mview) => &mut mview.custom_logical_compaction_window,
2721 CatalogItem::Log(_)
2722 | CatalogItem::View(_)
2723 | CatalogItem::Sink(_)
2724 | CatalogItem::Type(_)
2725 | CatalogItem::Func(_)
2726 | CatalogItem::Secret(_)
2727 | CatalogItem::Connection(_)
2728 | CatalogItem::MetricSink(_) => return None,
2729 };
2730 Some(cw)
2731 }
2732
2733 pub fn initial_logical_compaction_window(&self) -> Option<CompactionWindow> {
2741 let custom_logical_compaction_window = match self {
2742 CatalogItem::Table(_)
2743 | CatalogItem::Source(_)
2744 | CatalogItem::Index(_)
2745 | CatalogItem::MaterializedView(_) => self.custom_logical_compaction_window(),
2746 CatalogItem::Log(_)
2747 | CatalogItem::View(_)
2748 | CatalogItem::Sink(_)
2749 | CatalogItem::Type(_)
2750 | CatalogItem::Func(_)
2751 | CatalogItem::Secret(_)
2752 | CatalogItem::Connection(_)
2753 | CatalogItem::MetricSink(_) => return None,
2754 };
2755 Some(custom_logical_compaction_window.unwrap_or(CompactionWindow::Default))
2756 }
2757
2758 pub fn is_retained_metrics_object(&self) -> bool {
2762 match self {
2763 CatalogItem::Table(table) => table.is_retained_metrics_object,
2764 CatalogItem::Source(source) => source.is_retained_metrics_object,
2765 CatalogItem::Index(index) => index.is_retained_metrics_object,
2766 CatalogItem::Log(_)
2767 | CatalogItem::View(_)
2768 | CatalogItem::MaterializedView(_)
2769 | CatalogItem::Sink(_)
2770 | CatalogItem::Type(_)
2771 | CatalogItem::Func(_)
2772 | CatalogItem::Secret(_)
2773 | CatalogItem::Connection(_)
2774 | CatalogItem::MetricSink(_) => false,
2775 }
2776 }
2777
2778 pub fn to_serialized(&self) -> (String, GlobalId, BTreeMap<RelationVersion, GlobalId>) {
2779 match self {
2780 CatalogItem::Table(table) => {
2781 let create_sql = table
2782 .create_sql
2783 .clone()
2784 .expect("builtin tables cannot be serialized");
2785 let mut collections = table.collections.clone();
2786 let global_id = collections
2787 .remove(&RelationVersion::root())
2788 .expect("at least one version");
2789 (create_sql, global_id, collections)
2790 }
2791 CatalogItem::Log(_) => unreachable!("builtin logs cannot be serialized"),
2792 CatalogItem::Source(source) => {
2793 assert!(
2794 !matches!(source.data_source, DataSourceDesc::Introspection(_)),
2795 "cannot serialize introspection/builtin sources",
2796 );
2797 let create_sql = source
2798 .create_sql
2799 .clone()
2800 .expect("builtin sources cannot be serialized");
2801 (create_sql, source.global_id, BTreeMap::new())
2802 }
2803 CatalogItem::View(view) => (view.create_sql.clone(), view.global_id, BTreeMap::new()),
2804 CatalogItem::MaterializedView(mview) => {
2805 let mut collections = mview.collections.clone();
2806 let global_id = collections
2807 .remove(&RelationVersion::root())
2808 .expect("at least one version");
2809 (mview.create_sql.clone(), global_id, collections)
2810 }
2811 CatalogItem::Index(index) => {
2812 (index.create_sql.clone(), index.global_id, BTreeMap::new())
2813 }
2814 CatalogItem::Sink(sink) => (sink.create_sql.clone(), sink.global_id, BTreeMap::new()),
2815 CatalogItem::Type(typ) => {
2816 let create_sql = typ
2817 .create_sql
2818 .clone()
2819 .expect("builtin types cannot be serialized");
2820 (create_sql, typ.global_id, BTreeMap::new())
2821 }
2822 CatalogItem::Secret(secret) => {
2823 (secret.create_sql.clone(), secret.global_id, BTreeMap::new())
2824 }
2825 CatalogItem::Connection(connection) => (
2826 connection.create_sql.clone(),
2827 connection.global_id,
2828 BTreeMap::new(),
2829 ),
2830 CatalogItem::Func(_) => unreachable!("cannot serialize functions yet"),
2831 CatalogItem::MetricSink(ms) => (ms.create_sql.clone(), ms.global_id, BTreeMap::new()),
2832 }
2833 }
2834
2835 pub fn into_serialized(self) -> (String, GlobalId, BTreeMap<RelationVersion, GlobalId>) {
2836 match self {
2837 CatalogItem::Table(mut table) => {
2838 let create_sql = table
2839 .create_sql
2840 .expect("builtin tables cannot be serialized");
2841 let global_id = table
2842 .collections
2843 .remove(&RelationVersion::root())
2844 .expect("at least one version");
2845 (create_sql, global_id, table.collections)
2846 }
2847 CatalogItem::Log(_) => unreachable!("builtin logs cannot be serialized"),
2848 CatalogItem::Source(source) => {
2849 assert!(
2850 !matches!(source.data_source, DataSourceDesc::Introspection(_)),
2851 "cannot serialize introspection/builtin sources",
2852 );
2853 let create_sql = source
2854 .create_sql
2855 .expect("builtin sources cannot be serialized");
2856 (create_sql, source.global_id, BTreeMap::new())
2857 }
2858 CatalogItem::View(view) => (view.create_sql, view.global_id, BTreeMap::new()),
2859 CatalogItem::MaterializedView(mut mview) => {
2860 let global_id = mview
2861 .collections
2862 .remove(&RelationVersion::root())
2863 .expect("at least one version");
2864 (mview.create_sql, global_id, mview.collections)
2865 }
2866 CatalogItem::Index(index) => (index.create_sql, index.global_id, BTreeMap::new()),
2867 CatalogItem::Sink(sink) => (sink.create_sql, sink.global_id, BTreeMap::new()),
2868 CatalogItem::Type(typ) => {
2869 let create_sql = typ.create_sql.expect("builtin types cannot be serialized");
2870 (create_sql, typ.global_id, BTreeMap::new())
2871 }
2872 CatalogItem::Secret(secret) => (secret.create_sql, secret.global_id, BTreeMap::new()),
2873 CatalogItem::Connection(connection) => {
2874 (connection.create_sql, connection.global_id, BTreeMap::new())
2875 }
2876 CatalogItem::Func(_) => unreachable!("cannot serialize functions yet"),
2877 CatalogItem::MetricSink(ms) => (ms.create_sql, ms.global_id, BTreeMap::new()),
2878 }
2879 }
2880
2881 pub fn global_id_for_version(&self, version: RelationVersionSelector) -> Option<GlobalId> {
2884 let collections = match self {
2885 CatalogItem::MaterializedView(mv) => &mv.collections,
2886 CatalogItem::Table(table) => &table.collections,
2887 CatalogItem::Source(source) => return Some(source.global_id),
2888 CatalogItem::Log(log) => return Some(log.global_id),
2889 CatalogItem::View(view) => return Some(view.global_id),
2890 CatalogItem::Sink(sink) => return Some(sink.global_id),
2891 CatalogItem::Index(index) => return Some(index.global_id),
2892 CatalogItem::Type(ty) => return Some(ty.global_id),
2893 CatalogItem::Func(func) => return Some(func.global_id),
2894 CatalogItem::Secret(secret) => return Some(secret.global_id),
2895 CatalogItem::Connection(conn) => return Some(conn.global_id),
2896 CatalogItem::MetricSink(metric_sink) => return Some(metric_sink.global_id),
2897 };
2898 match version {
2899 RelationVersionSelector::Latest => collections.values().last().copied(),
2900 RelationVersionSelector::Specific(version) => collections.get(&version).copied(),
2901 }
2902 }
2903}
2904
2905impl CatalogEntry {
2906 pub fn relation_desc_latest(&self) -> Option<Cow<'_, RelationDesc>> {
2909 self.item.relation_desc(RelationVersionSelector::Latest)
2910 }
2911
2912 pub fn has_columns(&self) -> bool {
2914 match self.item() {
2915 CatalogItem::Type(Type { details, .. }) => {
2916 matches!(details.typ, CatalogType::Record { .. })
2917 }
2918 _ => self.relation_desc_latest().is_some(),
2919 }
2920 }
2921
2922 pub fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
2924 self.item.func(self)
2925 }
2926
2927 pub fn index(&self) -> Option<&Index> {
2929 match self.item() {
2930 CatalogItem::Index(idx) => Some(idx),
2931 _ => None,
2932 }
2933 }
2934
2935 pub fn materialized_view(&self) -> Option<&MaterializedView> {
2937 match self.item() {
2938 CatalogItem::MaterializedView(mv) => Some(mv),
2939 _ => None,
2940 }
2941 }
2942
2943 pub fn table(&self) -> Option<&Table> {
2945 match self.item() {
2946 CatalogItem::Table(tbl) => Some(tbl),
2947 _ => None,
2948 }
2949 }
2950
2951 pub fn source(&self) -> Option<&Source> {
2953 match self.item() {
2954 CatalogItem::Source(src) => Some(src),
2955 _ => None,
2956 }
2957 }
2958
2959 pub fn sink(&self) -> Option<&Sink> {
2961 match self.item() {
2962 CatalogItem::Sink(sink) => Some(sink),
2963 _ => None,
2964 }
2965 }
2966
2967 pub fn secret(&self) -> Option<&Secret> {
2969 match self.item() {
2970 CatalogItem::Secret(secret) => Some(secret),
2971 _ => None,
2972 }
2973 }
2974
2975 pub fn connection(&self) -> Result<&Connection, SqlCatalogError> {
2976 match self.item() {
2977 CatalogItem::Connection(connection) => Ok(connection),
2978 _ => {
2979 let db_name = match self.name().qualifiers.database_spec {
2980 ResolvedDatabaseSpecifier::Ambient => "".to_string(),
2981 ResolvedDatabaseSpecifier::Id(id) => format!("{id}."),
2982 };
2983 Err(SqlCatalogError::UnknownConnection(format!(
2984 "{}{}.{}",
2985 db_name,
2986 self.name().qualifiers.schema_spec,
2987 self.name().item
2988 )))
2989 }
2990 }
2991 }
2992
2993 pub fn source_desc(
2996 &self,
2997 ) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
2998 self.item.source_desc(self)
2999 }
3000
3001 pub fn is_connection(&self) -> bool {
3003 matches!(self.item(), CatalogItem::Connection(_))
3004 }
3005
3006 pub fn is_table(&self) -> bool {
3008 matches!(self.item(), CatalogItem::Table(_))
3009 }
3010
3011 pub fn is_source(&self) -> bool {
3014 matches!(self.item(), CatalogItem::Source(_))
3015 }
3016
3017 pub fn subsource_details(
3020 &self,
3021 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
3022 match &self.item() {
3023 CatalogItem::Source(source) => match &source.data_source {
3024 DataSourceDesc::IngestionExport {
3025 ingestion_id,
3026 external_reference,
3027 details,
3028 data_config: _,
3029 } => Some((*ingestion_id, external_reference, details)),
3030 _ => None,
3031 },
3032 _ => None,
3033 }
3034 }
3035
3036 pub fn source_export_details(
3039 &self,
3040 ) -> Option<(
3041 CatalogItemId,
3042 &UnresolvedItemName,
3043 &SourceExportDetails,
3044 &SourceExportDataConfig<ReferencedConnection>,
3045 )> {
3046 match &self.item() {
3047 CatalogItem::Source(source) => match &source.data_source {
3048 DataSourceDesc::IngestionExport {
3049 ingestion_id,
3050 external_reference,
3051 details,
3052 data_config,
3053 } => Some((*ingestion_id, external_reference, details, data_config)),
3054 _ => None,
3055 },
3056 CatalogItem::Table(table) => match &table.data_source {
3057 TableDataSource::DataSource {
3058 desc:
3059 DataSourceDesc::IngestionExport {
3060 ingestion_id,
3061 external_reference,
3062 details,
3063 data_config,
3064 },
3065 timeline: _,
3066 } => Some((*ingestion_id, external_reference, details, data_config)),
3067 _ => None,
3068 },
3069 _ => None,
3070 }
3071 }
3072
3073 pub fn is_progress_source(&self) -> bool {
3075 self.item().is_progress_source()
3076 }
3077
3078 pub fn progress_id(&self) -> Option<CatalogItemId> {
3080 match &self.item() {
3081 CatalogItem::Source(source) => match &source.data_source {
3082 DataSourceDesc::Ingestion { .. } => Some(self.id),
3083 DataSourceDesc::OldSyntaxIngestion {
3084 progress_subsource, ..
3085 } => Some(*progress_subsource),
3086 DataSourceDesc::IngestionExport { .. }
3087 | DataSourceDesc::Introspection(_)
3088 | DataSourceDesc::Progress
3089 | DataSourceDesc::Webhook { .. }
3090 | DataSourceDesc::Catalog => None,
3091 },
3092 CatalogItem::Table(_)
3093 | CatalogItem::Log(_)
3094 | CatalogItem::View(_)
3095 | CatalogItem::MaterializedView(_)
3096 | CatalogItem::Sink(_)
3097 | CatalogItem::Index(_)
3098 | CatalogItem::Type(_)
3099 | CatalogItem::Func(_)
3100 | CatalogItem::Secret(_)
3101 | CatalogItem::Connection(_)
3102 | CatalogItem::MetricSink(_) => None,
3103 }
3104 }
3105
3106 pub fn is_sink(&self) -> bool {
3108 matches!(self.item(), CatalogItem::Sink(_))
3109 }
3110
3111 pub fn is_materialized_view(&self) -> bool {
3113 matches!(self.item(), CatalogItem::MaterializedView(_))
3114 }
3115
3116 pub fn is_view(&self) -> bool {
3118 matches!(self.item(), CatalogItem::View(_))
3119 }
3120
3121 pub fn is_secret(&self) -> bool {
3123 matches!(self.item(), CatalogItem::Secret(_))
3124 }
3125
3126 pub fn is_introspection_source(&self) -> bool {
3128 matches!(self.item(), CatalogItem::Log(_))
3129 }
3130
3131 pub fn is_index(&self) -> bool {
3133 matches!(self.item(), CatalogItem::Index(_))
3134 }
3135
3136 pub fn is_metric_sink(&self) -> bool {
3138 matches!(self.item(), CatalogItem::MetricSink(_))
3139 }
3140
3141 pub fn is_relation(&self) -> bool {
3143 mz_sql::catalog::ObjectType::from(self.item_type()).is_relation()
3144 }
3145
3146 pub fn references(&self) -> &ResolvedIds {
3149 self.item.references()
3150 }
3151
3152 pub fn uses(&self) -> BTreeSet<CatalogItemId> {
3158 self.item.uses()
3159 }
3160
3161 pub fn item(&self) -> &CatalogItem {
3163 &self.item
3164 }
3165
3166 pub fn item_mut(&mut self) -> &mut CatalogItem {
3169 &mut self.item
3170 }
3171
3172 pub fn id(&self) -> CatalogItemId {
3174 self.id
3175 }
3176
3177 pub fn global_ids(&self) -> impl Iterator<Item = GlobalId> + '_ {
3179 self.item().global_ids()
3180 }
3181
3182 pub fn latest_global_id(&self) -> GlobalId {
3183 self.item().latest_global_id()
3184 }
3185
3186 pub fn oid(&self) -> u32 {
3188 self.oid
3189 }
3190
3191 pub fn name(&self) -> &QualifiedItemName {
3193 &self.name
3194 }
3195
3196 pub fn referenced_by(&self) -> &[CatalogItemId] {
3198 &self.referenced_by
3199 }
3200
3201 pub fn used_by(&self) -> &[CatalogItemId] {
3203 &self.used_by
3204 }
3205
3206 pub fn conn_id(&self) -> Option<&ConnectionId> {
3209 self.item.conn_id()
3210 }
3211
3212 pub fn owner_id(&self) -> &RoleId {
3214 &self.owner_id
3215 }
3216
3217 pub fn privileges(&self) -> &PrivilegeMap {
3219 &self.privileges
3220 }
3221
3222 pub fn comment_object_id(&self) -> CommentObjectId {
3224 use CatalogItemType::*;
3225 match self.item_type() {
3226 Table => CommentObjectId::Table(self.id),
3227 Source => CommentObjectId::Source(self.id),
3228 Sink => CommentObjectId::Sink(self.id),
3229 View => CommentObjectId::View(self.id),
3230 MaterializedView => CommentObjectId::MaterializedView(self.id),
3231 Index => CommentObjectId::Index(self.id),
3232 Func => CommentObjectId::Func(self.id),
3233 Connection => CommentObjectId::Connection(self.id),
3234 Type => CommentObjectId::Type(self.id),
3235 Secret => CommentObjectId::Secret(self.id),
3236 MetricSink => CommentObjectId::MetricSink(self.id),
3237 }
3238 }
3239}
3240
3241#[derive(Debug, Clone, Default)]
3242pub struct CommentsMap {
3243 map: BTreeMap<CommentObjectId, BTreeMap<Option<usize>, String>>,
3244}
3245
3246impl CommentsMap {
3247 pub fn update_comment(
3248 &mut self,
3249 object_id: CommentObjectId,
3250 sub_component: Option<usize>,
3251 comment: Option<String>,
3252 ) -> Option<String> {
3253 let object_comments = self.map.entry(object_id).or_default();
3254
3255 let (empty, prev) = if let Some(comment) = comment {
3257 let prev = object_comments.insert(sub_component, comment);
3258 (false, prev)
3259 } else {
3260 let prev = object_comments.remove(&sub_component);
3261 (object_comments.is_empty(), prev)
3262 };
3263
3264 if empty {
3266 self.map.remove(&object_id);
3267 }
3268
3269 prev
3271 }
3272
3273 pub fn drop_comments(
3279 &mut self,
3280 object_ids: &BTreeSet<CommentObjectId>,
3281 ) -> Vec<(CommentObjectId, Option<usize>, String)> {
3282 let mut removed_comments = Vec::new();
3283
3284 for object_id in object_ids {
3285 if let Some(comments) = self.map.remove(object_id) {
3286 let removed = comments
3287 .into_iter()
3288 .map(|(sub_comp, comment)| (object_id.clone(), sub_comp, comment));
3289 removed_comments.extend(removed);
3290 }
3291 }
3292
3293 removed_comments
3294 }
3295
3296 pub fn iter(&self) -> impl Iterator<Item = (CommentObjectId, Option<usize>, &str)> {
3297 self.map
3298 .iter()
3299 .map(|(id, comments)| {
3300 comments
3301 .iter()
3302 .map(|(pos, comment)| (*id, *pos, comment.as_str()))
3303 })
3304 .flatten()
3305 }
3306
3307 pub fn get_object_comments(
3308 &self,
3309 object_id: CommentObjectId,
3310 ) -> Option<&BTreeMap<Option<usize>, String>> {
3311 self.map.get(&object_id)
3312 }
3313}
3314
3315impl Serialize for CommentsMap {
3316 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3317 where
3318 S: serde::Serializer,
3319 {
3320 let comment_count = self
3321 .map
3322 .iter()
3323 .map(|(_object_id, comments)| comments.len())
3324 .sum();
3325
3326 let mut seq = serializer.serialize_seq(Some(comment_count))?;
3327 for (object_id, sub) in &self.map {
3328 for (sub_component, comment) in sub {
3329 seq.serialize_element(&(
3330 format!("{object_id:?}"),
3331 format!("{sub_component:?}"),
3332 comment,
3333 ))?;
3334 }
3335 }
3336 seq.end()
3337 }
3338}
3339
3340#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Default)]
3341pub struct DefaultPrivileges {
3342 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
3343 privileges: BTreeMap<DefaultPrivilegeObject, RoleDefaultPrivileges>,
3344}
3345
3346#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Default)]
3349struct RoleDefaultPrivileges(
3350 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
3352 BTreeMap<RoleId, DefaultPrivilegeAclItem>,
3353);
3354
3355impl Deref for RoleDefaultPrivileges {
3356 type Target = BTreeMap<RoleId, DefaultPrivilegeAclItem>;
3357
3358 fn deref(&self) -> &Self::Target {
3359 &self.0
3360 }
3361}
3362
3363impl DerefMut for RoleDefaultPrivileges {
3364 fn deref_mut(&mut self) -> &mut Self::Target {
3365 &mut self.0
3366 }
3367}
3368
3369impl DefaultPrivileges {
3370 pub fn grant(&mut self, object: DefaultPrivilegeObject, privilege: DefaultPrivilegeAclItem) {
3372 if privilege.acl_mode.is_empty() {
3373 return;
3374 }
3375
3376 let privileges = self.privileges.entry(object).or_default();
3377 if let Some(default_privilege) = privileges.get_mut(&privilege.grantee) {
3378 default_privilege.acl_mode |= privilege.acl_mode;
3379 } else {
3380 privileges.insert(privilege.grantee, privilege);
3381 }
3382 }
3383
3384 pub fn revoke(&mut self, object: &DefaultPrivilegeObject, privilege: &DefaultPrivilegeAclItem) {
3386 if let Some(privileges) = self.privileges.get_mut(object) {
3387 if let Some(default_privilege) = privileges.get_mut(&privilege.grantee) {
3388 default_privilege.acl_mode =
3389 default_privilege.acl_mode.difference(privilege.acl_mode);
3390 if default_privilege.acl_mode.is_empty() {
3391 privileges.remove(&privilege.grantee);
3392 }
3393 }
3394 if privileges.is_empty() {
3395 self.privileges.remove(object);
3396 }
3397 }
3398 }
3399
3400 pub fn get_privileges_for_grantee(
3403 &self,
3404 object: &DefaultPrivilegeObject,
3405 grantee: &RoleId,
3406 ) -> Option<&AclMode> {
3407 self.privileges
3408 .get(object)
3409 .and_then(|privileges| privileges.get(grantee))
3410 .map(|privilege| &privilege.acl_mode)
3411 }
3412
3413 pub fn get_applicable_privileges(
3415 &self,
3416 role_id: RoleId,
3417 database_id: Option<DatabaseId>,
3418 schema_id: Option<SchemaId>,
3419 object_type: mz_sql::catalog::ObjectType,
3420 ) -> impl Iterator<Item = DefaultPrivilegeAclItem> + '_ {
3421 let privilege_object_type = if object_type.is_relation() {
3425 mz_sql::catalog::ObjectType::Table
3426 } else {
3427 object_type
3428 };
3429 let valid_acl_mode = rbac::all_object_privileges(SystemObjectType::Object(object_type));
3430
3431 [
3435 DefaultPrivilegeObject {
3436 role_id,
3437 database_id,
3438 schema_id,
3439 object_type: privilege_object_type,
3440 },
3441 DefaultPrivilegeObject {
3442 role_id,
3443 database_id,
3444 schema_id: None,
3445 object_type: privilege_object_type,
3446 },
3447 DefaultPrivilegeObject {
3448 role_id,
3449 database_id: None,
3450 schema_id: None,
3451 object_type: privilege_object_type,
3452 },
3453 DefaultPrivilegeObject {
3454 role_id: RoleId::Public,
3455 database_id,
3456 schema_id,
3457 object_type: privilege_object_type,
3458 },
3459 DefaultPrivilegeObject {
3460 role_id: RoleId::Public,
3461 database_id,
3462 schema_id: None,
3463 object_type: privilege_object_type,
3464 },
3465 DefaultPrivilegeObject {
3466 role_id: RoleId::Public,
3467 database_id: None,
3468 schema_id: None,
3469 object_type: privilege_object_type,
3470 },
3471 ]
3472 .into_iter()
3473 .filter_map(|object| self.privileges.get(&object))
3474 .flat_map(|acl_map| acl_map.values())
3475 .fold(
3477 BTreeMap::new(),
3478 |mut accum, DefaultPrivilegeAclItem { grantee, acl_mode }| {
3479 let accum_acl_mode = accum.entry(grantee).or_insert_with(AclMode::empty);
3480 *accum_acl_mode |= *acl_mode;
3481 accum
3482 },
3483 )
3484 .into_iter()
3485 .map(move |(grantee, acl_mode)| (grantee, acl_mode & valid_acl_mode))
3490 .filter(|(_, acl_mode)| !acl_mode.is_empty())
3492 .map(|(grantee, acl_mode)| DefaultPrivilegeAclItem {
3493 grantee: *grantee,
3494 acl_mode,
3495 })
3496 }
3497
3498 pub fn iter(
3499 &self,
3500 ) -> impl Iterator<
3501 Item = (
3502 &DefaultPrivilegeObject,
3503 impl Iterator<Item = &DefaultPrivilegeAclItem>,
3504 ),
3505 > {
3506 self.privileges
3507 .iter()
3508 .map(|(object, acl_map)| (object, acl_map.values()))
3509 }
3510}
3511
3512#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3513pub struct ClusterConfig {
3514 pub variant: ClusterVariant,
3515 pub workload_class: Option<String>,
3516}
3517
3518impl ClusterConfig {
3519 pub fn features(&self) -> Option<&OptimizerFeatureOverrides> {
3520 match &self.variant {
3521 ClusterVariant::Managed(managed) => Some(&managed.optimizer_feature_overrides),
3522 ClusterVariant::Unmanaged => None,
3523 }
3524 }
3525}
3526
3527impl From<ClusterConfig> for durable::ClusterConfig {
3528 fn from(config: ClusterConfig) -> Self {
3529 Self {
3530 variant: config.variant.into(),
3531 workload_class: config.workload_class,
3532 }
3533 }
3534}
3535
3536impl From<durable::ClusterConfig> for ClusterConfig {
3537 fn from(config: durable::ClusterConfig) -> Self {
3538 Self {
3539 variant: config.variant.into(),
3540 workload_class: config.workload_class,
3541 }
3542 }
3543}
3544
3545#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3546pub struct ClusterVariantManaged {
3547 pub size: String,
3548 pub availability_zones: Vec<String>,
3549 pub logging: ReplicaLogging,
3550 pub arrangement_compression: bool,
3552 pub replication_factor: u32,
3553 pub optimizer_feature_overrides: OptimizerFeatureOverrides,
3554 pub schedule: ClusterSchedule,
3555 pub auto_scaling_strategy: Option<AutoScalingStrategy>,
3558 pub reconfiguration: Option<ReconfigurationState>,
3560 pub burst: Option<BurstState>,
3562}
3563
3564#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3566pub struct ManagedReplicaConfigShape<'a> {
3567 pub size: &'a str,
3568 pub availability_zones: &'a [String],
3569 pub logging: &'a ReplicaLogging,
3570 pub arrangement_compression: bool,
3571}
3572
3573impl<'a> ManagedReplicaConfigShape<'a> {
3574 pub fn new(
3576 size: &'a str,
3577 availability_zones: &'a [String],
3578 logging: &'a ReplicaLogging,
3579 arrangement_compression: bool,
3580 ) -> Self {
3581 Self {
3582 size,
3583 availability_zones,
3584 logging,
3585 arrangement_compression,
3586 }
3587 }
3588}
3589
3590impl ClusterVariantManaged {
3591 pub fn replica_config_shape(&self) -> ManagedReplicaConfigShape<'_> {
3593 let ClusterVariantManaged {
3594 size,
3595 availability_zones,
3596 logging,
3597 arrangement_compression,
3598 replication_factor: _,
3599 optimizer_feature_overrides: _,
3600 schedule: _,
3601 auto_scaling_strategy: _,
3602 reconfiguration: _,
3603 burst: _,
3604 } = self;
3605 ManagedReplicaConfigShape::new(size, availability_zones, logging, *arrangement_compression)
3606 }
3607
3608 pub fn realized_reconfiguration_target(&self) -> ReconfigurationTarget {
3610 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 } = self;
3622 ReconfigurationTarget {
3623 size: size.clone(),
3624 replication_factor: *replication_factor,
3625 availability_zones: availability_zones.clone(),
3626 logging: logging.clone(),
3627 arrangement_compression: *arrangement_compression,
3628 }
3629 }
3630
3631 pub fn apply_reconfiguration_target(&mut self, target: ReconfigurationTarget) {
3635 let ReconfigurationTarget {
3638 size,
3639 replication_factor,
3640 availability_zones,
3641 logging,
3642 arrangement_compression,
3643 } = target;
3644 self.size = size;
3645 self.replication_factor = replication_factor;
3646 self.availability_zones = availability_zones;
3647 self.logging = logging;
3648 self.arrangement_compression = arrangement_compression;
3649 }
3650
3651 pub fn has_unwarranted_burst_record(&self) -> bool {
3656 let Some(record) = &self.burst else {
3657 return false;
3658 };
3659 let hydration_size = self
3660 .auto_scaling_strategy
3661 .as_ref()
3662 .and_then(|strategy| strategy.on_hydration.as_ref())
3663 .map(|policy| policy.hydration_size.as_str());
3664 !mz_adapter_types::cluster_state::burst_record_warranted(
3665 &record.burst_size,
3666 self.replication_factor,
3667 hydration_size,
3668 )
3669 }
3670}
3671
3672impl From<ClusterVariantManaged> for durable::ClusterVariantManaged {
3673 fn from(managed: ClusterVariantManaged) -> Self {
3674 let ClusterVariantManaged {
3677 size,
3678 availability_zones,
3679 logging,
3680 arrangement_compression,
3681 replication_factor,
3682 optimizer_feature_overrides,
3683 schedule,
3684 auto_scaling_strategy,
3685 reconfiguration,
3686 burst,
3687 } = managed;
3688 Self {
3689 size,
3690 availability_zones,
3691 logging,
3692 arrangement_compression,
3693 replication_factor,
3694 optimizer_feature_overrides: optimizer_feature_overrides.into(),
3695 schedule,
3696 auto_scaling_strategy,
3697 reconfiguration: reconfiguration.map(Into::into),
3698 burst: burst.map(Into::into),
3699 }
3700 }
3701}
3702
3703impl From<durable::ClusterVariantManaged> for ClusterVariantManaged {
3704 fn from(managed: durable::ClusterVariantManaged) -> Self {
3705 let durable::ClusterVariantManaged {
3708 size,
3709 availability_zones,
3710 logging,
3711 arrangement_compression,
3712 replication_factor,
3713 optimizer_feature_overrides,
3714 schedule,
3715 auto_scaling_strategy,
3716 reconfiguration,
3717 burst,
3718 } = managed;
3719 Self {
3720 size,
3721 availability_zones,
3722 logging,
3723 arrangement_compression,
3724 replication_factor,
3725 optimizer_feature_overrides: optimizer_feature_overrides.into(),
3726 schedule,
3727 auto_scaling_strategy,
3728 reconfiguration: reconfiguration.map(Into::into),
3729 burst: burst.map(Into::into),
3730 }
3731 }
3732}
3733
3734#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3741pub struct ReconfigurationState {
3742 pub target: ReconfigurationTarget,
3743 pub deadline: Timestamp,
3744 pub on_timeout: OnTimeoutAction,
3745 pub status: ReconfigurationStatus,
3746}
3747
3748#[derive(
3767 Clone,
3768 Copy,
3769 Debug,
3770 Deserialize,
3771 Serialize,
3772 PartialOrd,
3773 PartialEq,
3774 Eq,
3775 Ord
3776)]
3777pub enum ReconfigurationStatus {
3778 InProgress,
3779 Finalized,
3780 TimedOut,
3781 Cancelled,
3782 ResourceExhausted,
3783}
3784
3785impl From<ReconfigurationStatus> for durable::ReconfigurationStatus {
3786 fn from(status: ReconfigurationStatus) -> Self {
3787 match status {
3788 ReconfigurationStatus::InProgress => durable::ReconfigurationStatus::InProgress,
3789 ReconfigurationStatus::Finalized => durable::ReconfigurationStatus::Finalized,
3790 ReconfigurationStatus::TimedOut => durable::ReconfigurationStatus::TimedOut,
3791 ReconfigurationStatus::Cancelled => durable::ReconfigurationStatus::Cancelled,
3792 ReconfigurationStatus::ResourceExhausted => {
3793 durable::ReconfigurationStatus::ResourceExhausted
3794 }
3795 }
3796 }
3797}
3798
3799impl From<durable::ReconfigurationStatus> for ReconfigurationStatus {
3800 fn from(status: durable::ReconfigurationStatus) -> Self {
3801 match status {
3802 durable::ReconfigurationStatus::InProgress => ReconfigurationStatus::InProgress,
3803 durable::ReconfigurationStatus::Finalized => ReconfigurationStatus::Finalized,
3804 durable::ReconfigurationStatus::TimedOut => ReconfigurationStatus::TimedOut,
3805 durable::ReconfigurationStatus::Cancelled => ReconfigurationStatus::Cancelled,
3806 durable::ReconfigurationStatus::ResourceExhausted => {
3807 ReconfigurationStatus::ResourceExhausted
3808 }
3809 }
3810 }
3811}
3812
3813impl ReconfigurationState {
3814 pub fn is_in_progress(&self) -> bool {
3815 matches!(self.status, ReconfigurationStatus::InProgress)
3816 }
3817}
3818
3819impl From<ReconfigurationState> for durable::ReconfigurationState {
3820 fn from(state: ReconfigurationState) -> Self {
3821 let ReconfigurationState {
3824 target,
3825 deadline,
3826 on_timeout,
3827 status,
3828 } = state;
3829 Self {
3830 target: target.into(),
3831 deadline,
3832 on_timeout,
3833 status: status.into(),
3834 }
3835 }
3836}
3837
3838impl From<durable::ReconfigurationState> for ReconfigurationState {
3839 fn from(state: durable::ReconfigurationState) -> Self {
3840 let durable::ReconfigurationState {
3843 target,
3844 deadline,
3845 on_timeout,
3846 status,
3847 } = state;
3848 Self {
3849 target: target.into(),
3850 deadline,
3851 on_timeout,
3852 status: status.into(),
3853 }
3854 }
3855}
3856
3857#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3859pub struct ReconfigurationTarget {
3860 pub size: String,
3861 pub replication_factor: u32,
3862 pub availability_zones: Vec<String>,
3863 pub logging: ReplicaLogging,
3864 pub arrangement_compression: bool,
3865}
3866
3867impl ReconfigurationTarget {
3868 pub fn matches_realized_config(&self, managed: &ClusterVariantManaged) -> bool {
3870 self == &managed.realized_reconfiguration_target()
3871 }
3872}
3873
3874impl From<ReconfigurationTarget> for durable::ReconfigurationTarget {
3875 fn from(target: ReconfigurationTarget) -> Self {
3876 let ReconfigurationTarget {
3879 size,
3880 replication_factor,
3881 availability_zones,
3882 logging,
3883 arrangement_compression,
3884 } = target;
3885 Self {
3886 size,
3887 replication_factor,
3888 availability_zones,
3889 logging,
3890 arrangement_compression,
3891 }
3892 }
3893}
3894
3895impl From<durable::ReconfigurationTarget> for ReconfigurationTarget {
3896 fn from(target: durable::ReconfigurationTarget) -> Self {
3897 let durable::ReconfigurationTarget {
3900 size,
3901 replication_factor,
3902 availability_zones,
3903 logging,
3904 arrangement_compression,
3905 } = target;
3906 Self {
3907 size,
3908 replication_factor,
3909 availability_zones,
3910 logging,
3911 arrangement_compression,
3912 }
3913 }
3914}
3915
3916#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3918pub struct BurstState {
3919 pub burst_size: String,
3920 pub linger_duration: Duration,
3921 pub steady_ready_at: Option<Timestamp>,
3922}
3923
3924impl From<BurstState> for durable::BurstState {
3925 fn from(burst: BurstState) -> Self {
3926 let BurstState {
3929 burst_size,
3930 linger_duration,
3931 steady_ready_at,
3932 } = burst;
3933 Self {
3934 burst_size,
3935 linger_duration,
3936 steady_ready_at,
3937 }
3938 }
3939}
3940
3941impl From<durable::BurstState> for BurstState {
3942 fn from(burst: durable::BurstState) -> Self {
3943 let durable::BurstState {
3946 burst_size,
3947 linger_duration,
3948 steady_ready_at,
3949 } = burst;
3950 Self {
3951 burst_size,
3952 linger_duration,
3953 steady_ready_at,
3954 }
3955 }
3956}
3957
3958#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
3959pub enum ClusterVariant {
3960 Managed(ClusterVariantManaged),
3961 Unmanaged,
3962}
3963
3964impl From<ClusterVariant> for durable::ClusterVariant {
3965 fn from(variant: ClusterVariant) -> Self {
3966 match variant {
3967 ClusterVariant::Managed(managed) => Self::Managed(managed.into()),
3968 ClusterVariant::Unmanaged => Self::Unmanaged,
3969 }
3970 }
3971}
3972
3973impl From<durable::ClusterVariant> for ClusterVariant {
3974 fn from(variant: durable::ClusterVariant) -> Self {
3975 match variant {
3976 durable::ClusterVariant::Managed(managed) => Self::Managed(managed.into()),
3977 durable::ClusterVariant::Unmanaged => Self::Unmanaged,
3978 }
3979 }
3980}
3981
3982impl mz_sql::catalog::CatalogDatabase for Database {
3983 fn name(&self) -> &str {
3984 &self.name
3985 }
3986
3987 fn id(&self) -> DatabaseId {
3988 self.id
3989 }
3990
3991 fn has_schemas(&self) -> bool {
3992 !self.schemas_by_name.is_empty()
3993 }
3994
3995 fn schema_ids(&self) -> &BTreeMap<String, SchemaId> {
3996 &self.schemas_by_name
3997 }
3998
3999 #[allow(clippy::as_conversions)]
4001 fn schemas(&self) -> Vec<&dyn CatalogSchema> {
4002 self.schemas_by_id
4003 .values()
4004 .map(|schema| schema as &dyn CatalogSchema)
4005 .collect()
4006 }
4007
4008 fn owner_id(&self) -> RoleId {
4009 self.owner_id
4010 }
4011
4012 fn privileges(&self) -> &PrivilegeMap {
4013 &self.privileges
4014 }
4015}
4016
4017impl mz_sql::catalog::CatalogSchema for Schema {
4018 fn database(&self) -> &ResolvedDatabaseSpecifier {
4019 &self.name.database
4020 }
4021
4022 fn name(&self) -> &QualifiedSchemaName {
4023 &self.name
4024 }
4025
4026 fn id(&self) -> &SchemaSpecifier {
4027 &self.id
4028 }
4029
4030 fn has_items(&self) -> bool {
4031 !self.items.is_empty() || !self.types.is_empty() || !self.functions.is_empty()
4035 }
4036
4037 fn item_ids(&self) -> Box<dyn Iterator<Item = CatalogItemId> + '_> {
4038 Box::new(
4039 self.items
4040 .values()
4041 .chain(self.functions.values())
4042 .chain(self.types.values())
4043 .copied(),
4044 )
4045 }
4046
4047 fn owner_id(&self) -> RoleId {
4048 self.owner_id
4049 }
4050
4051 fn privileges(&self) -> &PrivilegeMap {
4052 &self.privileges
4053 }
4054}
4055
4056impl mz_sql::catalog::CatalogRole for Role {
4057 fn name(&self) -> &str {
4058 &self.name
4059 }
4060
4061 fn id(&self) -> RoleId {
4062 self.id
4063 }
4064
4065 fn membership(&self) -> &BTreeMap<RoleId, RoleId> {
4066 &self.membership.map
4067 }
4068
4069 fn attributes(&self) -> &RoleAttributes {
4070 &self.attributes
4071 }
4072
4073 fn vars(&self) -> &BTreeMap<String, OwnedVarInput> {
4074 &self.vars.map
4075 }
4076}
4077
4078impl mz_sql::catalog::CatalogNetworkPolicy for NetworkPolicy {
4079 fn name(&self) -> &str {
4080 &self.name
4081 }
4082
4083 fn id(&self) -> NetworkPolicyId {
4084 self.id
4085 }
4086
4087 fn owner_id(&self) -> RoleId {
4088 self.owner_id
4089 }
4090
4091 fn privileges(&self) -> &PrivilegeMap {
4092 &self.privileges
4093 }
4094}
4095
4096impl mz_sql::catalog::CatalogCluster<'_> for Cluster {
4097 fn name(&self) -> &str {
4098 &self.name
4099 }
4100
4101 fn id(&self) -> ClusterId {
4102 self.id
4103 }
4104
4105 fn bound_objects(&self) -> &BTreeSet<CatalogItemId> {
4106 &self.bound_objects
4107 }
4108
4109 fn replica_ids(&self) -> &BTreeMap<String, ReplicaId> {
4110 &self.replica_id_by_name_
4111 }
4112
4113 #[allow(clippy::as_conversions)]
4115 fn replicas(&self) -> Vec<&dyn CatalogClusterReplica<'_>> {
4116 self.replicas()
4117 .map(|replica| replica as &dyn CatalogClusterReplica)
4118 .collect()
4119 }
4120
4121 fn replica(&self, id: ReplicaId) -> &dyn CatalogClusterReplica<'_> {
4122 self.replica(id).expect("catalog out of sync")
4123 }
4124
4125 fn owner_id(&self) -> RoleId {
4126 self.owner_id
4127 }
4128
4129 fn privileges(&self) -> &PrivilegeMap {
4130 &self.privileges
4131 }
4132
4133 fn is_managed(&self) -> bool {
4134 self.is_managed()
4135 }
4136
4137 fn managed_size(&self) -> Option<&str> {
4138 match &self.config.variant {
4139 ClusterVariant::Managed(ClusterVariantManaged { size, .. }) => Some(size),
4140 ClusterVariant::Unmanaged => None,
4141 }
4142 }
4143
4144 fn schedule(&self) -> Option<&ClusterSchedule> {
4145 match &self.config.variant {
4146 ClusterVariant::Managed(ClusterVariantManaged { schedule, .. }) => Some(schedule),
4147 ClusterVariant::Unmanaged => None,
4148 }
4149 }
4150
4151 fn replication_factor(&self) -> Option<u32> {
4152 match &self.config.variant {
4153 ClusterVariant::Managed(ClusterVariantManaged {
4154 replication_factor, ..
4155 }) => Some(*replication_factor),
4156 ClusterVariant::Unmanaged => None,
4157 }
4158 }
4159
4160 fn auto_scaling_strategy(&self) -> Option<&AutoScalingStrategy> {
4161 match &self.config.variant {
4162 ClusterVariant::Managed(ClusterVariantManaged {
4163 auto_scaling_strategy,
4164 ..
4165 }) => auto_scaling_strategy.as_ref(),
4166 ClusterVariant::Unmanaged => None,
4167 }
4168 }
4169 fn try_to_plan(&self) -> Result<CreateClusterPlan, PlanError> {
4170 self.try_to_plan()
4171 }
4172}
4173
4174impl mz_sql::catalog::CatalogClusterReplica<'_> for ClusterReplica {
4175 fn name(&self) -> &str {
4176 &self.name
4177 }
4178
4179 fn cluster_id(&self) -> ClusterId {
4180 self.cluster_id
4181 }
4182
4183 fn replica_id(&self) -> ReplicaId {
4184 self.replica_id
4185 }
4186
4187 fn owner_id(&self) -> RoleId {
4188 self.owner_id
4189 }
4190
4191 fn internal(&self) -> bool {
4192 self.config.location.internal()
4193 }
4194}
4195
4196impl mz_sql::catalog::CatalogItem for CatalogEntry {
4197 fn name(&self) -> &QualifiedItemName {
4198 self.name()
4199 }
4200
4201 fn id(&self) -> CatalogItemId {
4202 self.id()
4203 }
4204
4205 fn global_ids(&self) -> Box<dyn Iterator<Item = GlobalId> + '_> {
4206 Box::new(self.global_ids())
4207 }
4208
4209 fn oid(&self) -> u32 {
4210 self.oid()
4211 }
4212
4213 fn func(&self) -> Result<&'static mz_sql::func::Func, SqlCatalogError> {
4214 self.func()
4215 }
4216
4217 fn source_desc(&self) -> Result<Option<&SourceDesc<ReferencedConnection>>, SqlCatalogError> {
4218 self.source_desc()
4219 }
4220
4221 fn connection(
4222 &self,
4223 ) -> Result<mz_storage_types::connections::Connection<ReferencedConnection>, SqlCatalogError>
4224 {
4225 Ok(self.connection()?.details.to_connection())
4226 }
4227
4228 fn create_sql(&self) -> &str {
4229 match self.item() {
4230 CatalogItem::Table(Table { create_sql, .. }) => {
4231 create_sql.as_deref().unwrap_or("<builtin>")
4232 }
4233 CatalogItem::Source(Source { create_sql, .. }) => {
4234 create_sql.as_deref().unwrap_or("<builtin>")
4235 }
4236 CatalogItem::Sink(Sink { create_sql, .. }) => create_sql,
4237 CatalogItem::View(View { create_sql, .. }) => create_sql,
4238 CatalogItem::MaterializedView(MaterializedView { create_sql, .. }) => create_sql,
4239 CatalogItem::Index(Index { create_sql, .. }) => create_sql,
4240 CatalogItem::Type(Type { create_sql, .. }) => {
4241 create_sql.as_deref().unwrap_or("<builtin>")
4242 }
4243 CatalogItem::Secret(Secret { create_sql, .. }) => create_sql,
4244 CatalogItem::Connection(Connection { create_sql, .. }) => create_sql,
4245 CatalogItem::MetricSink(MetricSink { create_sql, .. }) => create_sql,
4246 CatalogItem::Func(_) => "<builtin>",
4247 CatalogItem::Log(_) => "<builtin>",
4248 }
4249 }
4250
4251 fn item_type(&self) -> SqlCatalogItemType {
4252 self.item().typ()
4253 }
4254
4255 fn index_details(&self) -> Option<(&[MirScalarExpr], GlobalId)> {
4256 if let CatalogItem::Index(Index { keys, on, .. }) = self.item() {
4257 Some((keys, *on))
4258 } else {
4259 None
4260 }
4261 }
4262
4263 fn writable_table_details(&self) -> Option<&[Expr<Aug>]> {
4264 if let CatalogItem::Table(Table {
4265 data_source: TableDataSource::TableWrites { defaults },
4266 ..
4267 }) = self.item()
4268 {
4269 Some(defaults.as_slice())
4270 } else {
4271 None
4272 }
4273 }
4274
4275 fn replacement_target(&self) -> Option<CatalogItemId> {
4276 if let CatalogItem::MaterializedView(mv) = self.item() {
4277 mv.replacement_target
4278 } else {
4279 None
4280 }
4281 }
4282
4283 fn type_details(&self) -> Option<&CatalogTypeDetails<IdReference>> {
4284 if let CatalogItem::Type(Type { details, .. }) = self.item() {
4285 Some(details)
4286 } else {
4287 None
4288 }
4289 }
4290
4291 fn references(&self) -> &ResolvedIds {
4292 self.references()
4293 }
4294
4295 fn uses(&self) -> BTreeSet<CatalogItemId> {
4296 self.uses()
4297 }
4298
4299 fn referenced_by(&self) -> &[CatalogItemId] {
4300 self.referenced_by()
4301 }
4302
4303 fn used_by(&self) -> &[CatalogItemId] {
4304 self.used_by()
4305 }
4306
4307 fn subsource_details(
4308 &self,
4309 ) -> Option<(CatalogItemId, &UnresolvedItemName, &SourceExportDetails)> {
4310 self.subsource_details()
4311 }
4312
4313 fn source_export_details(
4314 &self,
4315 ) -> Option<(
4316 CatalogItemId,
4317 &UnresolvedItemName,
4318 &SourceExportDetails,
4319 &SourceExportDataConfig<ReferencedConnection>,
4320 )> {
4321 self.source_export_details()
4322 }
4323
4324 fn is_progress_source(&self) -> bool {
4325 self.is_progress_source()
4326 }
4327
4328 fn progress_id(&self) -> Option<CatalogItemId> {
4329 self.progress_id()
4330 }
4331
4332 fn owner_id(&self) -> RoleId {
4333 self.owner_id
4334 }
4335
4336 fn privileges(&self) -> &PrivilegeMap {
4337 &self.privileges
4338 }
4339
4340 fn cluster_id(&self) -> Option<ClusterId> {
4341 self.item().cluster_id()
4342 }
4343
4344 fn at_version(
4345 &self,
4346 version: RelationVersionSelector,
4347 ) -> Box<dyn mz_sql::catalog::CatalogCollectionItem> {
4348 Box::new(CatalogCollectionEntry {
4349 entry: self.clone(),
4350 version,
4351 })
4352 }
4353
4354 fn latest_version(&self) -> Option<RelationVersion> {
4355 self.table().map(|t| t.desc.latest_version())
4356 }
4357}
4358
4359#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4361pub struct StateUpdate {
4362 pub kind: StateUpdateKind,
4363 pub ts: Timestamp,
4364 pub diff: StateDiff,
4365}
4366
4367#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4371pub enum StateUpdateKind {
4372 Role(durable::objects::Role),
4373 RoleAuth(durable::objects::RoleAuth),
4374 Database(durable::objects::Database),
4375 Schema(durable::objects::Schema),
4376 DefaultPrivilege(durable::objects::DefaultPrivilege),
4377 SystemPrivilege(MzAclItem),
4378 SystemConfiguration(durable::objects::SystemConfiguration),
4379 Cluster(durable::objects::Cluster),
4380 ClusterSystemConfiguration(durable::objects::ClusterSystemConfiguration),
4381 NetworkPolicy(durable::objects::NetworkPolicy),
4382 ClusterReplicaSize(durable::objects::ClusterReplicaSize),
4383 IntrospectionSourceIndex(durable::objects::IntrospectionSourceIndex),
4384 ClusterReplica(durable::objects::ClusterReplica),
4385 ReplicaSystemConfiguration(durable::objects::ReplicaSystemConfiguration),
4386 SourceReferences(durable::objects::SourceReferences),
4387 SystemObjectMapping(durable::objects::SystemObjectMapping),
4388 Item(durable::objects::Item),
4389 Comment(durable::objects::Comment),
4390 AuditLog(durable::objects::AuditLog),
4391 StorageCollectionMetadata(durable::objects::StorageCollectionMetadata),
4393 UnfinalizedShard(durable::objects::UnfinalizedShard),
4394}
4395
4396#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
4398pub enum StateDiff {
4399 Retraction,
4400 Addition,
4401}
4402
4403impl From<StateDiff> for Diff {
4404 fn from(diff: StateDiff) -> Self {
4405 match diff {
4406 StateDiff::Retraction => Diff::MINUS_ONE,
4407 StateDiff::Addition => Diff::ONE,
4408 }
4409 }
4410}
4411impl TryFrom<Diff> for StateDiff {
4412 type Error = String;
4413
4414 fn try_from(diff: Diff) -> Result<Self, Self::Error> {
4415 match diff {
4416 Diff::MINUS_ONE => Ok(Self::Retraction),
4417 Diff::ONE => Ok(Self::Addition),
4418 diff => Err(format!("invalid diff {diff}")),
4419 }
4420 }
4421}