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, 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 introspection_source_indexes: BTreeMap<CatalogItemId, CatalogEntry>,
94 system_object_mappings: BTreeMap<CatalogItemId, CatalogEntry>,
95}
96
97impl CatalogState {
98 #[must_use]
102 #[instrument]
103 pub(crate) async fn apply_updates(
104 &mut self,
105 updates: Vec<StateUpdate>,
106 local_expression_cache: &mut LocalExpressionCache,
107 ) -> (
108 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
109 Vec<ParsedStateUpdate>,
110 ) {
111 let mut builtin_table_updates = Vec::with_capacity(updates.len());
112 let mut catalog_updates = Vec::with_capacity(updates.len());
113
114 let updates = Self::consolidate_updates(updates);
119
120 let mut groups: Vec<Vec<_>> = Vec::new();
122 for (_, updates) in &updates.into_iter().chunk_by(|update| update.ts) {
123 let updates = sort_updates(updates.collect());
127 groups.push(updates);
128 }
129
130 for updates in groups {
131 let mut apply_state = ApplyState::Updates(Vec::new());
132 let mut retractions = InProgressRetractions::default();
133
134 for update in updates {
135 let (next_apply_state, (builtin_table_update, catalog_update)) = apply_state
136 .step(
137 ApplyState::new(update),
138 self,
139 &mut retractions,
140 local_expression_cache,
141 )
142 .await;
143 apply_state = next_apply_state;
144 builtin_table_updates.extend(builtin_table_update);
145 catalog_updates.extend(catalog_update);
146 }
147
148 let (builtin_table_update, catalog_update) = apply_state
150 .apply(self, &mut retractions, local_expression_cache)
151 .await;
152 builtin_table_updates.extend(builtin_table_update);
153 catalog_updates.extend(catalog_update);
154
155 let dropped_entries: Vec<CatalogEntry> = retractions.items.into_values().collect();
158 if !dropped_entries.is_empty() {
159 let dropped_notices = self.drop_optimizer_notices(dropped_entries);
160 if self.system_config().enable_mz_notices() {
161 self.pack_optimizer_notice_updates(
162 &mut builtin_table_updates,
163 dropped_notices.iter(),
164 Diff::MINUS_ONE,
165 );
166 }
167 }
168 }
169
170 (builtin_table_updates, catalog_updates)
171 }
172
173 fn consolidate_updates(updates: Vec<StateUpdate>) -> Vec<StateUpdate> {
181 let mut updates: Vec<(StateUpdateKind, Timestamp, mz_repr::Diff)> = updates
182 .into_iter()
183 .map(|update| (update.kind, update.ts, update.diff.into()))
184 .collect_vec();
185
186 consolidate_updates(&mut updates);
187
188 updates
189 .into_iter()
190 .map(|(kind, ts, diff)| StateUpdate {
191 kind,
192 ts,
193 diff: diff
194 .try_into()
195 .expect("catalog state cannot have diff other than -1 or 1"),
196 })
197 .collect_vec()
198 }
199
200 #[instrument(level = "debug")]
201 fn apply_updates_inner(
202 &mut self,
203 updates: Vec<StateUpdate>,
204 retractions: &mut InProgressRetractions,
205 local_expression_cache: &mut LocalExpressionCache,
206 ) -> Result<
207 (
208 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
209 Vec<ParsedStateUpdate>,
210 ),
211 CatalogError,
212 > {
213 soft_assert_no_log!(
214 updates.iter().map(|update| update.ts).all_equal(),
215 "all timestamps should be equal: {updates:?}"
216 );
217
218 let mut update_system_config = false;
219
220 let mut builtin_table_updates = Vec::with_capacity(updates.len());
221 let mut catalog_updates = Vec::new();
222
223 for state_update in updates {
224 if self.is_nonlocal_ephemeral_item_update(&state_update) {
228 tracing::debug!(
229 ?state_update,
230 "skipping ephemeral item update for non-local session"
231 );
232 continue;
233 }
234
235 if matches!(state_update.kind, StateUpdateKind::SystemConfiguration(_)) {
236 update_system_config = true;
237 }
238
239 match state_update.diff {
240 StateDiff::Retraction => {
241 if let Some(update) =
245 parsed_state_updates::parse_state_update(self, state_update.clone())
246 {
247 catalog_updates.push(update);
248 }
249
250 builtin_table_updates.extend(self.generate_builtin_table_update(
253 state_update.kind.clone(),
254 state_update.diff,
255 ));
256 self.apply_update(
257 state_update.kind,
258 state_update.diff,
259 retractions,
260 local_expression_cache,
261 )?;
262 }
263 StateDiff::Addition => {
264 self.apply_update(
265 state_update.kind.clone(),
266 state_update.diff,
267 retractions,
268 local_expression_cache,
269 )?;
270 builtin_table_updates.extend(self.generate_builtin_table_update(
274 state_update.kind.clone(),
275 state_update.diff,
276 ));
277
278 if let Some(update) =
281 parsed_state_updates::parse_state_update(self, state_update.clone())
282 {
283 catalog_updates.push(update);
284 }
285 }
286 }
287 }
288
289 if update_system_config {
290 self.system_configuration.sync_dyncfgs();
291 }
292
293 Ok((builtin_table_updates, catalog_updates))
294 }
295
296 #[instrument(level = "debug")]
297 fn apply_update(
298 &mut self,
299 kind: StateUpdateKind,
300 diff: StateDiff,
301 retractions: &mut InProgressRetractions,
302 local_expression_cache: &mut LocalExpressionCache,
303 ) -> Result<(), CatalogError> {
304 match kind {
305 StateUpdateKind::Role(role) => {
306 self.apply_role_update(role, diff, retractions);
307 }
308 StateUpdateKind::RoleAuth(role_auth) => {
309 self.apply_role_auth_update(role_auth, diff, retractions);
310 }
311 StateUpdateKind::Database(database) => {
312 self.apply_database_update(database, diff, retractions);
313 }
314 StateUpdateKind::Schema(schema) => {
315 self.apply_schema_update(schema, diff, retractions);
316 }
317 StateUpdateKind::DefaultPrivilege(default_privilege) => {
318 self.apply_default_privilege_update(default_privilege, diff, retractions);
319 }
320 StateUpdateKind::SystemPrivilege(system_privilege) => {
321 self.apply_system_privilege_update(system_privilege, diff, retractions);
322 }
323 StateUpdateKind::SystemConfiguration(system_configuration) => {
324 self.apply_system_configuration_update(system_configuration, diff, retractions);
325 }
326 StateUpdateKind::ClusterSystemConfiguration(cfg) => {
327 Self::apply_scoped_system_configuration_update(
328 &mut self.scoped_system_parameters.cluster,
329 cfg.cluster_id,
330 cfg.name,
331 cfg.value,
332 diff,
333 );
334 }
335 StateUpdateKind::ReplicaSystemConfiguration(cfg) => {
336 Self::apply_scoped_system_configuration_update(
337 &mut self.scoped_system_parameters.replica,
338 cfg.replica_id,
339 cfg.name,
340 cfg.value,
341 diff,
342 );
343 }
344 StateUpdateKind::Cluster(cluster) => {
345 self.apply_cluster_update(cluster, diff, retractions);
346 }
347 StateUpdateKind::NetworkPolicy(network_policy) => {
348 self.apply_network_policy_update(network_policy, diff, retractions);
349 }
350 StateUpdateKind::ClusterReplicaSize(size) => {
351 self.apply_cluster_replica_size_update(size, diff);
352 }
353 StateUpdateKind::IntrospectionSourceIndex(introspection_source_index) => {
354 self.apply_introspection_source_index_update(
355 introspection_source_index,
356 diff,
357 retractions,
358 );
359 }
360 StateUpdateKind::ClusterReplica(cluster_replica) => {
361 self.apply_cluster_replica_update(cluster_replica, diff, retractions);
362 }
363 StateUpdateKind::SystemObjectMapping(system_object_mapping) => {
364 self.apply_system_object_mapping_update(
365 system_object_mapping,
366 diff,
367 retractions,
368 local_expression_cache,
369 );
370 }
371 StateUpdateKind::Item(item) => {
372 self.apply_item_update(item, diff, retractions, local_expression_cache)?;
373 }
374 StateUpdateKind::Comment(comment) => {
375 self.apply_comment_update(comment, diff, retractions);
376 }
377 StateUpdateKind::SourceReferences(source_reference) => {
378 self.apply_source_references_update(source_reference, diff, retractions);
379 }
380 StateUpdateKind::AuditLog(_audit_log) => {
381 }
383 StateUpdateKind::StorageCollectionMetadata(storage_collection_metadata) => {
384 self.apply_storage_collection_metadata_update(
385 storage_collection_metadata,
386 diff,
387 retractions,
388 );
389 }
390 StateUpdateKind::UnfinalizedShard(unfinalized_shard) => {
391 self.apply_unfinalized_shard_update(unfinalized_shard, diff, retractions);
392 }
393 }
394
395 Ok(())
396 }
397
398 #[instrument(level = "debug")]
399 fn apply_role_auth_update(
400 &mut self,
401 role_auth: mz_catalog::durable::RoleAuth,
402 diff: StateDiff,
403 retractions: &mut InProgressRetractions,
404 ) {
405 apply_with_update(
406 &mut self.role_auth_by_id,
407 role_auth,
408 |role_auth| role_auth.role_id,
409 diff,
410 &mut retractions.role_auths,
411 );
412 }
413
414 #[instrument(level = "debug")]
415 fn apply_role_update(
416 &mut self,
417 role: mz_catalog::durable::Role,
418 diff: StateDiff,
419 retractions: &mut InProgressRetractions,
420 ) {
421 apply_inverted_lookup(&mut self.roles_by_name, &role.name, role.id, diff);
422 apply_with_update(
423 &mut self.roles_by_id,
424 role,
425 |role| role.id,
426 diff,
427 &mut retractions.roles,
428 );
429 }
430
431 #[instrument(level = "debug")]
432 fn apply_database_update(
433 &mut self,
434 database: mz_catalog::durable::Database,
435 diff: StateDiff,
436 retractions: &mut InProgressRetractions,
437 ) {
438 apply_inverted_lookup(
439 &mut self.database_by_name,
440 &database.name,
441 database.id,
442 diff,
443 );
444 apply_with_update(
445 &mut self.database_by_id,
446 database,
447 |database| database.id,
448 diff,
449 &mut retractions.databases,
450 );
451 }
452
453 #[instrument(level = "debug")]
454 fn apply_schema_update(
455 &mut self,
456 schema: mz_catalog::durable::Schema,
457 diff: StateDiff,
458 retractions: &mut InProgressRetractions,
459 ) {
460 match &schema.database_id {
461 Some(database_id) => {
462 let db = self
463 .database_by_id
464 .get_mut(database_id)
465 .expect("catalog out of sync");
466 apply_inverted_lookup(&mut db.schemas_by_name, &schema.name, schema.id, diff);
467 apply_with_update(
468 &mut db.schemas_by_id,
469 schema,
470 |schema| schema.id,
471 diff,
472 &mut retractions.schemas,
473 );
474 }
475 None => {
476 apply_inverted_lookup(
477 &mut self.ambient_schemas_by_name,
478 &schema.name,
479 schema.id,
480 diff,
481 );
482 apply_with_update(
483 &mut self.ambient_schemas_by_id,
484 schema,
485 |schema| schema.id,
486 diff,
487 &mut retractions.schemas,
488 );
489 }
490 }
491 }
492
493 #[instrument(level = "debug")]
494 fn apply_default_privilege_update(
495 &mut self,
496 default_privilege: mz_catalog::durable::DefaultPrivilege,
497 diff: StateDiff,
498 _retractions: &mut InProgressRetractions,
499 ) {
500 match diff {
501 StateDiff::Addition => Arc::make_mut(&mut self.default_privileges)
502 .grant(default_privilege.object, default_privilege.acl_item),
503 StateDiff::Retraction => Arc::make_mut(&mut self.default_privileges)
504 .revoke(&default_privilege.object, &default_privilege.acl_item),
505 }
506 }
507
508 #[instrument(level = "debug")]
509 fn apply_system_privilege_update(
510 &mut self,
511 system_privilege: MzAclItem,
512 diff: StateDiff,
513 _retractions: &mut InProgressRetractions,
514 ) {
515 match diff {
516 StateDiff::Addition => {
517 Arc::make_mut(&mut self.system_privileges).grant(system_privilege)
518 }
519 StateDiff::Retraction => {
520 Arc::make_mut(&mut self.system_privileges).revoke(&system_privilege)
521 }
522 }
523 }
524
525 #[instrument(level = "debug")]
526 fn apply_system_configuration_update(
527 &mut self,
528 system_configuration: mz_catalog::durable::SystemConfiguration,
529 diff: StateDiff,
530 _retractions: &mut InProgressRetractions,
531 ) {
532 let res = match diff {
533 StateDiff::Addition => self.insert_system_configuration(
534 &system_configuration.name,
535 VarInput::Flat(&system_configuration.value),
536 ),
537 StateDiff::Retraction => self.remove_system_configuration(&system_configuration.name),
538 };
539 match res {
540 Ok(_) => (),
541 Err(Error {
545 kind: ErrorKind::VarError(VarError::UnknownParameter(name)),
546 }) => {
547 warn!(%name, "unknown system parameter from catalog storage");
548 }
549 Err(e) => panic!("unable to update system variable: {e:?}"),
550 }
551 }
552
553 fn apply_scoped_system_configuration_update<Id: Ord>(
567 map: &mut BTreeMap<Id, BTreeMap<String, String>>,
568 id: Id,
569 name: String,
570 value: String,
571 diff: StateDiff,
572 ) {
573 match diff {
574 StateDiff::Addition => {
575 map.entry(id).or_default().insert(name, value);
576 }
577 StateDiff::Retraction => {
578 if let Some(values) = map.get_mut(&id) {
579 if values.get(&name) == Some(&value) {
580 values.remove(&name);
581 if values.is_empty() {
582 map.remove(&id);
583 }
584 }
585 }
586 }
587 }
588 }
589
590 #[instrument(level = "debug")]
591 fn apply_cluster_update(
592 &mut self,
593 cluster: mz_catalog::durable::Cluster,
594 diff: StateDiff,
595 retractions: &mut InProgressRetractions,
596 ) {
597 if matches!(diff, StateDiff::Addition) {
604 if let mz_catalog::durable::ClusterVariant::Managed(managed) = &cluster.config.variant {
605 if !self.cluster_replica_sizes.0.contains_key(&managed.size) {
606 soft_panic_or_log!(
607 "managed cluster {} ({}) references unknown replica size {:?}; \
608 mz_clusters.disk will resolve to false",
609 cluster.name,
610 cluster.id,
611 managed.size,
612 );
613 }
614 }
615 }
616 apply_inverted_lookup(&mut self.clusters_by_name, &cluster.name, cluster.id, diff);
617 apply_with_update(
618 &mut self.clusters_by_id,
619 cluster,
620 |cluster| cluster.id,
621 diff,
622 &mut retractions.clusters,
623 );
624 }
625
626 #[instrument(level = "debug")]
627 fn apply_network_policy_update(
628 &mut self,
629 policy: mz_catalog::durable::NetworkPolicy,
630 diff: StateDiff,
631 retractions: &mut InProgressRetractions,
632 ) {
633 apply_inverted_lookup(
634 &mut self.network_policies_by_name,
635 &policy.name,
636 policy.id,
637 diff,
638 );
639 apply_with_update(
640 &mut self.network_policies_by_id,
641 policy,
642 |policy| policy.id,
643 diff,
644 &mut retractions.network_policies,
645 );
646 }
647
648 #[instrument(level = "debug")]
649 fn apply_cluster_replica_size_update(
650 &mut self,
651 size: mz_catalog::durable::ClusterReplicaSize,
652 diff: StateDiff,
653 ) {
654 let sizes = &mut self.cluster_replica_sizes.0;
655 match diff {
656 StateDiff::Addition => {
657 sizes.insert(size.name, size.allocation);
658 }
659 StateDiff::Retraction => {
660 sizes.remove(&size.name);
661 }
662 }
663 }
664
665 #[instrument(level = "debug")]
666 fn apply_introspection_source_index_update(
667 &mut self,
668 introspection_source_index: mz_catalog::durable::IntrospectionSourceIndex,
669 diff: StateDiff,
670 retractions: &mut InProgressRetractions,
671 ) {
672 let cluster = self
673 .clusters_by_id
674 .get_mut(&introspection_source_index.cluster_id)
675 .expect("catalog out of sync");
676 let log = BUILTIN_LOG_LOOKUP
677 .get(introspection_source_index.name.as_str())
678 .expect("missing log");
679 apply_inverted_lookup(
680 &mut cluster.log_indexes,
681 &log.variant,
682 introspection_source_index.index_id,
683 diff,
684 );
685
686 match diff {
687 StateDiff::Addition => {
688 if let Some(mut entry) = retractions
689 .introspection_source_indexes
690 .remove(&introspection_source_index.item_id)
691 {
692 let (index_name, index) = self.create_introspection_source_index(
695 introspection_source_index.cluster_id,
696 log,
697 introspection_source_index.index_id,
698 );
699 assert_eq!(entry.id, introspection_source_index.item_id);
700 assert_eq!(entry.oid, introspection_source_index.oid);
701 assert_eq!(entry.name, index_name);
702 entry.item = index;
703 self.insert_entry(entry);
704 } else {
705 self.insert_introspection_source_index(
706 introspection_source_index.cluster_id,
707 log,
708 introspection_source_index.item_id,
709 introspection_source_index.index_id,
710 introspection_source_index.oid,
711 );
712 }
713 }
714 StateDiff::Retraction => {
715 let entry = self.drop_item(introspection_source_index.item_id);
716 retractions
717 .introspection_source_indexes
718 .insert(entry.id, entry);
719 }
720 }
721 }
722
723 #[instrument(level = "debug")]
724 fn apply_cluster_replica_update(
725 &mut self,
726 cluster_replica: mz_catalog::durable::ClusterReplica,
727 diff: StateDiff,
728 _retractions: &mut InProgressRetractions,
729 ) {
730 let cluster = self
731 .clusters_by_id
732 .get(&cluster_replica.cluster_id)
733 .expect("catalog out of sync");
734
735 if let mz_catalog::durable::ReplicaLocation::Managed { size, .. } =
744 &cluster_replica.config.location
745 {
746 if !self.cluster_replica_sizes.0.contains_key(size) {
747 soft_panic_or_log!(
748 "cluster replica {}.{} ({}) references unknown replica size {:?}; \
749 skipping in-memory registration",
750 cluster.name,
751 cluster_replica.name,
752 cluster_replica.replica_id,
753 size,
754 );
755 return;
756 }
757 }
758
759 let location = self
765 .concretize_replica_location(cluster_replica.config.location, &vec![], None, true)
766 .expect("catalog in unexpected state");
767 let cluster = self
768 .clusters_by_id
769 .get_mut(&cluster_replica.cluster_id)
770 .expect("catalog out of sync");
771 apply_inverted_lookup(
772 &mut cluster.replica_id_by_name_,
773 &cluster_replica.name,
774 cluster_replica.replica_id,
775 diff,
776 );
777 match diff {
778 StateDiff::Retraction => {
779 let prev = cluster.replicas_by_id_.remove(&cluster_replica.replica_id);
780 assert!(
781 prev.is_some(),
782 "retraction does not match existing value: {:?}",
783 cluster_replica.replica_id
784 );
785 }
786 StateDiff::Addition => {
787 let logging = ReplicaLogging {
788 log_logging: cluster_replica.config.logging.log_logging,
789 interval: cluster_replica.config.logging.interval,
790 };
791 let config = ReplicaConfig {
792 location,
793 compute: ComputeReplicaConfig {
794 logging,
795 arrangement_compression: cluster_replica.config.arrangement_compression,
796 },
797 };
798 let mem_cluster_replica = ClusterReplica {
799 name: cluster_replica.name.clone(),
800 cluster_id: cluster_replica.cluster_id,
801 replica_id: cluster_replica.replica_id,
802 config,
803 owner_id: cluster_replica.owner_id,
804 };
805 let prev = cluster
806 .replicas_by_id_
807 .insert(cluster_replica.replica_id, mem_cluster_replica);
808 assert_eq!(
809 prev, None,
810 "values must be explicitly retracted before inserting a new value: {:?}",
811 cluster_replica.replica_id
812 );
813 }
814 }
815 }
816
817 #[instrument(level = "debug")]
818 fn apply_system_object_mapping_update(
819 &mut self,
820 system_object_mapping: mz_catalog::durable::SystemObjectMapping,
821 diff: StateDiff,
822 retractions: &mut InProgressRetractions,
823 local_expression_cache: &mut LocalExpressionCache,
824 ) {
825 let item_id = system_object_mapping.unique_identifier.catalog_id;
826 let global_id = system_object_mapping.unique_identifier.global_id;
827
828 if system_object_mapping.unique_identifier.runtime_alterable() {
829 return;
833 }
834
835 if let StateDiff::Retraction = diff {
836 let entry = self.drop_item(item_id);
837 retractions.system_object_mappings.insert(item_id, entry);
838 return;
839 }
840
841 if let Some(entry) = retractions.system_object_mappings.remove(&item_id) {
842 self.insert_entry(entry);
847 return;
848 }
849
850 let builtin = BUILTIN_LOOKUP
851 .get(&system_object_mapping.description)
852 .expect("missing builtin")
853 .1;
854 let schema_name = builtin.schema();
855 let schema_id = self
856 .ambient_schemas_by_name
857 .get(schema_name)
858 .unwrap_or_else(|| panic!("unknown ambient schema: {schema_name}"));
859 let name = QualifiedItemName {
860 qualifiers: ItemQualifiers {
861 database_spec: ResolvedDatabaseSpecifier::Ambient,
862 schema_spec: SchemaSpecifier::Id(*schema_id),
863 },
864 item: builtin.name().into(),
865 };
866 match builtin {
867 Builtin::Log(log) => {
868 let mut acl_items = vec![rbac::owner_privilege(
869 mz_sql::catalog::ObjectType::Source,
870 MZ_SYSTEM_ROLE_ID,
871 )];
872 acl_items.extend_from_slice(&log.access);
873 self.insert_item(
874 item_id,
875 log.oid,
876 name.clone(),
877 CatalogItem::Log(Log {
878 variant: log.variant,
879 global_id,
880 }),
881 MZ_SYSTEM_ROLE_ID,
882 PrivilegeMap::from_mz_acl_items(acl_items),
883 );
884 }
885
886 Builtin::Table(table) => {
887 let mut acl_items = vec![rbac::owner_privilege(
888 mz_sql::catalog::ObjectType::Table,
889 MZ_SYSTEM_ROLE_ID,
890 )];
891 acl_items.extend_from_slice(&table.access);
892
893 self.insert_item(
894 item_id,
895 table.oid,
896 name.clone(),
897 CatalogItem::Table(Table {
898 create_sql: None,
899 desc: VersionedRelationDesc::new(table.desc.clone()),
900 collections: [(RelationVersion::root(), global_id)].into_iter().collect(),
901 conn_id: None,
902 resolved_ids: ResolvedIds::empty(),
903 custom_logical_compaction_window: table.is_retained_metrics_object.then(
904 || {
905 self.system_config()
906 .metrics_retention()
907 .try_into()
908 .expect("invalid metrics retention")
909 },
910 ),
911 is_retained_metrics_object: table.is_retained_metrics_object,
912 data_source: TableDataSource::TableWrites {
913 defaults: vec![Expr::null(); table.desc.arity()],
914 },
915 }),
916 MZ_SYSTEM_ROLE_ID,
917 PrivilegeMap::from_mz_acl_items(acl_items),
918 );
919 }
920 Builtin::Index(index) => {
921 let custom_logical_compaction_window =
922 index.is_retained_metrics_object.then(|| {
923 self.system_config()
924 .metrics_retention()
925 .try_into()
926 .expect("invalid metrics retention")
927 });
928 let versions = BTreeMap::new();
930
931 let item = self
932 .parse_item(
933 global_id,
934 &index.create_sql(),
935 &versions,
936 None,
937 index.is_retained_metrics_object,
938 custom_logical_compaction_window,
939 local_expression_cache,
940 None,
941 )
942 .unwrap_or_else(|e| {
943 panic!(
944 "internal error: failed to load bootstrap index:\n\
945 {}\n\
946 error:\n\
947 {:?}\n\n\
948 make sure that the schema name is specified in the builtin index's create sql statement.",
949 index.name, e
950 )
951 });
952 let CatalogItem::Index(_) = item else {
953 panic!(
954 "internal error: builtin index {}'s SQL does not begin with \"CREATE INDEX\".",
955 index.name
956 );
957 };
958
959 self.insert_item(
960 item_id,
961 index.oid,
962 name,
963 item,
964 MZ_SYSTEM_ROLE_ID,
965 PrivilegeMap::default(),
966 );
967 }
968 Builtin::View(_) => {
969 unreachable!("views added elsewhere");
971 }
972
973 Builtin::Type(typ) => {
975 let typ = self.resolve_builtin_type_references(typ);
976 if let CatalogType::Array { element_reference } = typ.details.typ {
977 let entry = self.get_entry_mut(&element_reference);
978 let item_type = match &mut entry.item {
979 CatalogItem::Type(item_type) => item_type,
980 _ => unreachable!("types can only reference other types"),
981 };
982 item_type.details.array_id = Some(item_id);
983 }
984
985 let schema_id = self.resolve_system_schema(typ.schema);
986
987 self.insert_item(
988 item_id,
989 typ.oid,
990 QualifiedItemName {
991 qualifiers: ItemQualifiers {
992 database_spec: ResolvedDatabaseSpecifier::Ambient,
993 schema_spec: SchemaSpecifier::Id(schema_id),
994 },
995 item: typ.name.to_owned(),
996 },
997 CatalogItem::Type(Type {
998 create_sql: None,
999 global_id,
1000 details: typ.details.clone(),
1001 resolved_ids: ResolvedIds::empty(),
1002 }),
1003 MZ_SYSTEM_ROLE_ID,
1004 PrivilegeMap::from_mz_acl_items(vec![
1005 rbac::default_builtin_object_privilege(mz_sql::catalog::ObjectType::Type),
1006 rbac::owner_privilege(mz_sql::catalog::ObjectType::Type, MZ_SYSTEM_ROLE_ID),
1007 ]),
1008 );
1009 }
1010
1011 Builtin::Func(func) => {
1012 let oid = INVALID_OID;
1016 self.insert_item(
1017 item_id,
1018 oid,
1019 name.clone(),
1020 CatalogItem::Func(Func {
1021 inner: func.inner,
1022 global_id,
1023 }),
1024 MZ_SYSTEM_ROLE_ID,
1025 PrivilegeMap::default(),
1026 );
1027 }
1028
1029 Builtin::Source(coll) => {
1030 let mut acl_items = vec![rbac::owner_privilege(
1031 mz_sql::catalog::ObjectType::Source,
1032 MZ_SYSTEM_ROLE_ID,
1033 )];
1034 acl_items.extend_from_slice(&coll.access);
1035
1036 self.insert_item(
1037 item_id,
1038 coll.oid,
1039 name.clone(),
1040 CatalogItem::Source(Source {
1041 create_sql: None,
1042 data_source: coll.data_source.clone(),
1043 desc: coll.desc.clone(),
1044 global_id,
1045 timeline: Timeline::EpochMilliseconds,
1046 resolved_ids: ResolvedIds::empty(),
1047 custom_logical_compaction_window: coll.is_retained_metrics_object.then(
1048 || {
1049 self.system_config()
1050 .metrics_retention()
1051 .try_into()
1052 .expect("invalid metrics retention")
1053 },
1054 ),
1055 is_retained_metrics_object: coll.is_retained_metrics_object,
1056 }),
1057 MZ_SYSTEM_ROLE_ID,
1058 PrivilegeMap::from_mz_acl_items(acl_items),
1059 );
1060 }
1061 Builtin::MaterializedView(mv) => {
1062 let mut acl_items = vec![rbac::owner_privilege(
1063 mz_sql::catalog::ObjectType::MaterializedView,
1064 MZ_SYSTEM_ROLE_ID,
1065 )];
1066 acl_items.extend_from_slice(&mv.access);
1067
1068 let custom_logical_compaction_window = mv.is_retained_metrics_object.then(|| {
1069 self.system_config()
1070 .metrics_retention()
1071 .try_into()
1072 .expect("invalid metrics retention")
1073 });
1074
1075 let versions = BTreeMap::new();
1077
1078 let mut item = self
1079 .parse_item(
1080 global_id,
1081 &mv.create_sql(),
1082 &versions,
1083 None,
1084 mv.is_retained_metrics_object,
1085 custom_logical_compaction_window,
1086 local_expression_cache,
1087 None,
1088 )
1089 .unwrap_or_else(|e| {
1090 panic!(
1091 "internal error: failed to load bootstrap materialized view:\n\
1092 {}\n\
1093 error:\n\
1094 {e:?}\n\n\
1095 make sure that the schema name is specified in the builtin \
1096 materialized view's create sql statement.",
1097 mv.name,
1098 )
1099 });
1100 let CatalogItem::MaterializedView(catalog_mv) = &mut item else {
1101 panic!(
1102 "internal error: builtin materialized view {}'s SQL does not begin \
1103 with \"CREATE MATERIALIZED VIEW\".",
1104 mv.name,
1105 );
1106 };
1107
1108 let mut desc = catalog_mv.desc.latest();
1112 for key in &mv.desc.typ().keys {
1113 desc = desc.with_key(key.clone());
1114 }
1115 catalog_mv.desc = VersionedRelationDesc::new(desc);
1116
1117 self.insert_item(
1118 item_id,
1119 mv.oid,
1120 name,
1121 item,
1122 MZ_SYSTEM_ROLE_ID,
1123 PrivilegeMap::from_mz_acl_items(acl_items),
1124 );
1125 }
1126 Builtin::Connection(connection) => {
1127 let versions = BTreeMap::new();
1129 let mut item = self
1130 .parse_item(
1131 global_id,
1132 connection.sql,
1133 &versions,
1134 None,
1135 false,
1136 None,
1137 local_expression_cache,
1138 None,
1139 )
1140 .unwrap_or_else(|e| {
1141 panic!(
1142 "internal error: failed to load bootstrap connection:\n\
1143 {}\n\
1144 error:\n\
1145 {:?}\n\n\
1146 make sure that the schema name is specified in the builtin connection's create sql statement.",
1147 connection.name, e
1148 )
1149 });
1150 let CatalogItem::Connection(_) = &mut item else {
1151 panic!(
1152 "internal error: builtin connection {}'s SQL does not begin with \"CREATE CONNECTION\".",
1153 connection.name
1154 );
1155 };
1156
1157 let mut acl_items = vec![rbac::owner_privilege(
1158 mz_sql::catalog::ObjectType::Connection,
1159 connection.owner_id.clone(),
1160 )];
1161 acl_items.extend_from_slice(connection.access);
1162
1163 self.insert_item(
1164 item_id,
1165 connection.oid,
1166 name.clone(),
1167 item,
1168 connection.owner_id.clone(),
1169 PrivilegeMap::from_mz_acl_items(acl_items),
1170 );
1171 }
1172 }
1173 }
1174
1175 fn is_nonlocal_ephemeral_item_update(&self, update: &StateUpdate) -> bool {
1178 let StateUpdateKind::Item(item) = &update.kind else {
1179 return false;
1180 };
1181 let Some(owner) = item.ephemeral_owner_session else {
1183 return false;
1184 };
1185 match update.diff {
1186 StateDiff::Addition => !self.temporary_namespaces.contains_uuid(&owner),
1188 StateDiff::Retraction => !self.entry_by_id.contains_key(&item.id),
1191 }
1192 }
1193
1194 #[instrument(level = "debug")]
1195 fn apply_item_update(
1196 &mut self,
1197 item: mz_catalog::durable::Item,
1198 diff: StateDiff,
1199 retractions: &mut InProgressRetractions,
1200 local_expression_cache: &mut LocalExpressionCache,
1201 ) -> Result<(), CatalogError> {
1202 match diff {
1203 StateDiff::Addition => {
1204 let key = item.key();
1205 let mz_catalog::durable::Item {
1206 id,
1207 oid,
1208 global_id,
1209 schema_id,
1210 name,
1211 create_sql,
1212 owner_id,
1213 privileges,
1214 extra_versions,
1215 ephemeral_owner_session,
1216 } = item;
1217
1218 let conn_id = ephemeral_owner_session.map(|owner| {
1225 self.temporary_namespaces
1226 .conn_for_uuid(&owner)
1227 .cloned()
1228 .unwrap_or_else(|| {
1229 panic!("no session record applied for temporary item owner {owner}")
1230 })
1231 });
1232 let name = match &conn_id {
1233 Some(_) => QualifiedItemName {
1234 qualifiers: ItemQualifiers {
1235 database_spec: ResolvedDatabaseSpecifier::Ambient,
1236 schema_spec: SchemaSpecifier::Temporary,
1237 },
1238 item: name.clone(),
1239 },
1240 None => {
1241 let schema = self.find_non_temp_schema(&schema_id);
1242 QualifiedItemName {
1243 qualifiers: ItemQualifiers {
1244 database_spec: schema.database().clone(),
1245 schema_spec: schema.id().clone(),
1246 },
1247 item: name.clone(),
1248 }
1249 }
1250 };
1251 let entry = match retractions.items.remove(&key) {
1252 Some(mut retraction) => {
1253 assert_eq!(retraction.id, id);
1254
1255 if retraction.create_sql() != create_sql {
1260 let mut catalog_item = self
1261 .deserialize_item(
1262 global_id,
1263 &create_sql,
1264 &extra_versions,
1265 local_expression_cache,
1266 Some(retraction.item),
1267 )
1268 .unwrap_or_else(|e| {
1269 panic!("{e:?}: invalid persisted SQL: {create_sql}")
1270 });
1271 if conn_id.is_some() {
1272 catalog_item.set_conn_id(conn_id.clone());
1280 catalog_item.set_create_sql(create_sql);
1292 }
1293 retraction.item = catalog_item;
1294 }
1295
1296 retraction.id = id;
1297 retraction.oid = oid;
1298 retraction.name = name;
1299 retraction.owner_id = owner_id;
1300 retraction.privileges = PrivilegeMap::from_mz_acl_items(privileges);
1301 retraction
1302 }
1303 None => {
1304 let mut catalog_item = self
1305 .deserialize_item(
1306 global_id,
1307 &create_sql,
1308 &extra_versions,
1309 local_expression_cache,
1310 None,
1311 )
1312 .unwrap_or_else(|e| {
1313 panic!("{e:?}: invalid persisted SQL: {create_sql}")
1314 });
1315
1316 if conn_id.is_some() {
1317 catalog_item.set_conn_id(conn_id.clone());
1319 catalog_item.set_create_sql(create_sql);
1320 }
1321
1322 CatalogEntry {
1323 item: catalog_item,
1324 referenced_by: Vec::new(),
1325 used_by: Vec::new(),
1326 id,
1327 oid,
1328 name,
1329 owner_id,
1330 privileges: PrivilegeMap::from_mz_acl_items(privileges),
1331 }
1332 }
1333 };
1334
1335 self.insert_entry(entry);
1336 }
1337 StateDiff::Retraction => {
1338 let entry = self.drop_item(item.id);
1339 let key = item.into_key_value().0;
1340 retractions.items.insert(key, entry);
1341 }
1342 }
1343 Ok(())
1344 }
1345
1346 #[instrument(level = "debug")]
1347 fn apply_comment_update(
1348 &mut self,
1349 comment: mz_catalog::durable::Comment,
1350 diff: StateDiff,
1351 _retractions: &mut InProgressRetractions,
1352 ) {
1353 match diff {
1354 StateDiff::Addition => {
1355 let prev = Arc::make_mut(&mut self.comments).update_comment(
1356 comment.object_id,
1357 comment.sub_component,
1358 Some(comment.comment),
1359 );
1360 assert_eq!(
1361 prev, None,
1362 "values must be explicitly retracted before inserting a new value"
1363 );
1364 }
1365 StateDiff::Retraction => {
1366 let prev = Arc::make_mut(&mut self.comments).update_comment(
1367 comment.object_id,
1368 comment.sub_component,
1369 None,
1370 );
1371 assert_eq!(
1372 prev,
1373 Some(comment.comment),
1374 "retraction does not match existing value: ({:?}, {:?})",
1375 comment.object_id,
1376 comment.sub_component,
1377 );
1378 }
1379 }
1380 }
1381
1382 #[instrument(level = "debug")]
1383 fn apply_source_references_update(
1384 &mut self,
1385 source_references: mz_catalog::durable::SourceReferences,
1386 diff: StateDiff,
1387 _retractions: &mut InProgressRetractions,
1388 ) {
1389 match diff {
1390 StateDiff::Addition => {
1391 let prev = self
1392 .source_references
1393 .insert(source_references.source_id, source_references.into());
1394 assert!(
1395 prev.is_none(),
1396 "values must be explicitly retracted before inserting a new value: {prev:?}"
1397 );
1398 }
1399 StateDiff::Retraction => {
1400 let prev = self.source_references.remove(&source_references.source_id);
1401 assert!(
1402 prev.is_some(),
1403 "retraction for a non-existent existing value: {source_references:?}"
1404 );
1405 }
1406 }
1407 }
1408
1409 #[instrument(level = "debug")]
1410 fn apply_storage_collection_metadata_update(
1411 &mut self,
1412 storage_collection_metadata: mz_catalog::durable::StorageCollectionMetadata,
1413 diff: StateDiff,
1414 _retractions: &mut InProgressRetractions,
1415 ) {
1416 apply_inverted_lookup(
1417 &mut Arc::make_mut(&mut self.storage_metadata).collection_metadata,
1418 &storage_collection_metadata.id,
1419 storage_collection_metadata.shard,
1420 diff,
1421 );
1422 }
1423
1424 #[instrument(level = "debug")]
1425 fn apply_unfinalized_shard_update(
1426 &mut self,
1427 unfinalized_shard: mz_catalog::durable::UnfinalizedShard,
1428 diff: StateDiff,
1429 _retractions: &mut InProgressRetractions,
1430 ) {
1431 match diff {
1432 StateDiff::Addition => {
1433 let newly_inserted = Arc::make_mut(&mut self.storage_metadata)
1434 .unfinalized_shards
1435 .insert(unfinalized_shard.shard);
1436 assert!(
1437 newly_inserted,
1438 "values must be explicitly retracted before inserting a new value: {unfinalized_shard:?}",
1439 );
1440 }
1441 StateDiff::Retraction => {
1442 let removed = Arc::make_mut(&mut self.storage_metadata)
1443 .unfinalized_shards
1444 .remove(&unfinalized_shard.shard);
1445 assert!(
1446 removed,
1447 "retraction does not match existing value: {unfinalized_shard:?}"
1448 );
1449 }
1450 }
1451 }
1452
1453 #[instrument(level = "debug")]
1456 pub(crate) fn generate_builtin_table_update(
1457 &self,
1458 kind: StateUpdateKind,
1459 diff: StateDiff,
1460 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1461 let diff = diff.into();
1462 match kind {
1463 StateUpdateKind::Role(_) => Vec::new(),
1467 StateUpdateKind::RoleAuth(role_auth) => {
1468 vec![self.pack_role_auth_update(role_auth.role_id, diff)]
1469 }
1470 StateUpdateKind::DefaultPrivilege(_) => Vec::new(),
1474 StateUpdateKind::SystemPrivilege(_) => Vec::new(),
1475 StateUpdateKind::SystemConfiguration(_) => Vec::new(),
1476 StateUpdateKind::ClusterSystemConfiguration(_) => Vec::new(),
1482 StateUpdateKind::ReplicaSystemConfiguration(_) => Vec::new(),
1483 StateUpdateKind::Cluster(_) => Vec::new(),
1487 StateUpdateKind::IntrospectionSourceIndex(introspection_source_index) => {
1488 self.pack_item_update(introspection_source_index.item_id, diff)
1489 }
1490 StateUpdateKind::ClusterReplica(_) => Vec::new(),
1493 StateUpdateKind::ClusterReplicaSize(size) => {
1494 Self::pack_replica_size_update(&size.name, &size.allocation, diff)
1495 }
1496 StateUpdateKind::SystemObjectMapping(system_object_mapping) => {
1497 if !system_object_mapping.unique_identifier.runtime_alterable() {
1501 self.pack_item_update(system_object_mapping.unique_identifier.catalog_id, diff)
1502 } else {
1503 vec![]
1504 }
1505 }
1506 StateUpdateKind::Item(item) => self.pack_item_update(item.id, diff),
1507 StateUpdateKind::Comment(_) => Vec::new(),
1508 StateUpdateKind::SourceReferences(_) => Vec::new(),
1509 StateUpdateKind::AuditLog(_) => Vec::new(),
1513 StateUpdateKind::Database(_)
1514 | StateUpdateKind::Schema(_)
1515 | StateUpdateKind::NetworkPolicy(_)
1516 | StateUpdateKind::StorageCollectionMetadata(_)
1517 | StateUpdateKind::UnfinalizedShard(_) => Vec::new(),
1518 }
1519 }
1520
1521 fn get_entry_mut(&mut self, id: &CatalogItemId) -> &mut CatalogEntry {
1522 self.entry_by_id
1523 .get_mut(id)
1524 .unwrap_or_else(|| panic!("catalog out of sync, missing id {id}"))
1525 }
1526
1527 pub(super) fn set_optimized_plan(
1532 &mut self,
1533 id: GlobalId,
1534 plan: DataflowDescription<mz_expr::OptimizedMirRelationExpr>,
1535 ) {
1536 let item_id = self.entry_by_global_id[&id];
1537 let entry = self.get_entry_mut(&item_id);
1538 match entry.item_mut() {
1539 CatalogItem::Index(idx) => idx.optimized_plan = Some(Arc::new(plan)),
1540 CatalogItem::MaterializedView(mv) => mv.optimized_plan = Some(Arc::new(plan)),
1541 CatalogItem::MetricSink(ms) => ms.optimized_plan = Some(Arc::new(plan)),
1542 other => panic!("set_optimized_plan called on {} ({:?})", id, other.typ()),
1543 }
1544 }
1545
1546 pub(super) fn set_physical_plan(
1551 &mut self,
1552 id: GlobalId,
1553 plan: mz_compute_types::dataflows::LirDataflowDescription,
1554 ) {
1555 let item_id = self.entry_by_global_id[&id];
1556 let entry = self.get_entry_mut(&item_id);
1557 match entry.item_mut() {
1558 CatalogItem::Index(idx) => idx.physical_plan = Some(Arc::new(plan)),
1559 CatalogItem::MaterializedView(mv) => mv.physical_plan = Some(Arc::new(plan)),
1560 CatalogItem::MetricSink(ms) => ms.physical_plan = Some(Arc::new(plan)),
1561 other => panic!("set_physical_plan called on {} ({:?})", id, other.typ()),
1562 }
1563 }
1564
1565 pub(super) fn set_dataflow_metainfo(
1570 &mut self,
1571 id: GlobalId,
1572 metainfo: DataflowMetainfo<Arc<OptimizerNotice>>,
1573 ) {
1574 for notice in metainfo.optimizer_notices.iter() {
1576 for dep_id in notice.dependencies.iter() {
1577 self.notices_by_dep_id
1578 .entry(*dep_id)
1579 .or_default()
1580 .push(Arc::clone(notice));
1581 }
1582 if let Some(item_id) = notice.item_id {
1583 soft_assert_eq_or_log!(
1584 item_id,
1585 id,
1586 "notice.item_id should match the id for whom we are saving the notice"
1587 );
1588 }
1589 }
1590 let item_id = self.entry_by_global_id[&id];
1592 let entry = self.get_entry_mut(&item_id);
1593 match entry.item_mut() {
1594 CatalogItem::Index(idx) => idx.dataflow_metainfo = Some(metainfo),
1595 CatalogItem::MaterializedView(mv) => mv.dataflow_metainfo = Some(metainfo),
1596 CatalogItem::MetricSink(ms) => ms.dataflow_metainfo = Some(metainfo),
1597 other => panic!("set_dataflow_metainfo called on {} ({:?})", id, other.typ()),
1598 }
1599 }
1600
1601 #[mz_ore::instrument(level = "trace")]
1612 pub(super) fn drop_optimizer_notices(
1613 &mut self,
1614 dropped_entries: Vec<CatalogEntry>,
1615 ) -> BTreeSet<Arc<OptimizerNotice>> {
1616 let mut dropped_notices = BTreeSet::new();
1617 let mut drop_ids = BTreeSet::new();
1618
1619 for mut entry in dropped_entries {
1621 drop_ids.extend(entry.global_ids());
1622 if let Some(metainfo) = entry.item_mut().dataflow_metainfo_mut() {
1623 soft_assert_or_log!(
1624 metainfo.optimizer_notices.iter().all_unique(),
1625 "should have been pushed there by \
1626 `push_optimizer_notice_dedup`"
1627 );
1628 for n in metainfo.optimizer_notices.drain(..) {
1629 for dep_id in n.dependencies.iter() {
1632 if let Some(notices) = self.notices_by_dep_id.get_mut(dep_id) {
1633 notices.retain(|x| &n != x);
1634 if notices.is_empty() {
1635 self.notices_by_dep_id.remove(dep_id);
1636 }
1637 }
1638 }
1639 dropped_notices.insert(n);
1640 }
1641 }
1642 }
1643
1644 for id in &drop_ids {
1648 if let Some(notices) = self.notices_by_dep_id.remove(id) {
1649 for n in notices.into_iter() {
1650 if let Some(item_id) = n.item_id.as_ref() {
1655 if let Some(entry) = self.try_get_entry_by_global_id(item_id) {
1656 let catalog_item_id = entry.id();
1657 let entry = self.get_entry_mut(&catalog_item_id);
1658 if let Some(m) = entry.item_mut().dataflow_metainfo_mut() {
1659 m.optimizer_notices.retain(|x| &n != x);
1660 }
1661 }
1662 }
1663 dropped_notices.insert(n);
1664 }
1665 }
1666 }
1667
1668 let todo_dep_ids: BTreeSet<GlobalId> = dropped_notices
1671 .iter()
1672 .flat_map(|n| n.dependencies.iter())
1673 .filter(|dep_id| !drop_ids.contains(dep_id))
1674 .copied()
1675 .collect();
1676 for id in todo_dep_ids {
1677 if let Some(notices) = self.notices_by_dep_id.get_mut(&id) {
1678 notices.retain(|n| !dropped_notices.contains(n));
1679 if notices.is_empty() {
1680 self.notices_by_dep_id.remove(&id);
1681 }
1682 }
1683 }
1684
1685 dropped_notices
1686 }
1687
1688 fn get_schema_mut(
1689 &mut self,
1690 database_spec: &ResolvedDatabaseSpecifier,
1691 schema_spec: &SchemaSpecifier,
1692 conn_id: &ConnectionId,
1693 ) -> &mut Schema {
1694 match (database_spec, schema_spec) {
1696 (ResolvedDatabaseSpecifier::Ambient, SchemaSpecifier::Temporary) => self
1697 .temporary_namespaces
1698 .schema_mut(conn_id)
1699 .expect("catalog out of sync"),
1700 (ResolvedDatabaseSpecifier::Ambient, SchemaSpecifier::Id(id)) => self
1701 .ambient_schemas_by_id
1702 .get_mut(id)
1703 .expect("catalog out of sync"),
1704 (ResolvedDatabaseSpecifier::Id(database_id), SchemaSpecifier::Id(schema_id)) => self
1705 .database_by_id
1706 .get_mut(database_id)
1707 .expect("catalog out of sync")
1708 .schemas_by_id
1709 .get_mut(schema_id)
1710 .expect("catalog out of sync"),
1711 (ResolvedDatabaseSpecifier::Id(_), SchemaSpecifier::Temporary) => {
1712 unreachable!("temporary schemas are in the ambient database")
1713 }
1714 }
1715 }
1716
1717 #[instrument(name = "catalog::parse_views")]
1727 async fn parse_builtin_views(
1728 state: &mut CatalogState,
1729 builtin_views: Vec<(&'static BuiltinView, CatalogItemId, GlobalId)>,
1730 retractions: &mut InProgressRetractions,
1731 local_expression_cache: &mut LocalExpressionCache,
1732 ) -> Vec<BuiltinTableUpdate<&'static BuiltinTable>> {
1733 let mut builtin_table_updates = Vec::with_capacity(builtin_views.len());
1734 let (updates, additions): (Vec<_>, Vec<_>) =
1735 builtin_views
1736 .into_iter()
1737 .partition_map(|(view, item_id, gid)| {
1738 match retractions.system_object_mappings.remove(&item_id) {
1739 Some(entry) => Either::Left(entry),
1740 None => Either::Right((view, item_id, gid)),
1741 }
1742 });
1743
1744 for entry in updates {
1745 let item_id = entry.id();
1750 state.insert_entry(entry);
1751 builtin_table_updates.extend(state.pack_item_update(item_id, Diff::ONE));
1752 }
1753
1754 let mut handles = Vec::new();
1755 let mut awaiting_id_dependencies: BTreeMap<CatalogItemId, Vec<CatalogItemId>> =
1756 BTreeMap::new();
1757 let mut awaiting_name_dependencies: BTreeMap<String, Vec<CatalogItemId>> = BTreeMap::new();
1758 let mut awaiting_all = Vec::new();
1761 let mut completed_ids: BTreeSet<CatalogItemId> = BTreeSet::new();
1763 let mut completed_names: BTreeSet<String> = BTreeSet::new();
1764
1765 let mut views: BTreeMap<CatalogItemId, (&BuiltinView, GlobalId)> = additions
1767 .into_iter()
1768 .map(|(view, item_id, gid)| (item_id, (view, gid)))
1769 .collect();
1770 let item_ids: Vec<_> = views.keys().copied().collect();
1771
1772 let mut ready: VecDeque<CatalogItemId> = views.keys().cloned().collect();
1773 while !handles.is_empty() || !ready.is_empty() || !awaiting_all.is_empty() {
1774 if handles.is_empty() && ready.is_empty() {
1775 ready.extend(awaiting_all.drain(..));
1777 }
1778
1779 if !ready.is_empty() {
1781 let spawn_state = Arc::new(state.clone());
1782 while let Some(id) = ready.pop_front() {
1783 let (view, global_id) = views.get(&id).expect("must exist");
1784 let global_id = *global_id;
1785 let create_sql = view.create_sql();
1786 let versions = BTreeMap::new();
1788
1789 let span = info_span!(parent: None, "parse builtin view", name = view.name);
1790 OpenTelemetryContext::obtain().attach_as_parent_to(&span);
1791 let task_state = Arc::clone(&spawn_state);
1792 let cached_expr = local_expression_cache.remove_cached_expression(&global_id);
1793 let handle = mz_ore::task::spawn_blocking(
1794 || "parse view",
1795 move || {
1796 span.in_scope(|| {
1797 let res = task_state.parse_item_inner(
1798 global_id,
1799 &create_sql,
1800 &versions,
1801 None,
1802 false,
1803 None,
1804 cached_expr,
1805 None,
1806 );
1807 (id, global_id, res)
1808 })
1809 },
1810 );
1811 handles.push(handle);
1812 }
1813 }
1814
1815 let (selected, _idx, remaining) = future::select_all(handles).await;
1817 handles = remaining;
1818 let (id, global_id, res) = selected;
1819 let mut insert_cached_expr = |cached_expr| {
1820 if let Some(cached_expr) = cached_expr {
1821 local_expression_cache.insert_cached_expression(global_id, cached_expr);
1822 }
1823 };
1824 match res {
1825 Ok((item, uncached_expr)) => {
1826 if let Some((uncached_expr, optimizer_features)) = uncached_expr {
1827 local_expression_cache.insert_uncached_expression(
1828 global_id,
1829 uncached_expr,
1830 optimizer_features,
1831 RelationVersion::root(),
1832 );
1833 }
1834 let (view, _gid) = views.remove(&id).expect("must exist");
1836 let schema_id = state
1837 .ambient_schemas_by_name
1838 .get(view.schema)
1839 .unwrap_or_else(|| panic!("unknown ambient schema: {}", view.schema));
1840 let qname = QualifiedItemName {
1841 qualifiers: ItemQualifiers {
1842 database_spec: ResolvedDatabaseSpecifier::Ambient,
1843 schema_spec: SchemaSpecifier::Id(*schema_id),
1844 },
1845 item: view.name.into(),
1846 };
1847 let mut acl_items = vec![rbac::owner_privilege(
1848 mz_sql::catalog::ObjectType::View,
1849 MZ_SYSTEM_ROLE_ID,
1850 )];
1851 acl_items.extend_from_slice(&view.access);
1852
1853 state.insert_item(
1854 id,
1855 view.oid,
1856 qname,
1857 item,
1858 MZ_SYSTEM_ROLE_ID,
1859 PrivilegeMap::from_mz_acl_items(acl_items),
1860 );
1861
1862 let mut resolved_dependent_items = Vec::new();
1864 if let Some(dependent_items) = awaiting_id_dependencies.remove(&id) {
1865 resolved_dependent_items.extend(dependent_items);
1866 }
1867 let entry = state.get_entry(&id);
1868 let full_name = state.resolve_full_name(entry.name(), None).to_string();
1869 if let Some(dependent_items) = awaiting_name_dependencies.remove(&full_name) {
1870 resolved_dependent_items.extend(dependent_items);
1871 }
1872 ready.extend(resolved_dependent_items);
1873
1874 completed_ids.insert(id);
1875 completed_names.insert(full_name);
1876 }
1877 Err((
1879 AdapterError::PlanError(plan::PlanError::InvalidId(missing_dep)),
1880 cached_expr,
1881 )) => {
1882 insert_cached_expr(cached_expr);
1883 if completed_ids.contains(&missing_dep) {
1884 ready.push_back(id);
1885 } else {
1886 awaiting_id_dependencies
1887 .entry(missing_dep)
1888 .or_default()
1889 .push(id);
1890 }
1891 }
1892 Err((
1894 AdapterError::PlanError(plan::PlanError::Catalog(
1895 SqlCatalogError::UnknownItem(missing_dep),
1896 )),
1897 cached_expr,
1898 )) => {
1899 insert_cached_expr(cached_expr);
1900 match CatalogItemId::from_str(&missing_dep) {
1901 Ok(missing_dep) => {
1902 if completed_ids.contains(&missing_dep) {
1903 ready.push_back(id);
1904 } else {
1905 awaiting_id_dependencies
1906 .entry(missing_dep)
1907 .or_default()
1908 .push(id);
1909 }
1910 }
1911 Err(_) => {
1912 if completed_names.contains(&missing_dep) {
1913 ready.push_back(id);
1914 } else {
1915 awaiting_name_dependencies
1916 .entry(missing_dep)
1917 .or_default()
1918 .push(id);
1919 }
1920 }
1921 }
1922 }
1923 Err((
1924 AdapterError::PlanError(plan::PlanError::InvalidCast { .. }),
1925 cached_expr,
1926 )) => {
1927 insert_cached_expr(cached_expr);
1928 awaiting_all.push(id);
1929 }
1930 Err((e, _)) => {
1931 let (bad_view, _gid) = views.get(&id).expect("must exist");
1932 panic!(
1933 "internal error: failed to load bootstrap view:\n\
1934 {name}\n\
1935 error:\n\
1936 {e:?}\n\n\
1937 Make sure that the schema name is specified in the builtin view's create sql statement.
1938 ",
1939 name = bad_view.name,
1940 )
1941 }
1942 }
1943 }
1944
1945 assert!(awaiting_id_dependencies.is_empty());
1946 assert!(
1947 awaiting_name_dependencies.is_empty(),
1948 "awaiting_name_dependencies: {awaiting_name_dependencies:?}"
1949 );
1950 assert!(awaiting_all.is_empty());
1951 assert!(views.is_empty());
1952
1953 builtin_table_updates.extend(
1955 item_ids
1956 .into_iter()
1957 .flat_map(|id| state.pack_item_update(id, Diff::ONE)),
1958 );
1959
1960 builtin_table_updates
1961 }
1962
1963 fn insert_entry(&mut self, entry: CatalogEntry) {
1965 if !entry.id.is_system() {
1966 if let Some(cluster_id) = entry.item.cluster_id() {
1967 self.clusters_by_id
1968 .get_mut(&cluster_id)
1969 .expect("catalog out of sync")
1970 .bound_objects
1971 .insert(entry.id);
1972 };
1973 }
1974
1975 for u in entry.references().items() {
1976 match self.entry_by_id.get_mut(u) {
1977 Some(metadata) => metadata.referenced_by.push(entry.id()),
1978 None => panic!(
1979 "Catalog: missing dependent catalog item {} while installing {}",
1980 u,
1981 self.resolve_full_name(entry.name(), entry.conn_id())
1982 ),
1983 }
1984 }
1985 for u in entry.uses() {
1986 if u == entry.id() {
1989 continue;
1990 }
1991 match self.entry_by_id.get_mut(&u) {
1992 Some(metadata) => metadata.used_by.push(entry.id()),
1993 None => panic!(
1994 "Catalog: missing dependent catalog item {} while installing {}",
1995 u,
1996 self.resolve_full_name(entry.name(), entry.conn_id())
1997 ),
1998 }
1999 }
2000 for gid in entry.item.global_ids() {
2001 self.entry_by_global_id.insert(gid, entry.id());
2002 }
2003 let conn_id = entry.item().conn_id().unwrap_or(&SYSTEM_CONN_ID);
2004 if entry.name().qualifiers.schema_spec == SchemaSpecifier::Temporary {
2007 self.temporary_namespaces
2008 .ensure_schema(conn_id, entry.owner_id);
2009 }
2010 let schema = self.get_schema_mut(
2011 &entry.name().qualifiers.database_spec,
2012 &entry.name().qualifiers.schema_spec,
2013 conn_id,
2014 );
2015
2016 let prev_id = match entry.item() {
2017 CatalogItem::Func(_) => schema
2018 .functions
2019 .insert(entry.name().item.clone(), entry.id()),
2020 CatalogItem::Type(_) => schema.types.insert(entry.name().item.clone(), entry.id()),
2021 _ => schema.items.insert(entry.name().item.clone(), entry.id()),
2022 };
2023
2024 assert!(
2025 prev_id.is_none(),
2026 "builtin name collision on {:?}",
2027 entry.name().item.clone()
2028 );
2029
2030 self.entry_by_id.insert(entry.id(), entry.clone());
2031 }
2032
2033 fn insert_item(
2035 &mut self,
2036 id: CatalogItemId,
2037 oid: u32,
2038 name: QualifiedItemName,
2039 item: CatalogItem,
2040 owner_id: RoleId,
2041 privileges: PrivilegeMap,
2042 ) {
2043 let entry = CatalogEntry {
2044 item,
2045 name,
2046 id,
2047 oid,
2048 used_by: Vec::new(),
2049 referenced_by: Vec::new(),
2050 owner_id,
2051 privileges,
2052 };
2053
2054 self.insert_entry(entry);
2055 }
2056
2057 #[mz_ore::instrument(level = "trace")]
2058 fn drop_item(&mut self, id: CatalogItemId) -> CatalogEntry {
2059 let metadata = self.entry_by_id.remove(&id).expect("catalog out of sync");
2060 for u in metadata.references().items() {
2061 if let Some(dep_metadata) = self.entry_by_id.get_mut(u) {
2062 dep_metadata.referenced_by.retain(|u| *u != metadata.id())
2063 }
2064 }
2065 for u in metadata.uses() {
2066 if let Some(dep_metadata) = self.entry_by_id.get_mut(&u) {
2067 dep_metadata.used_by.retain(|u| *u != metadata.id())
2068 }
2069 }
2070 for gid in metadata.global_ids() {
2071 self.entry_by_global_id.remove(&gid);
2072 }
2073
2074 let conn_id = metadata.item().conn_id().unwrap_or(&SYSTEM_CONN_ID);
2075 let schema = self.get_schema_mut(
2076 &metadata.name().qualifiers.database_spec,
2077 &metadata.name().qualifiers.schema_spec,
2078 conn_id,
2079 );
2080 if metadata.item_type() == CatalogItemType::Type {
2081 schema
2082 .types
2083 .remove(&metadata.name().item)
2084 .expect("catalog out of sync");
2085 } else {
2086 assert_ne!(metadata.item_type(), CatalogItemType::Func);
2089
2090 schema
2091 .items
2092 .remove(&metadata.name().item)
2093 .expect("catalog out of sync");
2094 };
2095
2096 if !id.is_system() {
2097 if let Some(cluster_id) = metadata.item().cluster_id() {
2098 assert!(
2099 self.clusters_by_id
2100 .get_mut(&cluster_id)
2101 .expect("catalog out of sync")
2102 .bound_objects
2103 .remove(&id),
2104 "catalog out of sync"
2105 );
2106 }
2107 }
2108
2109 metadata
2110 }
2111
2112 fn insert_introspection_source_index(
2113 &mut self,
2114 cluster_id: ClusterId,
2115 log: &'static BuiltinLog,
2116 item_id: CatalogItemId,
2117 global_id: GlobalId,
2118 oid: u32,
2119 ) {
2120 let (index_name, index) =
2121 self.create_introspection_source_index(cluster_id, log, global_id);
2122 self.insert_item(
2123 item_id,
2124 oid,
2125 index_name,
2126 index,
2127 MZ_SYSTEM_ROLE_ID,
2128 PrivilegeMap::default(),
2129 );
2130 }
2131
2132 fn create_introspection_source_index(
2133 &self,
2134 cluster_id: ClusterId,
2135 log: &'static BuiltinLog,
2136 global_id: GlobalId,
2137 ) -> (QualifiedItemName, CatalogItem) {
2138 let source_name = FullItemName {
2139 database: RawDatabaseSpecifier::Ambient,
2140 schema: log.schema.into(),
2141 item: log.name.into(),
2142 };
2143 let index_name = format!("{}_{}_primary_idx", log.name, cluster_id);
2144 let mut index_name = QualifiedItemName {
2145 qualifiers: ItemQualifiers {
2146 database_spec: ResolvedDatabaseSpecifier::Ambient,
2147 schema_spec: SchemaSpecifier::Id(self.get_mz_introspection_schema_id()),
2148 },
2149 item: index_name.clone(),
2150 };
2151 index_name = self.find_available_name(index_name, &SYSTEM_CONN_ID);
2152 let index_item_name = index_name.item.clone();
2153 let (log_item_id, log_global_id) = self.resolve_builtin_log(log);
2154 let index = CatalogItem::Index(Index {
2155 global_id,
2156 on: log_global_id,
2157 keys: log
2158 .variant
2159 .index_by()
2160 .into_iter()
2161 .map(MirScalarExpr::column)
2162 .collect(),
2163 create_sql: index_sql(
2164 index_item_name,
2165 cluster_id,
2166 source_name,
2167 &log.variant.desc(),
2168 &log.variant.index_by(),
2169 ),
2170 conn_id: None,
2171 resolved_ids: [(log_item_id, log_global_id)].into_iter().collect(),
2172 cluster_id,
2173 is_retained_metrics_object: false,
2174 custom_logical_compaction_window: None,
2175 optimized_plan: None,
2176 physical_plan: None,
2177 dataflow_metainfo: None,
2178 });
2179 (index_name, index)
2180 }
2181
2182 fn insert_system_configuration(&mut self, name: &str, value: VarInput) -> Result<bool, Error> {
2187 Ok(Arc::make_mut(&mut self.system_configuration).set(name, value)?)
2188 }
2189
2190 fn remove_system_configuration(&mut self, name: &str) -> Result<bool, Error> {
2195 Ok(Arc::make_mut(&mut self.system_configuration).reset(name)?)
2196 }
2197}
2198
2199fn sort_updates(updates: Vec<StateUpdate>) -> Vec<StateUpdate> {
2207 fn push_update<T>(
2208 update: T,
2209 diff: StateDiff,
2210 retractions: &mut Vec<T>,
2211 additions: &mut Vec<T>,
2212 ) {
2213 match diff {
2214 StateDiff::Retraction => retractions.push(update),
2215 StateDiff::Addition => additions.push(update),
2216 }
2217 }
2218
2219 soft_assert_no_log!(
2220 updates.iter().map(|update| update.ts).all_equal(),
2221 "all timestamps should be equal: {updates:?}"
2222 );
2223 soft_assert_no_log!(
2224 {
2225 let mut dedup = BTreeSet::new();
2226 updates.iter().all(|update| dedup.insert(&update.kind))
2227 },
2228 "updates should be consolidated: {updates:?}"
2229 );
2230
2231 let mut pre_cluster_retractions = Vec::new();
2233 let mut pre_cluster_additions = Vec::new();
2234 let mut cluster_retractions = Vec::new();
2235 let mut cluster_additions = Vec::new();
2236 let mut builtin_item_updates = Vec::new();
2237 let mut item_retractions = Vec::new();
2238 let mut item_additions = Vec::new();
2239 let mut post_item_retractions = Vec::new();
2240 let mut post_item_additions = Vec::new();
2241 for update in updates {
2242 let diff = update.diff.clone();
2243 match update.kind {
2244 StateUpdateKind::Role(_)
2245 | StateUpdateKind::RoleAuth(_)
2246 | StateUpdateKind::Database(_)
2247 | StateUpdateKind::Schema(_)
2248 | StateUpdateKind::DefaultPrivilege(_)
2249 | StateUpdateKind::SystemPrivilege(_)
2250 | StateUpdateKind::SystemConfiguration(_)
2251 | StateUpdateKind::NetworkPolicy(_)
2252 | StateUpdateKind::ClusterReplicaSize(_) => push_update(
2253 update,
2254 diff,
2255 &mut pre_cluster_retractions,
2256 &mut pre_cluster_additions,
2257 ),
2258 StateUpdateKind::Cluster(_)
2259 | StateUpdateKind::ClusterSystemConfiguration(_)
2260 | StateUpdateKind::IntrospectionSourceIndex(_)
2261 | StateUpdateKind::ClusterReplica(_)
2262 | StateUpdateKind::ReplicaSystemConfiguration(_) => push_update(
2263 update,
2264 diff,
2265 &mut cluster_retractions,
2266 &mut cluster_additions,
2267 ),
2268 StateUpdateKind::SystemObjectMapping(system_object_mapping) => {
2269 builtin_item_updates.push((system_object_mapping, update.ts, update.diff))
2270 }
2271 StateUpdateKind::Item(item) => push_update(
2272 (item, update.ts, update.diff),
2273 diff,
2274 &mut item_retractions,
2275 &mut item_additions,
2276 ),
2277 StateUpdateKind::Comment(_)
2278 | StateUpdateKind::SourceReferences(_)
2279 | StateUpdateKind::AuditLog(_)
2280 | StateUpdateKind::StorageCollectionMetadata(_)
2281 | StateUpdateKind::UnfinalizedShard(_) => push_update(
2282 update,
2283 diff,
2284 &mut post_item_retractions,
2285 &mut post_item_additions,
2286 ),
2287 }
2288 }
2289
2290 let builtin_item_updates = builtin_item_updates
2293 .into_iter()
2294 .map(|(system_object_mapping, ts, diff)| {
2295 let idx = BUILTIN_LOOKUP
2296 .get(&system_object_mapping.description)
2297 .expect("missing builtin")
2298 .0;
2299 (idx, system_object_mapping, ts, diff)
2300 })
2301 .sorted_by_key(|(idx, _, _, _)| *idx)
2302 .map(|(_, system_object_mapping, ts, diff)| (system_object_mapping, ts, diff));
2303
2304 let mut builtin_source_retractions = Vec::new();
2308 let mut builtin_source_additions = Vec::new();
2309 let mut other_builtin_retractions = Vec::new();
2310 let mut other_builtin_additions = Vec::new();
2311 for (builtin_item_update, ts, diff) in builtin_item_updates {
2312 let object_type = builtin_item_update.description.object_type;
2313 let update = StateUpdate {
2314 kind: StateUpdateKind::SystemObjectMapping(builtin_item_update),
2315 ts,
2316 diff,
2317 };
2318 if object_type == CatalogItemType::Source {
2319 push_update(
2320 update,
2321 diff,
2322 &mut builtin_source_retractions,
2323 &mut builtin_source_additions,
2324 );
2325 } else {
2326 push_update(
2327 update,
2328 diff,
2329 &mut other_builtin_retractions,
2330 &mut other_builtin_additions,
2331 );
2332 }
2333 }
2334
2335 fn sort_items_topological(items: &mut Vec<(mz_catalog::durable::Item, Timestamp, StateDiff)>) {
2341 tracing::debug!(?items, "sorting items by dependencies");
2342
2343 let key_fn = |item: &(mz_catalog::durable::Item, _, _)| item.0.id;
2344 let dependencies_fn = |item: &(mz_catalog::durable::Item, _, _)| {
2345 let statement = mz_sql::parse::parse(&item.0.create_sql)
2346 .expect("valid create_sql")
2347 .into_element()
2348 .ast;
2349 mz_sql::names::dependencies(&statement).expect("failed to find dependencies of item")
2350 };
2351 sort_topological(items, key_fn, dependencies_fn);
2352 }
2353
2354 fn sort_item_updates(
2370 item_updates: Vec<(mz_catalog::durable::Item, Timestamp, StateDiff)>,
2371 ) -> VecDeque<(mz_catalog::durable::Item, Timestamp, StateDiff)> {
2372 let mut types = Vec::new();
2375 let mut funcs = Vec::new();
2378 let mut secrets = Vec::new();
2379 let mut connections = Vec::new();
2380 let mut sources = Vec::new();
2381 let mut tables = Vec::new();
2382 let mut derived_items = Vec::new();
2383 let mut sinks = Vec::new();
2384 for update in item_updates {
2385 match update.0.item_type() {
2386 CatalogItemType::Type => types.push(update),
2387 CatalogItemType::Func => funcs.push(update),
2388 CatalogItemType::Secret => secrets.push(update),
2389 CatalogItemType::Connection => connections.push(update),
2390 CatalogItemType::Source => sources.push(update),
2391 CatalogItemType::Table => tables.push(update),
2392 CatalogItemType::View
2393 | CatalogItemType::MaterializedView
2394 | CatalogItemType::Index
2395 | CatalogItemType::MetricSink => derived_items.push(update),
2396 CatalogItemType::Sink => sinks.push(update),
2397 }
2398 }
2399
2400 sort_items_topological(&mut connections);
2404 sort_items_topological(&mut derived_items);
2405
2406 for group in [
2408 &mut types,
2409 &mut funcs,
2410 &mut secrets,
2411 &mut sources,
2412 &mut tables,
2413 &mut sinks,
2414 ] {
2415 group.sort_by_key(|(item, _, _)| item.id);
2416 }
2417
2418 iter::empty()
2419 .chain(types)
2420 .chain(funcs)
2421 .chain(secrets)
2422 .chain(connections)
2423 .chain(sources)
2424 .chain(tables)
2425 .chain(derived_items)
2426 .chain(sinks)
2427 .collect()
2428 }
2429
2430 fn into_state_updates(
2434 item_updates: VecDeque<(mz_catalog::durable::Item, Timestamp, StateDiff)>,
2435 ) -> Vec<StateUpdate> {
2436 item_updates
2437 .into_iter()
2438 .map(|(item, ts, diff)| StateUpdate {
2439 kind: StateUpdateKind::Item(item),
2440 ts,
2441 diff,
2442 })
2443 .collect()
2444 }
2445 let item_retractions = into_state_updates(sort_item_updates(item_retractions));
2446 let item_additions = into_state_updates(sort_item_updates(item_additions));
2447
2448 iter::empty()
2450 .chain(post_item_retractions.into_iter().rev())
2452 .chain(item_retractions.into_iter().rev())
2453 .chain(other_builtin_retractions.into_iter().rev())
2454 .chain(cluster_retractions.into_iter().rev())
2455 .chain(builtin_source_retractions.into_iter().rev())
2456 .chain(pre_cluster_retractions.into_iter().rev())
2457 .chain(pre_cluster_additions)
2458 .chain(builtin_source_additions)
2459 .chain(cluster_additions)
2460 .chain(other_builtin_additions)
2461 .chain(item_additions)
2462 .chain(post_item_additions)
2463 .collect()
2464}
2465
2466enum ApplyState {
2471 BuiltinViewAdditions(Vec<(&'static BuiltinView, CatalogItemId, GlobalId)>),
2473 Items(Vec<StateUpdate>),
2479 Updates(Vec<StateUpdate>),
2481}
2482
2483impl ApplyState {
2484 fn new(update: StateUpdate) -> Self {
2485 use StateUpdateKind::*;
2486 match &update.kind {
2487 SystemObjectMapping(som)
2488 if som.description.object_type == CatalogItemType::View
2489 && update.diff == StateDiff::Addition =>
2490 {
2491 let view_addition = lookup_builtin_view_addition(som.clone());
2492 Self::BuiltinViewAdditions(vec![view_addition])
2493 }
2494
2495 IntrospectionSourceIndex(_) | SystemObjectMapping(_) | Item(_) => {
2496 Self::Items(vec![update])
2497 }
2498
2499 Role(_)
2500 | RoleAuth(_)
2501 | Database(_)
2502 | Schema(_)
2503 | DefaultPrivilege(_)
2504 | SystemPrivilege(_)
2505 | SystemConfiguration(_)
2506 | ClusterSystemConfiguration(_)
2507 | ReplicaSystemConfiguration(_)
2508 | Cluster(_)
2509 | NetworkPolicy(_)
2510 | ClusterReplicaSize(_)
2511 | ClusterReplica(_)
2512 | SourceReferences(_)
2513 | Comment(_)
2514 | AuditLog(_)
2515 | StorageCollectionMetadata(_)
2516 | UnfinalizedShard(_) => Self::Updates(vec![update]),
2517 }
2518 }
2519
2520 async fn apply(
2526 self,
2527 state: &mut CatalogState,
2528 retractions: &mut InProgressRetractions,
2529 local_expression_cache: &mut LocalExpressionCache,
2530 ) -> (
2531 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
2532 Vec<ParsedStateUpdate>,
2533 ) {
2534 match self {
2535 Self::BuiltinViewAdditions(builtin_view_additions) => {
2536 let restore = Arc::clone(&state.system_configuration);
2537 Arc::make_mut(&mut state.system_configuration).enable_for_item_parsing();
2538 let builtin_table_updates = CatalogState::parse_builtin_views(
2539 state,
2540 builtin_view_additions,
2541 retractions,
2542 local_expression_cache,
2543 )
2544 .await;
2545 state.system_configuration = restore;
2546 (builtin_table_updates, Vec::new())
2547 }
2548 Self::Items(updates) => state.with_enable_for_item_parsing(|state| {
2549 state
2550 .apply_updates_inner(updates, retractions, local_expression_cache)
2551 .expect("corrupt catalog")
2552 }),
2553 Self::Updates(updates) => state
2554 .apply_updates_inner(updates, retractions, local_expression_cache)
2555 .expect("corrupt catalog"),
2556 }
2557 }
2558
2559 async fn step(
2560 self,
2561 next: Self,
2562 state: &mut CatalogState,
2563 retractions: &mut InProgressRetractions,
2564 local_expression_cache: &mut LocalExpressionCache,
2565 ) -> (
2566 Self,
2567 (
2568 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
2569 Vec<ParsedStateUpdate>,
2570 ),
2571 ) {
2572 match (self, next) {
2573 (
2574 Self::BuiltinViewAdditions(mut builtin_view_additions),
2575 Self::BuiltinViewAdditions(next_builtin_view_additions),
2576 ) => {
2577 builtin_view_additions.extend(next_builtin_view_additions);
2579 (
2580 Self::BuiltinViewAdditions(builtin_view_additions),
2581 (Vec::new(), Vec::new()),
2582 )
2583 }
2584 (Self::Items(mut updates), Self::Items(next_updates)) => {
2585 updates.extend(next_updates);
2587 (Self::Items(updates), (Vec::new(), Vec::new()))
2588 }
2589 (Self::Updates(mut updates), Self::Updates(next_updates)) => {
2590 updates.extend(next_updates);
2592 (Self::Updates(updates), (Vec::new(), Vec::new()))
2593 }
2594 (apply_state, next_apply_state) => {
2595 let updates = apply_state
2597 .apply(state, retractions, local_expression_cache)
2598 .await;
2599 (next_apply_state, updates)
2600 }
2601 }
2602 }
2603}
2604
2605trait MutableMap<K, V> {
2608 fn insert(&mut self, key: K, value: V) -> Option<V>;
2609 fn remove(&mut self, key: &K) -> Option<V>;
2610}
2611
2612impl<K: Ord, V> MutableMap<K, V> for BTreeMap<K, V> {
2613 fn insert(&mut self, key: K, value: V) -> Option<V> {
2614 BTreeMap::insert(self, key, value)
2615 }
2616 fn remove(&mut self, key: &K) -> Option<V> {
2617 BTreeMap::remove(self, key)
2618 }
2619}
2620
2621impl<K: Ord + Clone, V: Clone> MutableMap<K, V> for imbl::OrdMap<K, V> {
2622 fn insert(&mut self, key: K, value: V) -> Option<V> {
2623 imbl::OrdMap::insert(self, key, value)
2624 }
2625 fn remove(&mut self, key: &K) -> Option<V> {
2626 imbl::OrdMap::remove(self, key)
2627 }
2628}
2629
2630fn apply_inverted_lookup<K, V>(map: &mut impl MutableMap<K, V>, key: &K, value: V, diff: StateDiff)
2635where
2636 K: Ord + Clone + Debug,
2637 V: PartialEq + Debug,
2638{
2639 match diff {
2640 StateDiff::Retraction => {
2641 let prev = map.remove(key);
2642 assert_eq!(
2643 prev,
2644 Some(value),
2645 "retraction does not match existing value: {key:?}"
2646 );
2647 }
2648 StateDiff::Addition => {
2649 let prev = map.insert(key.clone(), value);
2650 assert_eq!(
2651 prev, None,
2652 "values must be explicitly retracted before inserting a new value: {key:?}"
2653 );
2654 }
2655 }
2656}
2657
2658fn apply_with_update<K, V, D>(
2661 map: &mut impl MutableMap<K, V>,
2662 durable: D,
2663 key_fn: impl FnOnce(&D) -> K,
2664 diff: StateDiff,
2665 retractions: &mut BTreeMap<D::Key, V>,
2666) where
2667 K: Ord,
2668 V: UpdateFrom<D> + PartialEq + Debug,
2669 D: DurableType,
2670 D::Key: Ord,
2671{
2672 match diff {
2673 StateDiff::Retraction => {
2674 let mem_key = key_fn(&durable);
2675 let value = map
2676 .remove(&mem_key)
2677 .expect("retraction does not match existing value: {key:?}");
2678 let durable_key = durable.into_key_value().0;
2679 retractions.insert(durable_key, value);
2680 }
2681 StateDiff::Addition => {
2682 let mem_key = key_fn(&durable);
2683 let durable_key = durable.key();
2684 let value = match retractions.remove(&durable_key) {
2685 Some(mut retraction) => {
2686 retraction.update_from(durable);
2687 retraction
2688 }
2689 None => durable.into(),
2690 };
2691 let prev = map.insert(mem_key, value);
2692 assert_eq!(
2693 prev, None,
2694 "values must be explicitly retracted before inserting a new value"
2695 );
2696 }
2697 }
2698}
2699
2700fn lookup_builtin_view_addition(
2702 mapping: SystemObjectMapping,
2703) -> (&'static BuiltinView, CatalogItemId, GlobalId) {
2704 let (_, builtin) = BUILTIN_LOOKUP
2705 .get(&mapping.description)
2706 .expect("missing builtin view");
2707 let Builtin::View(view) = builtin else {
2708 unreachable!("programming error, expected BuiltinView found {builtin:?}");
2709 };
2710
2711 (
2712 view,
2713 mapping.unique_identifier.catalog_id,
2714 mapping.unique_identifier.global_id,
2715 )
2716}