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