1use std::collections::{BTreeMap, BTreeSet, VecDeque};
14use std::fmt::Debug;
15use std::iter;
16use std::str::FromStr;
17use std::sync::Arc;
18
19use differential_dataflow::consolidation::consolidate_updates;
20use futures::future;
21use itertools::{Either, Itertools};
22use mz_adapter_types::connection::ConnectionId;
23use mz_catalog::SYSTEM_CONN_ID;
24use mz_catalog::builtin::{
25 BUILTIN_LOG_LOOKUP, BUILTIN_LOOKUP, Builtin, BuiltinLog, BuiltinTable, BuiltinView,
26};
27use mz_catalog::durable::objects::{
28 ClusterKey, DatabaseKey, DurableType, ItemKey, NetworkPolicyKey, RoleAuthKey, RoleKey,
29 SchemaKey,
30};
31use mz_catalog::durable::{CatalogError, SystemObjectMapping};
32use mz_catalog::memory::error::{Error, ErrorKind};
33use mz_catalog::memory::objects::{
34 CatalogEntry, CatalogItem, Cluster, ClusterReplica, Database, Func, Index, Log, NetworkPolicy,
35 Role, RoleAuth, Schema, Source, StateDiff, StateUpdate, StateUpdateKind, Table,
36 TableDataSource, TemporaryItem, Type, UpdateFrom,
37};
38use mz_compute_types::config::ComputeReplicaConfig;
39use mz_compute_types::dataflows::DataflowDescription;
40use mz_controller::clusters::{ReplicaConfig, ReplicaLogging};
41use mz_controller_types::ClusterId;
42use mz_expr::MirScalarExpr;
43use mz_ore::collections::CollectionExt;
44use mz_ore::tracing::OpenTelemetryContext;
45use mz_ore::{
46 instrument, soft_assert_eq_or_log, soft_assert_no_log, soft_assert_or_log, soft_panic_or_log,
47};
48use mz_pgrepr::oid::INVALID_OID;
49use mz_repr::adt::mz_acl_item::{MzAclItem, PrivilegeMap};
50use mz_repr::role_id::RoleId;
51use mz_repr::{CatalogItemId, Diff, GlobalId, RelationVersion, Timestamp, VersionedRelationDesc};
52use mz_sql::catalog::CatalogError as SqlCatalogError;
53use mz_sql::catalog::{CatalogItem as SqlCatalogItem, CatalogItemType, CatalogSchema, CatalogType};
54use mz_sql::names::{
55 FullItemName, ItemQualifiers, QualifiedItemName, RawDatabaseSpecifier,
56 ResolvedDatabaseSpecifier, ResolvedIds, SchemaSpecifier,
57};
58use mz_sql::session::user::MZ_SYSTEM_ROLE_ID;
59use mz_sql::session::vars::{VarError, VarInput};
60use mz_sql::{plan, rbac};
61use mz_sql_parser::ast::Expr;
62use mz_storage_types::sources::Timeline;
63use mz_transform::dataflow::DataflowMetainfo;
64use mz_transform::notice::OptimizerNotice;
65use tracing::{info_span, warn};
66
67use crate::AdapterError;
68use crate::catalog::state::LocalExpressionCache;
69use crate::catalog::{BuiltinTableUpdate, CatalogState};
70use crate::coord::catalog_implications::parsed_state_updates::{self, ParsedStateUpdate};
71use crate::util::{index_sql, sort_topological};
72
73#[derive(Debug, Clone, Default)]
85struct InProgressRetractions {
86 roles: BTreeMap<RoleKey, Role>,
87 role_auths: BTreeMap<RoleAuthKey, RoleAuth>,
88 databases: BTreeMap<DatabaseKey, Database>,
89 schemas: BTreeMap<SchemaKey, Schema>,
90 clusters: BTreeMap<ClusterKey, Cluster>,
91 network_policies: BTreeMap<NetworkPolicyKey, NetworkPolicy>,
92 items: BTreeMap<ItemKey, CatalogEntry>,
93 temp_items: BTreeMap<CatalogItemId, CatalogEntry>,
94 introspection_source_indexes: BTreeMap<CatalogItemId, CatalogEntry>,
95 system_object_mappings: BTreeMap<CatalogItemId, CatalogEntry>,
96}
97
98impl CatalogState {
99 #[must_use]
103 #[instrument]
104 pub(crate) async fn apply_updates(
105 &mut self,
106 updates: Vec<StateUpdate>,
107 local_expression_cache: &mut LocalExpressionCache,
108 ) -> (
109 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
110 Vec<ParsedStateUpdate>,
111 ) {
112 let mut builtin_table_updates = Vec::with_capacity(updates.len());
113 let mut catalog_updates = Vec::with_capacity(updates.len());
114
115 let updates = Self::consolidate_updates(updates);
120
121 let mut groups: Vec<Vec<_>> = Vec::new();
123 for (_, updates) in &updates.into_iter().chunk_by(|update| update.ts) {
124 let updates = sort_updates(updates.collect());
128 groups.push(updates);
129 }
130
131 for updates in groups {
132 let mut apply_state = ApplyState::Updates(Vec::new());
133 let mut retractions = InProgressRetractions::default();
134
135 for update in updates {
136 let (next_apply_state, (builtin_table_update, catalog_update)) = apply_state
137 .step(
138 ApplyState::new(update),
139 self,
140 &mut retractions,
141 local_expression_cache,
142 )
143 .await;
144 apply_state = next_apply_state;
145 builtin_table_updates.extend(builtin_table_update);
146 catalog_updates.extend(catalog_update);
147 }
148
149 let (builtin_table_update, catalog_update) = apply_state
151 .apply(self, &mut retractions, local_expression_cache)
152 .await;
153 builtin_table_updates.extend(builtin_table_update);
154 catalog_updates.extend(catalog_update);
155
156 let dropped_entries: Vec<CatalogEntry> = retractions
159 .items
160 .into_values()
161 .chain(retractions.temp_items.into_values())
162 .collect();
163 if !dropped_entries.is_empty() {
164 let dropped_notices = self.drop_optimizer_notices(dropped_entries);
165 if self.system_config().enable_mz_notices() {
166 self.pack_optimizer_notice_updates(
167 &mut builtin_table_updates,
168 dropped_notices.iter(),
169 Diff::MINUS_ONE,
170 );
171 }
172 }
173 }
174
175 (builtin_table_updates, catalog_updates)
176 }
177
178 fn consolidate_updates(updates: Vec<StateUpdate>) -> Vec<StateUpdate> {
186 let mut updates: Vec<(StateUpdateKind, Timestamp, mz_repr::Diff)> = updates
187 .into_iter()
188 .map(|update| (update.kind, update.ts, update.diff.into()))
189 .collect_vec();
190
191 consolidate_updates(&mut updates);
192
193 updates
194 .into_iter()
195 .map(|(kind, ts, diff)| StateUpdate {
196 kind,
197 ts,
198 diff: diff
199 .try_into()
200 .expect("catalog state cannot have diff other than -1 or 1"),
201 })
202 .collect_vec()
203 }
204
205 #[instrument(level = "debug")]
206 fn apply_updates_inner(
207 &mut self,
208 updates: Vec<StateUpdate>,
209 retractions: &mut InProgressRetractions,
210 local_expression_cache: &mut LocalExpressionCache,
211 ) -> Result<
212 (
213 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
214 Vec<ParsedStateUpdate>,
215 ),
216 CatalogError,
217 > {
218 soft_assert_no_log!(
219 updates.iter().map(|update| update.ts).all_equal(),
220 "all timestamps should be equal: {updates:?}"
221 );
222
223 let mut update_system_config = false;
224
225 let mut builtin_table_updates = Vec::with_capacity(updates.len());
226 let mut catalog_updates = Vec::new();
227
228 for state_update in updates {
229 if matches!(state_update.kind, StateUpdateKind::SystemConfiguration(_)) {
230 update_system_config = true;
231 }
232
233 match state_update.diff {
234 StateDiff::Retraction => {
235 if let Some(update) =
239 parsed_state_updates::parse_state_update(self, state_update.clone())
240 {
241 catalog_updates.push(update);
242 }
243
244 builtin_table_updates.extend(self.generate_builtin_table_update(
247 state_update.kind.clone(),
248 state_update.diff,
249 ));
250 self.apply_update(
251 state_update.kind,
252 state_update.diff,
253 retractions,
254 local_expression_cache,
255 )?;
256 }
257 StateDiff::Addition => {
258 self.apply_update(
259 state_update.kind.clone(),
260 state_update.diff,
261 retractions,
262 local_expression_cache,
263 )?;
264 builtin_table_updates.extend(self.generate_builtin_table_update(
268 state_update.kind.clone(),
269 state_update.diff,
270 ));
271
272 if let Some(update) =
275 parsed_state_updates::parse_state_update(self, state_update.clone())
276 {
277 catalog_updates.push(update);
278 }
279 }
280 }
281 }
282
283 if update_system_config {
284 self.system_configuration.dyncfg_updates();
285 }
286
287 Ok((builtin_table_updates, catalog_updates))
288 }
289
290 #[instrument(level = "debug")]
291 fn apply_update(
292 &mut self,
293 kind: StateUpdateKind,
294 diff: StateDiff,
295 retractions: &mut InProgressRetractions,
296 local_expression_cache: &mut LocalExpressionCache,
297 ) -> Result<(), CatalogError> {
298 match kind {
299 StateUpdateKind::Role(role) => {
300 self.apply_role_update(role, diff, retractions);
301 }
302 StateUpdateKind::RoleAuth(role_auth) => {
303 self.apply_role_auth_update(role_auth, diff, retractions);
304 }
305 StateUpdateKind::Database(database) => {
306 self.apply_database_update(database, diff, retractions);
307 }
308 StateUpdateKind::Schema(schema) => {
309 self.apply_schema_update(schema, diff, retractions);
310 }
311 StateUpdateKind::DefaultPrivilege(default_privilege) => {
312 self.apply_default_privilege_update(default_privilege, diff, retractions);
313 }
314 StateUpdateKind::SystemPrivilege(system_privilege) => {
315 self.apply_system_privilege_update(system_privilege, diff, retractions);
316 }
317 StateUpdateKind::SystemConfiguration(system_configuration) => {
318 self.apply_system_configuration_update(system_configuration, diff, retractions);
319 }
320 StateUpdateKind::ClusterSystemConfiguration(cfg) => {
321 Self::apply_scoped_system_configuration_update(
322 &mut self.scoped_system_parameters.cluster,
323 cfg.cluster_id,
324 cfg.name,
325 cfg.value,
326 diff,
327 );
328 }
329 StateUpdateKind::ReplicaSystemConfiguration(cfg) => {
330 Self::apply_scoped_system_configuration_update(
331 &mut self.scoped_system_parameters.replica,
332 cfg.replica_id,
333 cfg.name,
334 cfg.value,
335 diff,
336 );
337 }
338 StateUpdateKind::Cluster(cluster) => {
339 self.apply_cluster_update(cluster, diff, retractions);
340 }
341 StateUpdateKind::NetworkPolicy(network_policy) => {
342 self.apply_network_policy_update(network_policy, diff, retractions);
343 }
344 StateUpdateKind::IntrospectionSourceIndex(introspection_source_index) => {
345 self.apply_introspection_source_index_update(
346 introspection_source_index,
347 diff,
348 retractions,
349 );
350 }
351 StateUpdateKind::ClusterReplica(cluster_replica) => {
352 self.apply_cluster_replica_update(cluster_replica, diff, retractions);
353 }
354 StateUpdateKind::SystemObjectMapping(system_object_mapping) => {
355 self.apply_system_object_mapping_update(
356 system_object_mapping,
357 diff,
358 retractions,
359 local_expression_cache,
360 );
361 }
362 StateUpdateKind::TemporaryItem(item) => {
363 self.apply_temporary_item_update(item, diff, retractions, local_expression_cache);
364 }
365 StateUpdateKind::Item(item) => {
366 self.apply_item_update(item, diff, retractions, local_expression_cache)?;
367 }
368 StateUpdateKind::Comment(comment) => {
369 self.apply_comment_update(comment, diff, retractions);
370 }
371 StateUpdateKind::SourceReferences(source_reference) => {
372 self.apply_source_references_update(source_reference, diff, retractions);
373 }
374 StateUpdateKind::AuditLog(_audit_log) => {
375 }
377 StateUpdateKind::StorageCollectionMetadata(storage_collection_metadata) => {
378 self.apply_storage_collection_metadata_update(
379 storage_collection_metadata,
380 diff,
381 retractions,
382 );
383 }
384 StateUpdateKind::UnfinalizedShard(unfinalized_shard) => {
385 self.apply_unfinalized_shard_update(unfinalized_shard, diff, retractions);
386 }
387 }
388
389 Ok(())
390 }
391
392 #[instrument(level = "debug")]
393 fn apply_role_auth_update(
394 &mut self,
395 role_auth: mz_catalog::durable::RoleAuth,
396 diff: StateDiff,
397 retractions: &mut InProgressRetractions,
398 ) {
399 apply_with_update(
400 &mut self.role_auth_by_id,
401 role_auth,
402 |role_auth| role_auth.role_id,
403 diff,
404 &mut retractions.role_auths,
405 );
406 }
407
408 #[instrument(level = "debug")]
409 fn apply_role_update(
410 &mut self,
411 role: mz_catalog::durable::Role,
412 diff: StateDiff,
413 retractions: &mut InProgressRetractions,
414 ) {
415 apply_inverted_lookup(&mut self.roles_by_name, &role.name, role.id, diff);
416 apply_with_update(
417 &mut self.roles_by_id,
418 role,
419 |role| role.id,
420 diff,
421 &mut retractions.roles,
422 );
423 }
424
425 #[instrument(level = "debug")]
426 fn apply_database_update(
427 &mut self,
428 database: mz_catalog::durable::Database,
429 diff: StateDiff,
430 retractions: &mut InProgressRetractions,
431 ) {
432 apply_inverted_lookup(
433 &mut self.database_by_name,
434 &database.name,
435 database.id,
436 diff,
437 );
438 apply_with_update(
439 &mut self.database_by_id,
440 database,
441 |database| database.id,
442 diff,
443 &mut retractions.databases,
444 );
445 }
446
447 #[instrument(level = "debug")]
448 fn apply_schema_update(
449 &mut self,
450 schema: mz_catalog::durable::Schema,
451 diff: StateDiff,
452 retractions: &mut InProgressRetractions,
453 ) {
454 match &schema.database_id {
455 Some(database_id) => {
456 let db = self
457 .database_by_id
458 .get_mut(database_id)
459 .expect("catalog out of sync");
460 apply_inverted_lookup(&mut db.schemas_by_name, &schema.name, schema.id, diff);
461 apply_with_update(
462 &mut db.schemas_by_id,
463 schema,
464 |schema| schema.id,
465 diff,
466 &mut retractions.schemas,
467 );
468 }
469 None => {
470 apply_inverted_lookup(
471 &mut self.ambient_schemas_by_name,
472 &schema.name,
473 schema.id,
474 diff,
475 );
476 apply_with_update(
477 &mut self.ambient_schemas_by_id,
478 schema,
479 |schema| schema.id,
480 diff,
481 &mut retractions.schemas,
482 );
483 }
484 }
485 }
486
487 #[instrument(level = "debug")]
488 fn apply_default_privilege_update(
489 &mut self,
490 default_privilege: mz_catalog::durable::DefaultPrivilege,
491 diff: StateDiff,
492 _retractions: &mut InProgressRetractions,
493 ) {
494 match diff {
495 StateDiff::Addition => Arc::make_mut(&mut self.default_privileges)
496 .grant(default_privilege.object, default_privilege.acl_item),
497 StateDiff::Retraction => Arc::make_mut(&mut self.default_privileges)
498 .revoke(&default_privilege.object, &default_privilege.acl_item),
499 }
500 }
501
502 #[instrument(level = "debug")]
503 fn apply_system_privilege_update(
504 &mut self,
505 system_privilege: MzAclItem,
506 diff: StateDiff,
507 _retractions: &mut InProgressRetractions,
508 ) {
509 match diff {
510 StateDiff::Addition => {
511 Arc::make_mut(&mut self.system_privileges).grant(system_privilege)
512 }
513 StateDiff::Retraction => {
514 Arc::make_mut(&mut self.system_privileges).revoke(&system_privilege)
515 }
516 }
517 }
518
519 #[instrument(level = "debug")]
520 fn apply_system_configuration_update(
521 &mut self,
522 system_configuration: mz_catalog::durable::SystemConfiguration,
523 diff: StateDiff,
524 _retractions: &mut InProgressRetractions,
525 ) {
526 let res = match diff {
527 StateDiff::Addition => self.insert_system_configuration(
528 &system_configuration.name,
529 VarInput::Flat(&system_configuration.value),
530 ),
531 StateDiff::Retraction => self.remove_system_configuration(&system_configuration.name),
532 };
533 match res {
534 Ok(_) => (),
535 Err(Error {
539 kind: ErrorKind::VarError(VarError::UnknownParameter(name)),
540 }) => {
541 warn!(%name, "unknown system parameter from catalog storage");
542 }
543 Err(e) => panic!("unable to update system variable: {e:?}"),
544 }
545 }
546
547 fn apply_scoped_system_configuration_update<Id: Ord>(
561 map: &mut BTreeMap<Id, BTreeMap<String, String>>,
562 id: Id,
563 name: String,
564 value: String,
565 diff: StateDiff,
566 ) {
567 match diff {
568 StateDiff::Addition => {
569 map.entry(id).or_default().insert(name, value);
570 }
571 StateDiff::Retraction => {
572 if let Some(values) = map.get_mut(&id) {
573 if values.get(&name) == Some(&value) {
574 values.remove(&name);
575 if values.is_empty() {
576 map.remove(&id);
577 }
578 }
579 }
580 }
581 }
582 }
583
584 #[instrument(level = "debug")]
585 fn apply_cluster_update(
586 &mut self,
587 cluster: mz_catalog::durable::Cluster,
588 diff: StateDiff,
589 retractions: &mut InProgressRetractions,
590 ) {
591 if matches!(diff, StateDiff::Addition) {
598 if let mz_catalog::durable::ClusterVariant::Managed(managed) = &cluster.config.variant {
599 if !self.cluster_replica_sizes.0.contains_key(&managed.size) {
600 soft_panic_or_log!(
601 "managed cluster {} ({}) references unknown replica size {:?}; \
602 mz_clusters.disk will resolve to false",
603 cluster.name,
604 cluster.id,
605 managed.size,
606 );
607 }
608 }
609 }
610 apply_inverted_lookup(&mut self.clusters_by_name, &cluster.name, cluster.id, diff);
611 apply_with_update(
612 &mut self.clusters_by_id,
613 cluster,
614 |cluster| cluster.id,
615 diff,
616 &mut retractions.clusters,
617 );
618 }
619
620 #[instrument(level = "debug")]
621 fn apply_network_policy_update(
622 &mut self,
623 policy: mz_catalog::durable::NetworkPolicy,
624 diff: StateDiff,
625 retractions: &mut InProgressRetractions,
626 ) {
627 apply_inverted_lookup(
628 &mut self.network_policies_by_name,
629 &policy.name,
630 policy.id,
631 diff,
632 );
633 apply_with_update(
634 &mut self.network_policies_by_id,
635 policy,
636 |policy| policy.id,
637 diff,
638 &mut retractions.network_policies,
639 );
640 }
641
642 #[instrument(level = "debug")]
643 fn apply_introspection_source_index_update(
644 &mut self,
645 introspection_source_index: mz_catalog::durable::IntrospectionSourceIndex,
646 diff: StateDiff,
647 retractions: &mut InProgressRetractions,
648 ) {
649 let cluster = self
650 .clusters_by_id
651 .get_mut(&introspection_source_index.cluster_id)
652 .expect("catalog out of sync");
653 let log = BUILTIN_LOG_LOOKUP
654 .get(introspection_source_index.name.as_str())
655 .expect("missing log");
656 apply_inverted_lookup(
657 &mut cluster.log_indexes,
658 &log.variant,
659 introspection_source_index.index_id,
660 diff,
661 );
662
663 match diff {
664 StateDiff::Addition => {
665 if let Some(mut entry) = retractions
666 .introspection_source_indexes
667 .remove(&introspection_source_index.item_id)
668 {
669 let (index_name, index) = self.create_introspection_source_index(
672 introspection_source_index.cluster_id,
673 log,
674 introspection_source_index.index_id,
675 );
676 assert_eq!(entry.id, introspection_source_index.item_id);
677 assert_eq!(entry.oid, introspection_source_index.oid);
678 assert_eq!(entry.name, index_name);
679 entry.item = index;
680 self.insert_entry(entry);
681 } else {
682 self.insert_introspection_source_index(
683 introspection_source_index.cluster_id,
684 log,
685 introspection_source_index.item_id,
686 introspection_source_index.index_id,
687 introspection_source_index.oid,
688 );
689 }
690 }
691 StateDiff::Retraction => {
692 let entry = self.drop_item(introspection_source_index.item_id);
693 retractions
694 .introspection_source_indexes
695 .insert(entry.id, entry);
696 }
697 }
698 }
699
700 #[instrument(level = "debug")]
701 fn apply_cluster_replica_update(
702 &mut self,
703 cluster_replica: mz_catalog::durable::ClusterReplica,
704 diff: StateDiff,
705 _retractions: &mut InProgressRetractions,
706 ) {
707 let cluster = self
708 .clusters_by_id
709 .get(&cluster_replica.cluster_id)
710 .expect("catalog out of sync");
711
712 if let mz_catalog::durable::ReplicaLocation::Managed { size, .. } =
721 &cluster_replica.config.location
722 {
723 if !self.cluster_replica_sizes.0.contains_key(size) {
724 soft_panic_or_log!(
725 "cluster replica {}.{} ({}) references unknown replica size {:?}; \
726 skipping in-memory registration",
727 cluster.name,
728 cluster_replica.name,
729 cluster_replica.replica_id,
730 size,
731 );
732 return;
733 }
734 }
735
736 let location = self
742 .concretize_replica_location(cluster_replica.config.location, &vec![], None, true)
743 .expect("catalog in unexpected state");
744 let cluster = self
745 .clusters_by_id
746 .get_mut(&cluster_replica.cluster_id)
747 .expect("catalog out of sync");
748 apply_inverted_lookup(
749 &mut cluster.replica_id_by_name_,
750 &cluster_replica.name,
751 cluster_replica.replica_id,
752 diff,
753 );
754 match diff {
755 StateDiff::Retraction => {
756 let prev = cluster.replicas_by_id_.remove(&cluster_replica.replica_id);
757 assert!(
758 prev.is_some(),
759 "retraction does not match existing value: {:?}",
760 cluster_replica.replica_id
761 );
762 }
763 StateDiff::Addition => {
764 let logging = ReplicaLogging {
765 log_logging: cluster_replica.config.logging.log_logging,
766 interval: cluster_replica.config.logging.interval,
767 };
768 let config = ReplicaConfig {
769 location,
770 compute: ComputeReplicaConfig {
771 logging,
772 arrangement_compression: cluster_replica.config.arrangement_compression,
773 },
774 };
775 let mem_cluster_replica = ClusterReplica {
776 name: cluster_replica.name.clone(),
777 cluster_id: cluster_replica.cluster_id,
778 replica_id: cluster_replica.replica_id,
779 config,
780 owner_id: cluster_replica.owner_id,
781 };
782 let prev = cluster
783 .replicas_by_id_
784 .insert(cluster_replica.replica_id, mem_cluster_replica);
785 assert_eq!(
786 prev, None,
787 "values must be explicitly retracted before inserting a new value: {:?}",
788 cluster_replica.replica_id
789 );
790 }
791 }
792 }
793
794 #[instrument(level = "debug")]
795 fn apply_system_object_mapping_update(
796 &mut self,
797 system_object_mapping: mz_catalog::durable::SystemObjectMapping,
798 diff: StateDiff,
799 retractions: &mut InProgressRetractions,
800 local_expression_cache: &mut LocalExpressionCache,
801 ) {
802 let item_id = system_object_mapping.unique_identifier.catalog_id;
803 let global_id = system_object_mapping.unique_identifier.global_id;
804
805 if system_object_mapping.unique_identifier.runtime_alterable() {
806 return;
810 }
811
812 if let StateDiff::Retraction = diff {
813 let entry = self.drop_item(item_id);
814 retractions.system_object_mappings.insert(item_id, entry);
815 return;
816 }
817
818 if let Some(entry) = retractions.system_object_mappings.remove(&item_id) {
819 self.insert_entry(entry);
824 return;
825 }
826
827 let builtin = BUILTIN_LOOKUP
828 .get(&system_object_mapping.description)
829 .expect("missing builtin")
830 .1;
831 let schema_name = builtin.schema();
832 let schema_id = self
833 .ambient_schemas_by_name
834 .get(schema_name)
835 .unwrap_or_else(|| panic!("unknown ambient schema: {schema_name}"));
836 let name = QualifiedItemName {
837 qualifiers: ItemQualifiers {
838 database_spec: ResolvedDatabaseSpecifier::Ambient,
839 schema_spec: SchemaSpecifier::Id(*schema_id),
840 },
841 item: builtin.name().into(),
842 };
843 match builtin {
844 Builtin::Log(log) => {
845 let mut acl_items = vec![rbac::owner_privilege(
846 mz_sql::catalog::ObjectType::Source,
847 MZ_SYSTEM_ROLE_ID,
848 )];
849 acl_items.extend_from_slice(&log.access);
850 self.insert_item(
851 item_id,
852 log.oid,
853 name.clone(),
854 CatalogItem::Log(Log {
855 variant: log.variant,
856 global_id,
857 }),
858 MZ_SYSTEM_ROLE_ID,
859 PrivilegeMap::from_mz_acl_items(acl_items),
860 );
861 }
862
863 Builtin::Table(table) => {
864 let mut acl_items = vec![rbac::owner_privilege(
865 mz_sql::catalog::ObjectType::Table,
866 MZ_SYSTEM_ROLE_ID,
867 )];
868 acl_items.extend_from_slice(&table.access);
869
870 self.insert_item(
871 item_id,
872 table.oid,
873 name.clone(),
874 CatalogItem::Table(Table {
875 create_sql: None,
876 desc: VersionedRelationDesc::new(table.desc.clone()),
877 collections: [(RelationVersion::root(), global_id)].into_iter().collect(),
878 conn_id: None,
879 resolved_ids: ResolvedIds::empty(),
880 custom_logical_compaction_window: table.is_retained_metrics_object.then(
881 || {
882 self.system_config()
883 .metrics_retention()
884 .try_into()
885 .expect("invalid metrics retention")
886 },
887 ),
888 is_retained_metrics_object: table.is_retained_metrics_object,
889 data_source: TableDataSource::TableWrites {
890 defaults: vec![Expr::null(); table.desc.arity()],
891 },
892 }),
893 MZ_SYSTEM_ROLE_ID,
894 PrivilegeMap::from_mz_acl_items(acl_items),
895 );
896 }
897 Builtin::Index(index) => {
898 let custom_logical_compaction_window =
899 index.is_retained_metrics_object.then(|| {
900 self.system_config()
901 .metrics_retention()
902 .try_into()
903 .expect("invalid metrics retention")
904 });
905 let versions = BTreeMap::new();
907
908 let item = self
909 .parse_item(
910 global_id,
911 &index.create_sql(),
912 &versions,
913 None,
914 index.is_retained_metrics_object,
915 custom_logical_compaction_window,
916 local_expression_cache,
917 None,
918 )
919 .unwrap_or_else(|e| {
920 panic!(
921 "internal error: failed to load bootstrap index:\n\
922 {}\n\
923 error:\n\
924 {:?}\n\n\
925 make sure that the schema name is specified in the builtin index's create sql statement.",
926 index.name, e
927 )
928 });
929 let CatalogItem::Index(_) = item else {
930 panic!(
931 "internal error: builtin index {}'s SQL does not begin with \"CREATE INDEX\".",
932 index.name
933 );
934 };
935
936 self.insert_item(
937 item_id,
938 index.oid,
939 name,
940 item,
941 MZ_SYSTEM_ROLE_ID,
942 PrivilegeMap::default(),
943 );
944 }
945 Builtin::View(_) => {
946 unreachable!("views added elsewhere");
948 }
949
950 Builtin::Type(typ) => {
952 let typ = self.resolve_builtin_type_references(typ);
953 if let CatalogType::Array { element_reference } = typ.details.typ {
954 let entry = self.get_entry_mut(&element_reference);
955 let item_type = match &mut entry.item {
956 CatalogItem::Type(item_type) => item_type,
957 _ => unreachable!("types can only reference other types"),
958 };
959 item_type.details.array_id = Some(item_id);
960 }
961
962 let schema_id = self.resolve_system_schema(typ.schema);
963
964 self.insert_item(
965 item_id,
966 typ.oid,
967 QualifiedItemName {
968 qualifiers: ItemQualifiers {
969 database_spec: ResolvedDatabaseSpecifier::Ambient,
970 schema_spec: SchemaSpecifier::Id(schema_id),
971 },
972 item: typ.name.to_owned(),
973 },
974 CatalogItem::Type(Type {
975 create_sql: None,
976 global_id,
977 details: typ.details.clone(),
978 resolved_ids: ResolvedIds::empty(),
979 }),
980 MZ_SYSTEM_ROLE_ID,
981 PrivilegeMap::from_mz_acl_items(vec![
982 rbac::default_builtin_object_privilege(mz_sql::catalog::ObjectType::Type),
983 rbac::owner_privilege(mz_sql::catalog::ObjectType::Type, MZ_SYSTEM_ROLE_ID),
984 ]),
985 );
986 }
987
988 Builtin::Func(func) => {
989 let oid = INVALID_OID;
993 self.insert_item(
994 item_id,
995 oid,
996 name.clone(),
997 CatalogItem::Func(Func {
998 inner: func.inner,
999 global_id,
1000 }),
1001 MZ_SYSTEM_ROLE_ID,
1002 PrivilegeMap::default(),
1003 );
1004 }
1005
1006 Builtin::Source(coll) => {
1007 let mut acl_items = vec![rbac::owner_privilege(
1008 mz_sql::catalog::ObjectType::Source,
1009 MZ_SYSTEM_ROLE_ID,
1010 )];
1011 acl_items.extend_from_slice(&coll.access);
1012
1013 self.insert_item(
1014 item_id,
1015 coll.oid,
1016 name.clone(),
1017 CatalogItem::Source(Source {
1018 create_sql: None,
1019 data_source: coll.data_source.clone(),
1020 desc: coll.desc.clone(),
1021 global_id,
1022 timeline: Timeline::EpochMilliseconds,
1023 resolved_ids: ResolvedIds::empty(),
1024 custom_logical_compaction_window: coll.is_retained_metrics_object.then(
1025 || {
1026 self.system_config()
1027 .metrics_retention()
1028 .try_into()
1029 .expect("invalid metrics retention")
1030 },
1031 ),
1032 is_retained_metrics_object: coll.is_retained_metrics_object,
1033 }),
1034 MZ_SYSTEM_ROLE_ID,
1035 PrivilegeMap::from_mz_acl_items(acl_items),
1036 );
1037 }
1038 Builtin::MaterializedView(mv) => {
1039 let mut acl_items = vec![rbac::owner_privilege(
1040 mz_sql::catalog::ObjectType::MaterializedView,
1041 MZ_SYSTEM_ROLE_ID,
1042 )];
1043 acl_items.extend_from_slice(&mv.access);
1044
1045 let custom_logical_compaction_window = mv.is_retained_metrics_object.then(|| {
1046 self.system_config()
1047 .metrics_retention()
1048 .try_into()
1049 .expect("invalid metrics retention")
1050 });
1051
1052 let versions = BTreeMap::new();
1054
1055 let mut item = self
1056 .parse_item(
1057 global_id,
1058 &mv.create_sql(),
1059 &versions,
1060 None,
1061 mv.is_retained_metrics_object,
1062 custom_logical_compaction_window,
1063 local_expression_cache,
1064 None,
1065 )
1066 .unwrap_or_else(|e| {
1067 panic!(
1068 "internal error: failed to load bootstrap materialized view:\n\
1069 {}\n\
1070 error:\n\
1071 {e:?}\n\n\
1072 make sure that the schema name is specified in the builtin \
1073 materialized view's create sql statement.",
1074 mv.name,
1075 )
1076 });
1077 let CatalogItem::MaterializedView(catalog_mv) = &mut item else {
1078 panic!(
1079 "internal error: builtin materialized view {}'s SQL does not begin \
1080 with \"CREATE MATERIALIZED VIEW\".",
1081 mv.name,
1082 );
1083 };
1084
1085 let mut desc = catalog_mv.desc.latest();
1089 for key in &mv.desc.typ().keys {
1090 desc = desc.with_key(key.clone());
1091 }
1092 catalog_mv.desc = VersionedRelationDesc::new(desc);
1093
1094 self.insert_item(
1095 item_id,
1096 mv.oid,
1097 name,
1098 item,
1099 MZ_SYSTEM_ROLE_ID,
1100 PrivilegeMap::from_mz_acl_items(acl_items),
1101 );
1102 }
1103 Builtin::Connection(connection) => {
1104 let versions = BTreeMap::new();
1106 let mut item = self
1107 .parse_item(
1108 global_id,
1109 connection.sql,
1110 &versions,
1111 None,
1112 false,
1113 None,
1114 local_expression_cache,
1115 None,
1116 )
1117 .unwrap_or_else(|e| {
1118 panic!(
1119 "internal error: failed to load bootstrap connection:\n\
1120 {}\n\
1121 error:\n\
1122 {:?}\n\n\
1123 make sure that the schema name is specified in the builtin connection's create sql statement.",
1124 connection.name, e
1125 )
1126 });
1127 let CatalogItem::Connection(_) = &mut item else {
1128 panic!(
1129 "internal error: builtin connection {}'s SQL does not begin with \"CREATE CONNECTION\".",
1130 connection.name
1131 );
1132 };
1133
1134 let mut acl_items = vec![rbac::owner_privilege(
1135 mz_sql::catalog::ObjectType::Connection,
1136 connection.owner_id.clone(),
1137 )];
1138 acl_items.extend_from_slice(connection.access);
1139
1140 self.insert_item(
1141 item_id,
1142 connection.oid,
1143 name.clone(),
1144 item,
1145 connection.owner_id.clone(),
1146 PrivilegeMap::from_mz_acl_items(acl_items),
1147 );
1148 }
1149 }
1150 }
1151
1152 #[instrument(level = "debug")]
1153 fn apply_temporary_item_update(
1154 &mut self,
1155 temporary_item: TemporaryItem,
1156 diff: StateDiff,
1157 retractions: &mut InProgressRetractions,
1158 local_expression_cache: &mut LocalExpressionCache,
1159 ) {
1160 match diff {
1161 StateDiff::Addition => {
1162 let TemporaryItem {
1163 id,
1164 oid,
1165 global_id,
1166 schema_id,
1167 name,
1168 conn_id,
1169 create_sql,
1170 owner_id,
1171 privileges,
1172 extra_versions,
1173 } = temporary_item;
1174 let temp_conn_id = conn_id
1177 .as_ref()
1178 .expect("temporary items must have a connection id");
1179 if !self.temporary_schemas.contains_key(temp_conn_id) {
1180 self.create_temporary_schema(temp_conn_id, owner_id)
1181 .expect("failed to create temporary schema");
1182 }
1183 let schema = self.find_temp_schema(&schema_id);
1184 let name = QualifiedItemName {
1185 qualifiers: ItemQualifiers {
1186 database_spec: schema.database().clone(),
1187 schema_spec: schema.id().clone(),
1188 },
1189 item: name.clone(),
1190 };
1191
1192 let entry = match retractions.temp_items.remove(&id) {
1193 Some(mut retraction) => {
1194 assert_eq!(retraction.id, id);
1195
1196 if retraction.create_sql() != create_sql {
1201 let mut catalog_item = self
1202 .deserialize_item(
1203 global_id,
1204 &create_sql,
1205 &extra_versions,
1206 local_expression_cache,
1207 Some(retraction.item),
1208 )
1209 .unwrap_or_else(|e| {
1210 panic!("{e:?}: invalid persisted SQL: {create_sql}")
1211 });
1212 catalog_item.set_conn_id(conn_id);
1219 catalog_item.set_create_sql(create_sql);
1230 retraction.item = catalog_item;
1231 }
1232
1233 retraction.id = id;
1234 retraction.oid = oid;
1235 retraction.name = name;
1236 retraction.owner_id = owner_id;
1237 retraction.privileges = PrivilegeMap::from_mz_acl_items(privileges);
1238 retraction
1239 }
1240 None => {
1241 let mut catalog_item = self
1242 .deserialize_item(
1243 global_id,
1244 &create_sql,
1245 &extra_versions,
1246 local_expression_cache,
1247 None,
1248 )
1249 .unwrap_or_else(|e| {
1250 panic!("{e:?}: invalid persisted SQL: {create_sql}")
1251 });
1252
1253 catalog_item.set_conn_id(conn_id);
1259 catalog_item.set_create_sql(create_sql);
1262
1263 CatalogEntry {
1264 item: catalog_item,
1265 referenced_by: Vec::new(),
1266 used_by: Vec::new(),
1267 id,
1268 oid,
1269 name,
1270 owner_id,
1271 privileges: PrivilegeMap::from_mz_acl_items(privileges),
1272 }
1273 }
1274 };
1275 self.insert_entry(entry);
1276 }
1277 StateDiff::Retraction => {
1278 let entry = self.drop_item(temporary_item.id);
1279 retractions.temp_items.insert(temporary_item.id, entry);
1280 }
1281 }
1282 }
1283
1284 #[instrument(level = "debug")]
1285 fn apply_item_update(
1286 &mut self,
1287 item: mz_catalog::durable::Item,
1288 diff: StateDiff,
1289 retractions: &mut InProgressRetractions,
1290 local_expression_cache: &mut LocalExpressionCache,
1291 ) -> Result<(), CatalogError> {
1292 match diff {
1293 StateDiff::Addition => {
1294 let key = item.key();
1295 let mz_catalog::durable::Item {
1296 id,
1297 oid,
1298 global_id,
1299 schema_id,
1300 name,
1301 create_sql,
1302 owner_id,
1303 privileges,
1304 extra_versions,
1305 } = item;
1306 let schema = self.find_non_temp_schema(&schema_id);
1307 let name = QualifiedItemName {
1308 qualifiers: ItemQualifiers {
1309 database_spec: schema.database().clone(),
1310 schema_spec: schema.id().clone(),
1311 },
1312 item: name.clone(),
1313 };
1314 let entry = match retractions.items.remove(&key) {
1315 Some(retraction) => {
1316 assert_eq!(retraction.id, item.id);
1317
1318 let item = self
1319 .deserialize_item(
1320 global_id,
1321 &create_sql,
1322 &extra_versions,
1323 local_expression_cache,
1324 Some(retraction.item),
1325 )
1326 .unwrap_or_else(|e| {
1327 panic!("{e:?}: invalid persisted SQL: {create_sql}")
1328 });
1329
1330 CatalogEntry {
1331 item,
1332 id,
1333 oid,
1334 name,
1335 owner_id,
1336 privileges: PrivilegeMap::from_mz_acl_items(privileges),
1337 referenced_by: retraction.referenced_by,
1338 used_by: retraction.used_by,
1339 }
1340 }
1341 None => {
1342 let catalog_item = self
1343 .deserialize_item(
1344 global_id,
1345 &create_sql,
1346 &extra_versions,
1347 local_expression_cache,
1348 None,
1349 )
1350 .unwrap_or_else(|e| {
1351 panic!("{e:?}: invalid persisted SQL: {create_sql}")
1352 });
1353 CatalogEntry {
1354 item: catalog_item,
1355 referenced_by: Vec::new(),
1356 used_by: Vec::new(),
1357 id,
1358 oid,
1359 name,
1360 owner_id,
1361 privileges: PrivilegeMap::from_mz_acl_items(privileges),
1362 }
1363 }
1364 };
1365
1366 self.insert_entry(entry);
1367 }
1368 StateDiff::Retraction => {
1369 let entry = self.drop_item(item.id);
1370 let key = item.into_key_value().0;
1371 retractions.items.insert(key, entry);
1372 }
1373 }
1374 Ok(())
1375 }
1376
1377 #[instrument(level = "debug")]
1378 fn apply_comment_update(
1379 &mut self,
1380 comment: mz_catalog::durable::Comment,
1381 diff: StateDiff,
1382 _retractions: &mut InProgressRetractions,
1383 ) {
1384 match diff {
1385 StateDiff::Addition => {
1386 let prev = Arc::make_mut(&mut self.comments).update_comment(
1387 comment.object_id,
1388 comment.sub_component,
1389 Some(comment.comment),
1390 );
1391 assert_eq!(
1392 prev, None,
1393 "values must be explicitly retracted before inserting a new value"
1394 );
1395 }
1396 StateDiff::Retraction => {
1397 let prev = Arc::make_mut(&mut self.comments).update_comment(
1398 comment.object_id,
1399 comment.sub_component,
1400 None,
1401 );
1402 assert_eq!(
1403 prev,
1404 Some(comment.comment),
1405 "retraction does not match existing value: ({:?}, {:?})",
1406 comment.object_id,
1407 comment.sub_component,
1408 );
1409 }
1410 }
1411 }
1412
1413 #[instrument(level = "debug")]
1414 fn apply_source_references_update(
1415 &mut self,
1416 source_references: mz_catalog::durable::SourceReferences,
1417 diff: StateDiff,
1418 _retractions: &mut InProgressRetractions,
1419 ) {
1420 match diff {
1421 StateDiff::Addition => {
1422 let prev = self
1423 .source_references
1424 .insert(source_references.source_id, source_references.into());
1425 assert!(
1426 prev.is_none(),
1427 "values must be explicitly retracted before inserting a new value: {prev:?}"
1428 );
1429 }
1430 StateDiff::Retraction => {
1431 let prev = self.source_references.remove(&source_references.source_id);
1432 assert!(
1433 prev.is_some(),
1434 "retraction for a non-existent existing value: {source_references:?}"
1435 );
1436 }
1437 }
1438 }
1439
1440 #[instrument(level = "debug")]
1441 fn apply_storage_collection_metadata_update(
1442 &mut self,
1443 storage_collection_metadata: mz_catalog::durable::StorageCollectionMetadata,
1444 diff: StateDiff,
1445 _retractions: &mut InProgressRetractions,
1446 ) {
1447 apply_inverted_lookup(
1448 &mut Arc::make_mut(&mut self.storage_metadata).collection_metadata,
1449 &storage_collection_metadata.id,
1450 storage_collection_metadata.shard,
1451 diff,
1452 );
1453 }
1454
1455 #[instrument(level = "debug")]
1456 fn apply_unfinalized_shard_update(
1457 &mut self,
1458 unfinalized_shard: mz_catalog::durable::UnfinalizedShard,
1459 diff: StateDiff,
1460 _retractions: &mut InProgressRetractions,
1461 ) {
1462 match diff {
1463 StateDiff::Addition => {
1464 let newly_inserted = Arc::make_mut(&mut self.storage_metadata)
1465 .unfinalized_shards
1466 .insert(unfinalized_shard.shard);
1467 assert!(
1468 newly_inserted,
1469 "values must be explicitly retracted before inserting a new value: {unfinalized_shard:?}",
1470 );
1471 }
1472 StateDiff::Retraction => {
1473 let removed = Arc::make_mut(&mut self.storage_metadata)
1474 .unfinalized_shards
1475 .remove(&unfinalized_shard.shard);
1476 assert!(
1477 removed,
1478 "retraction does not match existing value: {unfinalized_shard:?}"
1479 );
1480 }
1481 }
1482 }
1483
1484 #[instrument(level = "debug")]
1487 pub(crate) fn generate_builtin_table_update(
1488 &self,
1489 kind: StateUpdateKind,
1490 diff: StateDiff,
1491 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1492 let diff = diff.into();
1493 match kind {
1494 StateUpdateKind::Role(_) => Vec::new(),
1498 StateUpdateKind::RoleAuth(role_auth) => {
1499 vec![self.pack_role_auth_update(role_auth.role_id, diff)]
1500 }
1501 StateUpdateKind::DefaultPrivilege(_) => Vec::new(),
1505 StateUpdateKind::SystemPrivilege(_) => Vec::new(),
1506 StateUpdateKind::SystemConfiguration(_) => Vec::new(),
1507 StateUpdateKind::ClusterSystemConfiguration(_) => Vec::new(),
1513 StateUpdateKind::ReplicaSystemConfiguration(_) => Vec::new(),
1514 StateUpdateKind::Cluster(_) => Vec::new(),
1518 StateUpdateKind::IntrospectionSourceIndex(introspection_source_index) => {
1519 self.pack_item_update(introspection_source_index.item_id, diff)
1520 }
1521 StateUpdateKind::ClusterReplica(_) => Vec::new(),
1524 StateUpdateKind::SystemObjectMapping(system_object_mapping) => {
1525 if !system_object_mapping.unique_identifier.runtime_alterable() {
1529 self.pack_item_update(system_object_mapping.unique_identifier.catalog_id, diff)
1530 } else {
1531 vec![]
1532 }
1533 }
1534 StateUpdateKind::TemporaryItem(item) => self.pack_item_update(item.id, diff),
1535 StateUpdateKind::Item(item) => self.pack_item_update(item.id, diff),
1536 StateUpdateKind::Comment(_) => Vec::new(),
1537 StateUpdateKind::SourceReferences(source_references) => {
1538 self.pack_source_references_update(&source_references, diff)
1539 }
1540 StateUpdateKind::AuditLog(_) => Vec::new(),
1544 StateUpdateKind::Database(_)
1545 | StateUpdateKind::Schema(_)
1546 | StateUpdateKind::NetworkPolicy(_)
1547 | StateUpdateKind::StorageCollectionMetadata(_)
1548 | StateUpdateKind::UnfinalizedShard(_) => Vec::new(),
1549 }
1550 }
1551
1552 fn get_entry_mut(&mut self, id: &CatalogItemId) -> &mut CatalogEntry {
1553 self.entry_by_id
1554 .get_mut(id)
1555 .unwrap_or_else(|| panic!("catalog out of sync, missing id {id}"))
1556 }
1557
1558 pub(super) fn set_optimized_plan(
1563 &mut self,
1564 id: GlobalId,
1565 plan: DataflowDescription<mz_expr::OptimizedMirRelationExpr>,
1566 ) {
1567 let item_id = self.entry_by_global_id[&id];
1568 let entry = self.get_entry_mut(&item_id);
1569 match entry.item_mut() {
1570 CatalogItem::Index(idx) => idx.optimized_plan = Some(Arc::new(plan)),
1571 CatalogItem::MaterializedView(mv) => mv.optimized_plan = Some(Arc::new(plan)),
1572 other => panic!("set_optimized_plan called on {} ({:?})", id, other.typ()),
1573 }
1574 }
1575
1576 pub(super) fn set_physical_plan(
1581 &mut self,
1582 id: GlobalId,
1583 plan: DataflowDescription<mz_compute_types::plan::LirRelationExpr>,
1584 ) {
1585 let item_id = self.entry_by_global_id[&id];
1586 let entry = self.get_entry_mut(&item_id);
1587 match entry.item_mut() {
1588 CatalogItem::Index(idx) => idx.physical_plan = Some(Arc::new(plan)),
1589 CatalogItem::MaterializedView(mv) => mv.physical_plan = Some(Arc::new(plan)),
1590 other => panic!("set_physical_plan called on {} ({:?})", id, other.typ()),
1591 }
1592 }
1593
1594 pub(super) fn set_dataflow_metainfo(
1599 &mut self,
1600 id: GlobalId,
1601 metainfo: DataflowMetainfo<Arc<OptimizerNotice>>,
1602 ) {
1603 for notice in metainfo.optimizer_notices.iter() {
1605 for dep_id in notice.dependencies.iter() {
1606 self.notices_by_dep_id
1607 .entry(*dep_id)
1608 .or_default()
1609 .push(Arc::clone(notice));
1610 }
1611 if let Some(item_id) = notice.item_id {
1612 soft_assert_eq_or_log!(
1613 item_id,
1614 id,
1615 "notice.item_id should match the id for whom we are saving the notice"
1616 );
1617 }
1618 }
1619 let item_id = self.entry_by_global_id[&id];
1621 let entry = self.get_entry_mut(&item_id);
1622 match entry.item_mut() {
1623 CatalogItem::Index(idx) => idx.dataflow_metainfo = Some(metainfo),
1624 CatalogItem::MaterializedView(mv) => mv.dataflow_metainfo = Some(metainfo),
1625 other => panic!("set_dataflow_metainfo called on {} ({:?})", id, other.typ()),
1626 }
1627 }
1628
1629 #[mz_ore::instrument(level = "trace")]
1640 pub(super) fn drop_optimizer_notices(
1641 &mut self,
1642 dropped_entries: Vec<CatalogEntry>,
1643 ) -> BTreeSet<Arc<OptimizerNotice>> {
1644 let mut dropped_notices = BTreeSet::new();
1645 let mut drop_ids = BTreeSet::new();
1646
1647 for mut entry in dropped_entries {
1649 drop_ids.extend(entry.global_ids());
1650 let metainfo = match entry.item_mut() {
1651 CatalogItem::Index(idx) => idx.dataflow_metainfo.take(),
1652 CatalogItem::MaterializedView(mv) => mv.dataflow_metainfo.take(),
1653 _ => None,
1654 };
1655 if let Some(mut metainfo) = metainfo {
1656 soft_assert_or_log!(
1657 metainfo.optimizer_notices.iter().all_unique(),
1658 "should have been pushed there by \
1659 `push_optimizer_notice_dedup`"
1660 );
1661 for n in metainfo.optimizer_notices.drain(..) {
1662 for dep_id in n.dependencies.iter() {
1665 if let Some(notices) = self.notices_by_dep_id.get_mut(dep_id) {
1666 notices.retain(|x| &n != x);
1667 if notices.is_empty() {
1668 self.notices_by_dep_id.remove(dep_id);
1669 }
1670 }
1671 }
1672 dropped_notices.insert(n);
1673 }
1674 }
1675 }
1676
1677 for id in &drop_ids {
1681 if let Some(notices) = self.notices_by_dep_id.remove(id) {
1682 for n in notices.into_iter() {
1683 if let Some(item_id) = n.item_id.as_ref() {
1688 if let Some(entry) = self.try_get_entry_by_global_id(item_id) {
1689 let catalog_item_id = entry.id();
1690 let entry = self.get_entry_mut(&catalog_item_id);
1691 let item = entry.item_mut();
1692 match item {
1693 CatalogItem::Index(idx) => {
1694 if let Some(ref mut m) = idx.dataflow_metainfo {
1695 m.optimizer_notices.retain(|x| &n != x);
1696 }
1697 }
1698 CatalogItem::MaterializedView(mv) => {
1699 if let Some(ref mut m) = mv.dataflow_metainfo {
1700 m.optimizer_notices.retain(|x| &n != x);
1701 }
1702 }
1703 _ => {}
1704 }
1705 }
1706 }
1707 dropped_notices.insert(n);
1708 }
1709 }
1710 }
1711
1712 let todo_dep_ids: BTreeSet<GlobalId> = dropped_notices
1715 .iter()
1716 .flat_map(|n| n.dependencies.iter())
1717 .filter(|dep_id| !drop_ids.contains(dep_id))
1718 .copied()
1719 .collect();
1720 for id in todo_dep_ids {
1721 if let Some(notices) = self.notices_by_dep_id.get_mut(&id) {
1722 notices.retain(|n| !dropped_notices.contains(n));
1723 if notices.is_empty() {
1724 self.notices_by_dep_id.remove(&id);
1725 }
1726 }
1727 }
1728
1729 dropped_notices
1730 }
1731
1732 fn get_schema_mut(
1733 &mut self,
1734 database_spec: &ResolvedDatabaseSpecifier,
1735 schema_spec: &SchemaSpecifier,
1736 conn_id: &ConnectionId,
1737 ) -> &mut Schema {
1738 match (database_spec, schema_spec) {
1740 (ResolvedDatabaseSpecifier::Ambient, SchemaSpecifier::Temporary) => self
1741 .temporary_schemas
1742 .get_mut(conn_id)
1743 .expect("catalog out of sync"),
1744 (ResolvedDatabaseSpecifier::Ambient, SchemaSpecifier::Id(id)) => self
1745 .ambient_schemas_by_id
1746 .get_mut(id)
1747 .expect("catalog out of sync"),
1748 (ResolvedDatabaseSpecifier::Id(database_id), SchemaSpecifier::Id(schema_id)) => self
1749 .database_by_id
1750 .get_mut(database_id)
1751 .expect("catalog out of sync")
1752 .schemas_by_id
1753 .get_mut(schema_id)
1754 .expect("catalog out of sync"),
1755 (ResolvedDatabaseSpecifier::Id(_), SchemaSpecifier::Temporary) => {
1756 unreachable!("temporary schemas are in the ambient database")
1757 }
1758 }
1759 }
1760
1761 #[instrument(name = "catalog::parse_views")]
1771 async fn parse_builtin_views(
1772 state: &mut CatalogState,
1773 builtin_views: Vec<(&'static BuiltinView, CatalogItemId, GlobalId)>,
1774 retractions: &mut InProgressRetractions,
1775 local_expression_cache: &mut LocalExpressionCache,
1776 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1777 let mut builtin_table_updates = Vec::with_capacity(builtin_views.len());
1778 let (updates, additions): (Vec<_>, Vec<_>) =
1779 builtin_views
1780 .into_iter()
1781 .partition_map(|(view, item_id, gid)| {
1782 match retractions.system_object_mappings.remove(&item_id) {
1783 Some(entry) => Either::Left(entry),
1784 None => Either::Right((view, item_id, gid)),
1785 }
1786 });
1787
1788 for entry in updates {
1789 let item_id = entry.id();
1794 state.insert_entry(entry);
1795 builtin_table_updates.extend(state.pack_item_update(item_id, Diff::ONE));
1796 }
1797
1798 let mut handles = Vec::new();
1799 let mut awaiting_id_dependencies: BTreeMap<CatalogItemId, Vec<CatalogItemId>> =
1800 BTreeMap::new();
1801 let mut awaiting_name_dependencies: BTreeMap<String, Vec<CatalogItemId>> = BTreeMap::new();
1802 let mut awaiting_all = Vec::new();
1805 let mut completed_ids: BTreeSet<CatalogItemId> = BTreeSet::new();
1807 let mut completed_names: BTreeSet<String> = BTreeSet::new();
1808
1809 let mut views: BTreeMap<CatalogItemId, (&BuiltinView, GlobalId)> = additions
1811 .into_iter()
1812 .map(|(view, item_id, gid)| (item_id, (view, gid)))
1813 .collect();
1814 let item_ids: Vec<_> = views.keys().copied().collect();
1815
1816 let mut ready: VecDeque<CatalogItemId> = views.keys().cloned().collect();
1817 while !handles.is_empty() || !ready.is_empty() || !awaiting_all.is_empty() {
1818 if handles.is_empty() && ready.is_empty() {
1819 ready.extend(awaiting_all.drain(..));
1821 }
1822
1823 if !ready.is_empty() {
1825 let spawn_state = Arc::new(state.clone());
1826 while let Some(id) = ready.pop_front() {
1827 let (view, global_id) = views.get(&id).expect("must exist");
1828 let global_id = *global_id;
1829 let create_sql = view.create_sql();
1830 let versions = BTreeMap::new();
1832
1833 let span = info_span!(parent: None, "parse builtin view", name = view.name);
1834 OpenTelemetryContext::obtain().attach_as_parent_to(&span);
1835 let task_state = Arc::clone(&spawn_state);
1836 let cached_expr = local_expression_cache.remove_cached_expression(&global_id);
1837 let handle = mz_ore::task::spawn_blocking(
1838 || "parse view",
1839 move || {
1840 span.in_scope(|| {
1841 let res = task_state.parse_item_inner(
1842 global_id,
1843 &create_sql,
1844 &versions,
1845 None,
1846 false,
1847 None,
1848 cached_expr,
1849 None,
1850 );
1851 (id, global_id, res)
1852 })
1853 },
1854 );
1855 handles.push(handle);
1856 }
1857 }
1858
1859 let (selected, _idx, remaining) = future::select_all(handles).await;
1861 handles = remaining;
1862 let (id, global_id, res) = selected;
1863 let mut insert_cached_expr = |cached_expr| {
1864 if let Some(cached_expr) = cached_expr {
1865 local_expression_cache.insert_cached_expression(global_id, cached_expr);
1866 }
1867 };
1868 match res {
1869 Ok((item, uncached_expr)) => {
1870 if let Some((uncached_expr, optimizer_features)) = uncached_expr {
1871 local_expression_cache.insert_uncached_expression(
1872 global_id,
1873 uncached_expr,
1874 optimizer_features,
1875 );
1876 }
1877 let (view, _gid) = views.remove(&id).expect("must exist");
1879 let schema_id = state
1880 .ambient_schemas_by_name
1881 .get(view.schema)
1882 .unwrap_or_else(|| panic!("unknown ambient schema: {}", view.schema));
1883 let qname = QualifiedItemName {
1884 qualifiers: ItemQualifiers {
1885 database_spec: ResolvedDatabaseSpecifier::Ambient,
1886 schema_spec: SchemaSpecifier::Id(*schema_id),
1887 },
1888 item: view.name.into(),
1889 };
1890 let mut acl_items = vec![rbac::owner_privilege(
1891 mz_sql::catalog::ObjectType::View,
1892 MZ_SYSTEM_ROLE_ID,
1893 )];
1894 acl_items.extend_from_slice(&view.access);
1895
1896 state.insert_item(
1897 id,
1898 view.oid,
1899 qname,
1900 item,
1901 MZ_SYSTEM_ROLE_ID,
1902 PrivilegeMap::from_mz_acl_items(acl_items),
1903 );
1904
1905 let mut resolved_dependent_items = Vec::new();
1907 if let Some(dependent_items) = awaiting_id_dependencies.remove(&id) {
1908 resolved_dependent_items.extend(dependent_items);
1909 }
1910 let entry = state.get_entry(&id);
1911 let full_name = state.resolve_full_name(entry.name(), None).to_string();
1912 if let Some(dependent_items) = awaiting_name_dependencies.remove(&full_name) {
1913 resolved_dependent_items.extend(dependent_items);
1914 }
1915 ready.extend(resolved_dependent_items);
1916
1917 completed_ids.insert(id);
1918 completed_names.insert(full_name);
1919 }
1920 Err((
1922 AdapterError::PlanError(plan::PlanError::InvalidId(missing_dep)),
1923 cached_expr,
1924 )) => {
1925 insert_cached_expr(cached_expr);
1926 if completed_ids.contains(&missing_dep) {
1927 ready.push_back(id);
1928 } else {
1929 awaiting_id_dependencies
1930 .entry(missing_dep)
1931 .or_default()
1932 .push(id);
1933 }
1934 }
1935 Err((
1937 AdapterError::PlanError(plan::PlanError::Catalog(
1938 SqlCatalogError::UnknownItem(missing_dep),
1939 )),
1940 cached_expr,
1941 )) => {
1942 insert_cached_expr(cached_expr);
1943 match CatalogItemId::from_str(&missing_dep) {
1944 Ok(missing_dep) => {
1945 if completed_ids.contains(&missing_dep) {
1946 ready.push_back(id);
1947 } else {
1948 awaiting_id_dependencies
1949 .entry(missing_dep)
1950 .or_default()
1951 .push(id);
1952 }
1953 }
1954 Err(_) => {
1955 if completed_names.contains(&missing_dep) {
1956 ready.push_back(id);
1957 } else {
1958 awaiting_name_dependencies
1959 .entry(missing_dep)
1960 .or_default()
1961 .push(id);
1962 }
1963 }
1964 }
1965 }
1966 Err((
1967 AdapterError::PlanError(plan::PlanError::InvalidCast { .. }),
1968 cached_expr,
1969 )) => {
1970 insert_cached_expr(cached_expr);
1971 awaiting_all.push(id);
1972 }
1973 Err((e, _)) => {
1974 let (bad_view, _gid) = views.get(&id).expect("must exist");
1975 panic!(
1976 "internal error: failed to load bootstrap view:\n\
1977 {name}\n\
1978 error:\n\
1979 {e:?}\n\n\
1980 Make sure that the schema name is specified in the builtin view's create sql statement.
1981 ",
1982 name = bad_view.name,
1983 )
1984 }
1985 }
1986 }
1987
1988 assert!(awaiting_id_dependencies.is_empty());
1989 assert!(
1990 awaiting_name_dependencies.is_empty(),
1991 "awaiting_name_dependencies: {awaiting_name_dependencies:?}"
1992 );
1993 assert!(awaiting_all.is_empty());
1994 assert!(views.is_empty());
1995
1996 builtin_table_updates.extend(
1998 item_ids
1999 .into_iter()
2000 .flat_map(|id| state.pack_item_update(id, Diff::ONE)),
2001 );
2002
2003 builtin_table_updates
2004 }
2005
2006 fn insert_entry(&mut self, entry: CatalogEntry) {
2008 if !entry.id.is_system() {
2009 if let Some(cluster_id) = entry.item.cluster_id() {
2010 self.clusters_by_id
2011 .get_mut(&cluster_id)
2012 .expect("catalog out of sync")
2013 .bound_objects
2014 .insert(entry.id);
2015 };
2016 }
2017
2018 for u in entry.references().items() {
2019 match self.entry_by_id.get_mut(u) {
2020 Some(metadata) => metadata.referenced_by.push(entry.id()),
2021 None => panic!(
2022 "Catalog: missing dependent catalog item {} while installing {}",
2023 &u,
2024 self.resolve_full_name(entry.name(), entry.conn_id())
2025 ),
2026 }
2027 }
2028 for u in entry.uses() {
2029 if u == entry.id() {
2032 continue;
2033 }
2034 match self.entry_by_id.get_mut(&u) {
2035 Some(metadata) => metadata.used_by.push(entry.id()),
2036 None => panic!(
2037 "Catalog: missing dependent catalog item {} while installing {}",
2038 &u,
2039 self.resolve_full_name(entry.name(), entry.conn_id())
2040 ),
2041 }
2042 }
2043 for gid in entry.item.global_ids() {
2044 self.entry_by_global_id.insert(gid, entry.id());
2045 }
2046 let conn_id = entry.item().conn_id().unwrap_or(&SYSTEM_CONN_ID);
2047 if entry.name().qualifiers.schema_spec == SchemaSpecifier::Temporary
2050 && !self.temporary_schemas.contains_key(conn_id)
2051 {
2052 self.create_temporary_schema(conn_id, entry.owner_id)
2053 .expect("failed to create temporary schema");
2054 }
2055 let schema = self.get_schema_mut(
2056 &entry.name().qualifiers.database_spec,
2057 &entry.name().qualifiers.schema_spec,
2058 conn_id,
2059 );
2060
2061 let prev_id = match entry.item() {
2062 CatalogItem::Func(_) => schema
2063 .functions
2064 .insert(entry.name().item.clone(), entry.id()),
2065 CatalogItem::Type(_) => schema.types.insert(entry.name().item.clone(), entry.id()),
2066 _ => schema.items.insert(entry.name().item.clone(), entry.id()),
2067 };
2068
2069 assert!(
2070 prev_id.is_none(),
2071 "builtin name collision on {:?}",
2072 entry.name().item.clone()
2073 );
2074
2075 self.entry_by_id.insert(entry.id(), entry.clone());
2076 }
2077
2078 fn insert_item(
2080 &mut self,
2081 id: CatalogItemId,
2082 oid: u32,
2083 name: QualifiedItemName,
2084 item: CatalogItem,
2085 owner_id: RoleId,
2086 privileges: PrivilegeMap,
2087 ) {
2088 let entry = CatalogEntry {
2089 item,
2090 name,
2091 id,
2092 oid,
2093 used_by: Vec::new(),
2094 referenced_by: Vec::new(),
2095 owner_id,
2096 privileges,
2097 };
2098
2099 self.insert_entry(entry);
2100 }
2101
2102 #[mz_ore::instrument(level = "trace")]
2103 fn drop_item(&mut self, id: CatalogItemId) -> CatalogEntry {
2104 let metadata = self.entry_by_id.remove(&id).expect("catalog out of sync");
2105 for u in metadata.references().items() {
2106 if let Some(dep_metadata) = self.entry_by_id.get_mut(u) {
2107 dep_metadata.referenced_by.retain(|u| *u != metadata.id())
2108 }
2109 }
2110 for u in metadata.uses() {
2111 if let Some(dep_metadata) = self.entry_by_id.get_mut(&u) {
2112 dep_metadata.used_by.retain(|u| *u != metadata.id())
2113 }
2114 }
2115 for gid in metadata.global_ids() {
2116 self.entry_by_global_id.remove(&gid);
2117 }
2118
2119 let conn_id = metadata.item().conn_id().unwrap_or(&SYSTEM_CONN_ID);
2120 let schema = self.get_schema_mut(
2121 &metadata.name().qualifiers.database_spec,
2122 &metadata.name().qualifiers.schema_spec,
2123 conn_id,
2124 );
2125 if metadata.item_type() == CatalogItemType::Type {
2126 schema
2127 .types
2128 .remove(&metadata.name().item)
2129 .expect("catalog out of sync");
2130 } else {
2131 assert_ne!(metadata.item_type(), CatalogItemType::Func);
2134
2135 schema
2136 .items
2137 .remove(&metadata.name().item)
2138 .expect("catalog out of sync");
2139 };
2140
2141 if !id.is_system() {
2142 if let Some(cluster_id) = metadata.item().cluster_id() {
2143 assert!(
2144 self.clusters_by_id
2145 .get_mut(&cluster_id)
2146 .expect("catalog out of sync")
2147 .bound_objects
2148 .remove(&id),
2149 "catalog out of sync"
2150 );
2151 }
2152 }
2153
2154 metadata
2155 }
2156
2157 fn insert_introspection_source_index(
2158 &mut self,
2159 cluster_id: ClusterId,
2160 log: &'static BuiltinLog,
2161 item_id: CatalogItemId,
2162 global_id: GlobalId,
2163 oid: u32,
2164 ) {
2165 let (index_name, index) =
2166 self.create_introspection_source_index(cluster_id, log, global_id);
2167 self.insert_item(
2168 item_id,
2169 oid,
2170 index_name,
2171 index,
2172 MZ_SYSTEM_ROLE_ID,
2173 PrivilegeMap::default(),
2174 );
2175 }
2176
2177 fn create_introspection_source_index(
2178 &self,
2179 cluster_id: ClusterId,
2180 log: &'static BuiltinLog,
2181 global_id: GlobalId,
2182 ) -> (QualifiedItemName, CatalogItem) {
2183 let source_name = FullItemName {
2184 database: RawDatabaseSpecifier::Ambient,
2185 schema: log.schema.into(),
2186 item: log.name.into(),
2187 };
2188 let index_name = format!("{}_{}_primary_idx", log.name, cluster_id);
2189 let mut index_name = QualifiedItemName {
2190 qualifiers: ItemQualifiers {
2191 database_spec: ResolvedDatabaseSpecifier::Ambient,
2192 schema_spec: SchemaSpecifier::Id(self.get_mz_introspection_schema_id()),
2193 },
2194 item: index_name.clone(),
2195 };
2196 index_name = self.find_available_name(index_name, &SYSTEM_CONN_ID);
2197 let index_item_name = index_name.item.clone();
2198 let (log_item_id, log_global_id) = self.resolve_builtin_log(log);
2199 let index = CatalogItem::Index(Index {
2200 global_id,
2201 on: log_global_id,
2202 keys: log
2203 .variant
2204 .index_by()
2205 .into_iter()
2206 .map(MirScalarExpr::column)
2207 .collect(),
2208 create_sql: index_sql(
2209 index_item_name,
2210 cluster_id,
2211 source_name,
2212 &log.variant.desc(),
2213 &log.variant.index_by(),
2214 ),
2215 conn_id: None,
2216 resolved_ids: [(log_item_id, log_global_id)].into_iter().collect(),
2217 cluster_id,
2218 is_retained_metrics_object: false,
2219 custom_logical_compaction_window: None,
2220 optimized_plan: None,
2221 physical_plan: None,
2222 dataflow_metainfo: None,
2223 });
2224 (index_name, index)
2225 }
2226
2227 fn insert_system_configuration(&mut self, name: &str, value: VarInput) -> Result<bool, Error> {
2232 Ok(Arc::make_mut(&mut self.system_configuration).set(name, value)?)
2233 }
2234
2235 fn remove_system_configuration(&mut self, name: &str) -> Result<bool, Error> {
2240 Ok(Arc::make_mut(&mut self.system_configuration).reset(name)?)
2241 }
2242}
2243
2244fn sort_updates(updates: Vec<StateUpdate>) -> Vec<StateUpdate> {
2252 fn push_update<T>(
2253 update: T,
2254 diff: StateDiff,
2255 retractions: &mut Vec<T>,
2256 additions: &mut Vec<T>,
2257 ) {
2258 match diff {
2259 StateDiff::Retraction => retractions.push(update),
2260 StateDiff::Addition => additions.push(update),
2261 }
2262 }
2263
2264 soft_assert_no_log!(
2265 updates.iter().map(|update| update.ts).all_equal(),
2266 "all timestamps should be equal: {updates:?}"
2267 );
2268 soft_assert_no_log!(
2269 {
2270 let mut dedup = BTreeSet::new();
2271 updates.iter().all(|update| dedup.insert(&update.kind))
2272 },
2273 "updates should be consolidated: {updates:?}"
2274 );
2275
2276 let mut pre_cluster_retractions = Vec::new();
2278 let mut pre_cluster_additions = Vec::new();
2279 let mut cluster_retractions = Vec::new();
2280 let mut cluster_additions = Vec::new();
2281 let mut builtin_item_updates = Vec::new();
2282 let mut item_retractions = Vec::new();
2283 let mut item_additions = Vec::new();
2284 let mut temp_item_retractions = Vec::new();
2285 let mut temp_item_additions = Vec::new();
2286 let mut post_item_retractions = Vec::new();
2287 let mut post_item_additions = Vec::new();
2288 for update in updates {
2289 let diff = update.diff.clone();
2290 match update.kind {
2291 StateUpdateKind::Role(_)
2292 | StateUpdateKind::RoleAuth(_)
2293 | StateUpdateKind::Database(_)
2294 | StateUpdateKind::Schema(_)
2295 | StateUpdateKind::DefaultPrivilege(_)
2296 | StateUpdateKind::SystemPrivilege(_)
2297 | StateUpdateKind::SystemConfiguration(_)
2298 | StateUpdateKind::NetworkPolicy(_) => push_update(
2299 update,
2300 diff,
2301 &mut pre_cluster_retractions,
2302 &mut pre_cluster_additions,
2303 ),
2304 StateUpdateKind::Cluster(_)
2305 | StateUpdateKind::ClusterSystemConfiguration(_)
2306 | StateUpdateKind::IntrospectionSourceIndex(_)
2307 | StateUpdateKind::ClusterReplica(_)
2308 | StateUpdateKind::ReplicaSystemConfiguration(_) => push_update(
2309 update,
2310 diff,
2311 &mut cluster_retractions,
2312 &mut cluster_additions,
2313 ),
2314 StateUpdateKind::SystemObjectMapping(system_object_mapping) => {
2315 builtin_item_updates.push((system_object_mapping, update.ts, update.diff))
2316 }
2317 StateUpdateKind::TemporaryItem(item) => push_update(
2318 (item, update.ts, update.diff),
2319 diff,
2320 &mut temp_item_retractions,
2321 &mut temp_item_additions,
2322 ),
2323 StateUpdateKind::Item(item) => push_update(
2324 (item, update.ts, update.diff),
2325 diff,
2326 &mut item_retractions,
2327 &mut item_additions,
2328 ),
2329 StateUpdateKind::Comment(_)
2330 | StateUpdateKind::SourceReferences(_)
2331 | StateUpdateKind::AuditLog(_)
2332 | StateUpdateKind::StorageCollectionMetadata(_)
2333 | StateUpdateKind::UnfinalizedShard(_) => push_update(
2334 update,
2335 diff,
2336 &mut post_item_retractions,
2337 &mut post_item_additions,
2338 ),
2339 }
2340 }
2341
2342 let builtin_item_updates = builtin_item_updates
2345 .into_iter()
2346 .map(|(system_object_mapping, ts, diff)| {
2347 let idx = BUILTIN_LOOKUP
2348 .get(&system_object_mapping.description)
2349 .expect("missing builtin")
2350 .0;
2351 (idx, system_object_mapping, ts, diff)
2352 })
2353 .sorted_by_key(|(idx, _, _, _)| *idx)
2354 .map(|(_, system_object_mapping, ts, diff)| (system_object_mapping, ts, diff));
2355
2356 let mut builtin_source_retractions = Vec::new();
2360 let mut builtin_source_additions = Vec::new();
2361 let mut other_builtin_retractions = Vec::new();
2362 let mut other_builtin_additions = Vec::new();
2363 for (builtin_item_update, ts, diff) in builtin_item_updates {
2364 let object_type = builtin_item_update.description.object_type;
2365 let update = StateUpdate {
2366 kind: StateUpdateKind::SystemObjectMapping(builtin_item_update),
2367 ts,
2368 diff,
2369 };
2370 if object_type == CatalogItemType::Source {
2371 push_update(
2372 update,
2373 diff,
2374 &mut builtin_source_retractions,
2375 &mut builtin_source_additions,
2376 );
2377 } else {
2378 push_update(
2379 update,
2380 diff,
2381 &mut other_builtin_retractions,
2382 &mut other_builtin_additions,
2383 );
2384 }
2385 }
2386
2387 fn sort_items_topological(items: &mut Vec<(mz_catalog::durable::Item, Timestamp, StateDiff)>) {
2393 tracing::debug!(?items, "sorting items by dependencies");
2394
2395 let key_fn = |item: &(mz_catalog::durable::Item, _, _)| item.0.id;
2396 let dependencies_fn = |item: &(mz_catalog::durable::Item, _, _)| {
2397 let statement = mz_sql::parse::parse(&item.0.create_sql)
2398 .expect("valid create_sql")
2399 .into_element()
2400 .ast;
2401 mz_sql::names::dependencies(&statement).expect("failed to find dependencies of item")
2402 };
2403 sort_topological(items, key_fn, dependencies_fn);
2404 }
2405
2406 fn sort_item_updates(
2422 item_updates: Vec<(mz_catalog::durable::Item, Timestamp, StateDiff)>,
2423 ) -> VecDeque<(mz_catalog::durable::Item, Timestamp, StateDiff)> {
2424 let mut types = Vec::new();
2427 let mut funcs = Vec::new();
2430 let mut secrets = Vec::new();
2431 let mut connections = Vec::new();
2432 let mut sources = Vec::new();
2433 let mut tables = Vec::new();
2434 let mut derived_items = Vec::new();
2435 let mut sinks = Vec::new();
2436 for update in item_updates {
2437 match update.0.item_type() {
2438 CatalogItemType::Type => types.push(update),
2439 CatalogItemType::Func => funcs.push(update),
2440 CatalogItemType::Secret => secrets.push(update),
2441 CatalogItemType::Connection => connections.push(update),
2442 CatalogItemType::Source => sources.push(update),
2443 CatalogItemType::Table => tables.push(update),
2444 CatalogItemType::View
2445 | CatalogItemType::MaterializedView
2446 | CatalogItemType::Index => derived_items.push(update),
2447 CatalogItemType::Sink => sinks.push(update),
2448 }
2449 }
2450
2451 sort_items_topological(&mut connections);
2455 sort_items_topological(&mut derived_items);
2456
2457 for group in [
2459 &mut types,
2460 &mut funcs,
2461 &mut secrets,
2462 &mut sources,
2463 &mut tables,
2464 &mut sinks,
2465 ] {
2466 group.sort_by_key(|(item, _, _)| item.id);
2467 }
2468
2469 iter::empty()
2470 .chain(types)
2471 .chain(funcs)
2472 .chain(secrets)
2473 .chain(connections)
2474 .chain(sources)
2475 .chain(tables)
2476 .chain(derived_items)
2477 .chain(sinks)
2478 .collect()
2479 }
2480
2481 let item_retractions = sort_item_updates(item_retractions);
2482 let item_additions = sort_item_updates(item_additions);
2483
2484 fn sort_temp_item_updates(
2488 temp_item_updates: Vec<(TemporaryItem, Timestamp, StateDiff)>,
2489 ) -> VecDeque<(TemporaryItem, Timestamp, StateDiff)> {
2490 let mut types = Vec::new();
2493 let mut funcs = Vec::new();
2495 let mut secrets = Vec::new();
2496 let mut connections = Vec::new();
2497 let mut sources = Vec::new();
2498 let mut tables = Vec::new();
2499 let mut derived_items = Vec::new();
2500 let mut sinks = Vec::new();
2501 for update in temp_item_updates {
2502 match update.0.item_type() {
2503 CatalogItemType::Type => types.push(update),
2504 CatalogItemType::Func => funcs.push(update),
2505 CatalogItemType::Secret => secrets.push(update),
2506 CatalogItemType::Connection => connections.push(update),
2507 CatalogItemType::Source => sources.push(update),
2508 CatalogItemType::Table => tables.push(update),
2509 CatalogItemType::View
2510 | CatalogItemType::MaterializedView
2511 | CatalogItemType::Index => derived_items.push(update),
2512 CatalogItemType::Sink => sinks.push(update),
2513 }
2514 }
2515
2516 for group in [
2518 &mut types,
2519 &mut funcs,
2520 &mut secrets,
2521 &mut connections,
2522 &mut sources,
2523 &mut tables,
2524 &mut derived_items,
2525 &mut sinks,
2526 ] {
2527 group.sort_by_key(|(item, _, _)| item.id);
2528 }
2529
2530 iter::empty()
2531 .chain(types)
2532 .chain(funcs)
2533 .chain(secrets)
2534 .chain(connections)
2535 .chain(sources)
2536 .chain(tables)
2537 .chain(derived_items)
2538 .chain(sinks)
2539 .collect()
2540 }
2541 let temp_item_retractions = sort_temp_item_updates(temp_item_retractions);
2542 let temp_item_additions = sort_temp_item_updates(temp_item_additions);
2543
2544 fn merge_item_updates(
2560 item_updates: VecDeque<(mz_catalog::durable::Item, Timestamp, StateDiff)>,
2561 temp_item_updates: VecDeque<(TemporaryItem, Timestamp, StateDiff)>,
2562 ) -> Vec<StateUpdate> {
2563 let mut state_updates = Vec::with_capacity(item_updates.len() + temp_item_updates.len());
2564 for (item, ts, diff) in item_updates {
2565 state_updates.push(StateUpdate {
2566 kind: StateUpdateKind::Item(item),
2567 ts,
2568 diff,
2569 });
2570 }
2571 for (temp_item, ts, diff) in temp_item_updates {
2572 state_updates.push(StateUpdate {
2573 kind: StateUpdateKind::TemporaryItem(temp_item),
2574 ts,
2575 diff,
2576 });
2577 }
2578 state_updates
2579 }
2580 let item_retractions = merge_item_updates(item_retractions, temp_item_retractions);
2581 let item_additions = merge_item_updates(item_additions, temp_item_additions);
2582
2583 iter::empty()
2585 .chain(post_item_retractions.into_iter().rev())
2587 .chain(item_retractions.into_iter().rev())
2588 .chain(other_builtin_retractions.into_iter().rev())
2589 .chain(cluster_retractions.into_iter().rev())
2590 .chain(builtin_source_retractions.into_iter().rev())
2591 .chain(pre_cluster_retractions.into_iter().rev())
2592 .chain(pre_cluster_additions)
2593 .chain(builtin_source_additions)
2594 .chain(cluster_additions)
2595 .chain(other_builtin_additions)
2596 .chain(item_additions)
2597 .chain(post_item_additions)
2598 .collect()
2599}
2600
2601enum ApplyState {
2606 BuiltinViewAdditions(Vec<(&'static BuiltinView, CatalogItemId, GlobalId)>),
2608 Items(Vec<StateUpdate>),
2614 Updates(Vec<StateUpdate>),
2616}
2617
2618impl ApplyState {
2619 fn new(update: StateUpdate) -> Self {
2620 use StateUpdateKind::*;
2621 match &update.kind {
2622 SystemObjectMapping(som)
2623 if som.description.object_type == CatalogItemType::View
2624 && update.diff == StateDiff::Addition =>
2625 {
2626 let view_addition = lookup_builtin_view_addition(som.clone());
2627 Self::BuiltinViewAdditions(vec![view_addition])
2628 }
2629
2630 IntrospectionSourceIndex(_) | SystemObjectMapping(_) | TemporaryItem(_) | Item(_) => {
2631 Self::Items(vec![update])
2632 }
2633
2634 Role(_)
2635 | RoleAuth(_)
2636 | Database(_)
2637 | Schema(_)
2638 | DefaultPrivilege(_)
2639 | SystemPrivilege(_)
2640 | SystemConfiguration(_)
2641 | ClusterSystemConfiguration(_)
2642 | ReplicaSystemConfiguration(_)
2643 | Cluster(_)
2644 | NetworkPolicy(_)
2645 | ClusterReplica(_)
2646 | SourceReferences(_)
2647 | Comment(_)
2648 | AuditLog(_)
2649 | StorageCollectionMetadata(_)
2650 | UnfinalizedShard(_) => Self::Updates(vec![update]),
2651 }
2652 }
2653
2654 async fn apply(
2660 self,
2661 state: &mut CatalogState,
2662 retractions: &mut InProgressRetractions,
2663 local_expression_cache: &mut LocalExpressionCache,
2664 ) -> (
2665 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
2666 Vec<ParsedStateUpdate>,
2667 ) {
2668 match self {
2669 Self::BuiltinViewAdditions(builtin_view_additions) => {
2670 let restore = Arc::clone(&state.system_configuration);
2671 Arc::make_mut(&mut state.system_configuration).enable_for_item_parsing();
2672 let builtin_table_updates = CatalogState::parse_builtin_views(
2673 state,
2674 builtin_view_additions,
2675 retractions,
2676 local_expression_cache,
2677 )
2678 .await;
2679 state.system_configuration = restore;
2680 (builtin_table_updates, Vec::new())
2681 }
2682 Self::Items(updates) => state.with_enable_for_item_parsing(|state| {
2683 state
2684 .apply_updates_inner(updates, retractions, local_expression_cache)
2685 .expect("corrupt catalog")
2686 }),
2687 Self::Updates(updates) => state
2688 .apply_updates_inner(updates, retractions, local_expression_cache)
2689 .expect("corrupt catalog"),
2690 }
2691 }
2692
2693 async fn step(
2694 self,
2695 next: Self,
2696 state: &mut CatalogState,
2697 retractions: &mut InProgressRetractions,
2698 local_expression_cache: &mut LocalExpressionCache,
2699 ) -> (
2700 Self,
2701 (
2702 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
2703 Vec<ParsedStateUpdate>,
2704 ),
2705 ) {
2706 match (self, next) {
2707 (
2708 Self::BuiltinViewAdditions(mut builtin_view_additions),
2709 Self::BuiltinViewAdditions(next_builtin_view_additions),
2710 ) => {
2711 builtin_view_additions.extend(next_builtin_view_additions);
2713 (
2714 Self::BuiltinViewAdditions(builtin_view_additions),
2715 (Vec::new(), Vec::new()),
2716 )
2717 }
2718 (Self::Items(mut updates), Self::Items(next_updates)) => {
2719 updates.extend(next_updates);
2721 (Self::Items(updates), (Vec::new(), Vec::new()))
2722 }
2723 (Self::Updates(mut updates), Self::Updates(next_updates)) => {
2724 updates.extend(next_updates);
2726 (Self::Updates(updates), (Vec::new(), Vec::new()))
2727 }
2728 (apply_state, next_apply_state) => {
2729 let updates = apply_state
2731 .apply(state, retractions, local_expression_cache)
2732 .await;
2733 (next_apply_state, updates)
2734 }
2735 }
2736 }
2737}
2738
2739trait MutableMap<K, V> {
2742 fn insert(&mut self, key: K, value: V) -> Option<V>;
2743 fn remove(&mut self, key: &K) -> Option<V>;
2744}
2745
2746impl<K: Ord, V> MutableMap<K, V> for BTreeMap<K, V> {
2747 fn insert(&mut self, key: K, value: V) -> Option<V> {
2748 BTreeMap::insert(self, key, value)
2749 }
2750 fn remove(&mut self, key: &K) -> Option<V> {
2751 BTreeMap::remove(self, key)
2752 }
2753}
2754
2755impl<K: Ord + Clone, V: Clone> MutableMap<K, V> for imbl::OrdMap<K, V> {
2756 fn insert(&mut self, key: K, value: V) -> Option<V> {
2757 imbl::OrdMap::insert(self, key, value)
2758 }
2759 fn remove(&mut self, key: &K) -> Option<V> {
2760 imbl::OrdMap::remove(self, key)
2761 }
2762}
2763
2764fn apply_inverted_lookup<K, V>(map: &mut impl MutableMap<K, V>, key: &K, value: V, diff: StateDiff)
2769where
2770 K: Ord + Clone + Debug,
2771 V: PartialEq + Debug,
2772{
2773 match diff {
2774 StateDiff::Retraction => {
2775 let prev = map.remove(key);
2776 assert_eq!(
2777 prev,
2778 Some(value),
2779 "retraction does not match existing value: {key:?}"
2780 );
2781 }
2782 StateDiff::Addition => {
2783 let prev = map.insert(key.clone(), value);
2784 assert_eq!(
2785 prev, None,
2786 "values must be explicitly retracted before inserting a new value: {key:?}"
2787 );
2788 }
2789 }
2790}
2791
2792fn apply_with_update<K, V, D>(
2795 map: &mut impl MutableMap<K, V>,
2796 durable: D,
2797 key_fn: impl FnOnce(&D) -> K,
2798 diff: StateDiff,
2799 retractions: &mut BTreeMap<D::Key, V>,
2800) where
2801 K: Ord,
2802 V: UpdateFrom<D> + PartialEq + Debug,
2803 D: DurableType,
2804 D::Key: Ord,
2805{
2806 match diff {
2807 StateDiff::Retraction => {
2808 let mem_key = key_fn(&durable);
2809 let value = map
2810 .remove(&mem_key)
2811 .expect("retraction does not match existing value: {key:?}");
2812 let durable_key = durable.into_key_value().0;
2813 retractions.insert(durable_key, value);
2814 }
2815 StateDiff::Addition => {
2816 let mem_key = key_fn(&durable);
2817 let durable_key = durable.key();
2818 let value = match retractions.remove(&durable_key) {
2819 Some(mut retraction) => {
2820 retraction.update_from(durable);
2821 retraction
2822 }
2823 None => durable.into(),
2824 };
2825 let prev = map.insert(mem_key, value);
2826 assert_eq!(
2827 prev, None,
2828 "values must be explicitly retracted before inserting a new value"
2829 );
2830 }
2831 }
2832}
2833
2834fn lookup_builtin_view_addition(
2836 mapping: SystemObjectMapping,
2837) -> (&'static BuiltinView, CatalogItemId, GlobalId) {
2838 let (_, builtin) = BUILTIN_LOOKUP
2839 .get(&mapping.description)
2840 .expect("missing builtin view");
2841 let Builtin::View(view) = builtin else {
2842 unreachable!("programming error, expected BuiltinView found {builtin:?}");
2843 };
2844
2845 (
2846 view,
2847 mapping.unique_identifier.catalog_id,
2848 mapping.unique_identifier.global_id,
2849 )
2850}