1use std::collections::{BTreeMap, BTreeSet};
11use std::fmt::Debug;
12use std::num::NonZeroU32;
13use std::time::Duration;
14
15use anyhow::anyhow;
16use derivative::Derivative;
17use itertools::Itertools;
18use mz_audit_log::VersionedEvent;
19use mz_compute_client::logging::{ComputeLog, DifferentialLog, LogVariant, TimelyLog};
20use mz_controller::clusters::ReplicaAllocation;
21use mz_controller_types::{ClusterId, ReplicaId};
22use mz_ore::cast::{u64_to_usize, usize_to_u64};
23use mz_ore::collections::{CollectionExt, HashSet};
24use mz_ore::now::SYSTEM_TIME;
25use mz_ore::vec::VecExt;
26use mz_ore::{soft_assert_no_log, soft_assert_or_log, soft_panic_or_log};
27use mz_persist_types::ShardId;
28use mz_pgrepr::oid::FIRST_USER_OID;
29use mz_proto::TryFromProtoError;
30use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem};
31use mz_repr::cluster_replica_size_id::ClusterReplicaSizeId;
32use mz_repr::network_policy_id::NetworkPolicyId;
33use mz_repr::role_id::RoleId;
34use mz_repr::{CatalogItemId, Diff, GlobalId, RelationVersion};
35use mz_sql::catalog::{
36 CatalogError as SqlCatalogError, CatalogItemType, ObjectType, PasswordAction,
37 RoleAttributesRaw, RoleMembership, RoleVars,
38};
39use mz_sql::names::{CommentObjectId, DatabaseId, ResolvedDatabaseSpecifier, SchemaId};
40use mz_sql::plan::NetworkPolicyRule;
41use mz_sql_parser::ast::QualifiedReplica;
42use mz_storage_client::controller::StorageTxn;
43use mz_storage_types::controller::StorageError;
44use tracing::warn;
45use uuid::Uuid;
46
47use crate::builtin::BuiltinLog;
48use crate::durable::initialize::{
49 ENABLE_0DT_DEPLOYMENT_PANIC_AFTER_TIMEOUT, SYSTEM_CONFIG_SYNCED_KEY,
50 WITH_0DT_DEPLOYMENT_DDL_CHECK_INTERVAL, WITH_0DT_DEPLOYMENT_MAX_WAIT,
51};
52use crate::durable::objects::serialization::{RustType, proto};
53use crate::durable::objects::{
54 AuditLogKey, Cluster, ClusterConfig, ClusterIntrospectionSourceIndexKey,
55 ClusterIntrospectionSourceIndexValue, ClusterKey, ClusterReplica, ClusterReplicaKey,
56 ClusterReplicaSize, ClusterReplicaSizeKey, ClusterReplicaSizeValue, ClusterReplicaValue,
57 ClusterSystemConfiguration, ClusterSystemConfigurationKey, ClusterSystemConfigurationValue,
58 ClusterValue, CommentKey, CommentValue, Config, ConfigKey, ConfigValue, Database, DatabaseKey,
59 DatabaseValue, DefaultPrivilegesKey, DefaultPrivilegesValue, DurableType, GidMappingKey,
60 GidMappingValue, IdAllocKey, IdAllocValue, IndexColumnsKey, IndexColumnsValue,
61 IntrospectionSourceIndex, Item, ItemColumnsKey, ItemColumnsValue, ItemKey, ItemMetadata,
62 ItemValue, NetworkPolicyKey, NetworkPolicyValue, ReplicaConfig, ReplicaSystemConfiguration,
63 ReplicaSystemConfigurationKey, ReplicaSystemConfigurationValue, Role, RoleKey, RoleValue,
64 Schema, SchemaKey, SchemaValue, ServerConfigurationKey, ServerConfigurationValue, SettingKey,
65 SettingValue, SourceReference, SourceReferencesKey, SourceReferencesValue,
66 StorageCollectionMetadataKey, StorageCollectionMetadataValue, SystemObjectDescription,
67 SystemObjectMapping, SystemPrivilegesKey, SystemPrivilegesValue, TxnWalShardValue,
68 UnfinalizedShardKey,
69};
70use crate::durable::{
71 AUDIT_LOG_ID_ALLOC_KEY, BUILTIN_MIGRATION_SHARD_KEY, CATALOG_CONTENT_VERSION_KEY, CatalogError,
72 DATABASE_ID_ALLOC_KEY, DefaultPrivilege, DurableCatalogError, DurableCatalogState,
73 EXPRESSION_CACHE_SHARD_KEY, MOCK_AUTHENTICATION_NONCE_KEY, NetworkPolicy, OID_ALLOC_KEY,
74 SCHEMA_ID_ALLOC_KEY, SYSTEM_CLUSTER_ID_ALLOC_KEY, SYSTEM_CLUSTER_REPLICA_SIZE_ID_ALLOC_KEY,
75 SYSTEM_ITEM_ALLOC_KEY, SYSTEM_REPLICA_ID_ALLOC_KEY, Snapshot, SystemConfiguration,
76 USER_ITEM_ALLOC_KEY, USER_NETWORK_POLICY_ID_ALLOC_KEY, USER_ROLE_ID_ALLOC_KEY,
77};
78use crate::memory::objects::{StateDiff, StateUpdate, StateUpdateKind};
79
80type Timestamp = u64;
81
82#[derive(Debug, PartialEq)]
83struct CommitCapability;
84
85#[derive(Derivative)]
88#[derivative(Debug)]
89pub struct Transaction<'a> {
90 #[derivative(Debug = "ignore")]
91 #[derivative(PartialEq = "ignore")]
92 durable_catalog: &'a mut dyn DurableCatalogState,
93 databases: TableTransaction<DatabaseKey, DatabaseValue>,
94 schemas: TableTransaction<SchemaKey, SchemaValue>,
95 items: TableTransaction<ItemKey, ItemValue>,
96 item_columns: TableTransaction<ItemColumnsKey, ItemColumnsValue>,
97 index_columns: TableTransaction<IndexColumnsKey, IndexColumnsValue>,
98 comments: TableTransaction<CommentKey, CommentValue>,
99 roles: TableTransaction<RoleKey, RoleValue>,
100 role_auth: TableTransaction<RoleAuthKey, RoleAuthValue>,
101 clusters: TableTransaction<ClusterKey, ClusterValue>,
102 cluster_replicas: TableTransaction<ClusterReplicaKey, ClusterReplicaValue>,
103 introspection_sources:
104 TableTransaction<ClusterIntrospectionSourceIndexKey, ClusterIntrospectionSourceIndexValue>,
105 id_allocator: TableTransaction<IdAllocKey, IdAllocValue>,
106 configs: TableTransaction<ConfigKey, ConfigValue>,
107 settings: TableTransaction<SettingKey, SettingValue>,
108 system_gid_mapping: TableTransaction<GidMappingKey, GidMappingValue>,
109 system_configurations: TableTransaction<ServerConfigurationKey, ServerConfigurationValue>,
110 cluster_system_configurations:
111 TableTransaction<ClusterSystemConfigurationKey, ClusterSystemConfigurationValue>,
112 replica_system_configurations:
113 TableTransaction<ReplicaSystemConfigurationKey, ReplicaSystemConfigurationValue>,
114 default_privileges: TableTransaction<DefaultPrivilegesKey, DefaultPrivilegesValue>,
115 source_references: TableTransaction<SourceReferencesKey, SourceReferencesValue>,
116 system_privileges: TableTransaction<SystemPrivilegesKey, SystemPrivilegesValue>,
117 network_policies: TableTransaction<NetworkPolicyKey, NetworkPolicyValue>,
118 cluster_replica_sizes: TableTransaction<ClusterReplicaSizeKey, ClusterReplicaSizeValue>,
119 storage_collection_metadata:
120 TableTransaction<StorageCollectionMetadataKey, StorageCollectionMetadataValue>,
121 unfinalized_shards: TableTransaction<UnfinalizedShardKey, ()>,
122 txn_wal_shard: TableTransaction<(), TxnWalShardValue>,
123 audit_log_updates: Vec<(AuditLogKey, Diff, Timestamp)>,
126 upper: mz_repr::Timestamp,
128 op_id: Timestamp,
130 commit_capability: Option<CommitCapability>,
134}
135
136#[derive(Debug)]
138pub struct DryRunTransaction<'a> {
139 transaction: Transaction<'a>,
140}
141
142impl<'a> DryRunTransaction<'a> {
143 pub fn new(mut transaction: Transaction<'a>) -> Self {
145 transaction.commit_capability = None;
146 Self { transaction }
147 }
148
149 pub fn transaction_mut(&mut self) -> &mut Transaction<'a> {
151 &mut self.transaction
152 }
153
154 pub fn current_snapshot(&self) -> Snapshot {
156 self.transaction.current_snapshot()
157 }
158}
159
160impl<'a> Transaction<'a> {
161 pub(super) fn new(
162 durable_catalog: &'a mut dyn DurableCatalogState,
163 Snapshot {
164 databases,
165 schemas,
166 roles,
167 role_auth,
168 items,
169 item_columns,
170 index_columns,
171 comments,
172 clusters,
173 network_policies,
174 cluster_replica_sizes,
175 cluster_replicas,
176 introspection_sources,
177 id_allocator,
178 configs,
179 settings,
180 source_references,
181 system_object_mappings,
182 system_configurations,
183 cluster_system_configurations,
184 replica_system_configurations,
185 default_privileges,
186 system_privileges,
187 storage_collection_metadata,
188 unfinalized_shards,
189 txn_wal_shard,
190 }: Snapshot,
191 upper: mz_repr::Timestamp,
192 ) -> Result<Transaction<'a>, CatalogError> {
193 let database_unique_fn: fn(&DatabaseValue, &DatabaseValue) -> bool =
196 |a, b| a.name == b.name;
197 let schema_unique_fn: fn(&SchemaValue, &SchemaValue) -> bool =
198 |a, b| a.database_id == b.database_id && a.name == b.name;
199 let role_key: fn(&RoleValue, &RoleValue) -> bool = |a, b| a.name == b.name;
200 let cluster_unique_fn: fn(&ClusterValue, &ClusterValue) -> bool = |a, b| a.name == b.name;
201 let network_policy_unique_fn: fn(&NetworkPolicyValue, &NetworkPolicyValue) -> bool =
202 |a, b| a.name == b.name;
203 let cluster_replica_size_unique_fn: fn(
204 &ClusterReplicaSizeValue,
205 &ClusterReplicaSizeValue,
206 ) -> bool = |a, b| a.name == b.name;
207 let cluster_replica_unique_fn: fn(&ClusterReplicaValue, &ClusterReplicaValue) -> bool =
208 |a, b| a.cluster_id == b.cluster_id && a.name == b.name;
209
210 Ok(Transaction {
211 durable_catalog,
212 databases: TableTransaction::new_with_uniqueness_fn(
213 databases,
214 database_unique_fn,
215 database_unique_fn,
216 )?,
217 schemas: TableTransaction::new_with_uniqueness_fn(
218 schemas,
219 schema_unique_fn,
220 schema_unique_fn,
221 )?,
222 items: TableTransaction::new_with_uniqueness_fn(
227 items,
228 |a: &ItemValue, b| {
229 a.schema_id == b.schema_id
230 && a.name == b.name
231 && a.ephemeral_owner_session == b.ephemeral_owner_session
232 && {
233 let a_type = a.item_type();
235 let b_type = b.item_type();
236 (a_type != CatalogItemType::Type && b_type != CatalogItemType::Type)
237 || (a_type == CatalogItemType::Type && b_type.conflicts_with_type())
238 || (b_type == CatalogItemType::Type && a_type.conflicts_with_type())
239 }
240 },
241 |prev: &ItemValue, next| {
242 prev.schema_id == next.schema_id
243 && prev.name == next.name
244 && prev.ephemeral_owner_session == next.ephemeral_owner_session
245 && prev.item_type() == next.item_type()
247 },
248 )?,
249 item_columns: TableTransaction::new(item_columns)?,
250 index_columns: TableTransaction::new(index_columns)?,
251 comments: TableTransaction::new(comments)?,
252 roles: TableTransaction::new_with_uniqueness_fn(roles, role_key, role_key)?,
253 role_auth: TableTransaction::new(role_auth)?,
254 clusters: TableTransaction::new_with_uniqueness_fn(
255 clusters,
256 cluster_unique_fn,
257 cluster_unique_fn,
258 )?,
259 network_policies: TableTransaction::new_with_uniqueness_fn(
260 network_policies,
261 network_policy_unique_fn,
262 network_policy_unique_fn,
263 )?,
264 cluster_replica_sizes: TableTransaction::new_with_uniqueness_fn(
265 cluster_replica_sizes,
266 cluster_replica_size_unique_fn,
267 cluster_replica_size_unique_fn,
268 )?,
269 cluster_replicas: TableTransaction::new_with_uniqueness_fn(
270 cluster_replicas,
271 cluster_replica_unique_fn,
272 cluster_replica_unique_fn,
273 )?,
274 introspection_sources: TableTransaction::new(introspection_sources)?,
275 id_allocator: TableTransaction::new(id_allocator)?,
276 configs: TableTransaction::new(configs)?,
277 settings: TableTransaction::new(settings)?,
278 source_references: TableTransaction::new(source_references)?,
279 system_gid_mapping: TableTransaction::new(system_object_mappings)?,
280 system_configurations: TableTransaction::new(system_configurations)?,
281 cluster_system_configurations: TableTransaction::new(cluster_system_configurations)?,
282 replica_system_configurations: TableTransaction::new(replica_system_configurations)?,
283 default_privileges: TableTransaction::new(default_privileges)?,
284 system_privileges: TableTransaction::new(system_privileges)?,
285 storage_collection_metadata: TableTransaction::new(storage_collection_metadata)?,
286 unfinalized_shards: TableTransaction::new(unfinalized_shards)?,
287 txn_wal_shard: TableTransaction::new(txn_wal_shard)?,
291 audit_log_updates: Vec::new(),
292 upper,
293 op_id: 0,
294 commit_capability: Some(CommitCapability),
295 })
296 }
297
298 pub fn get_item(&self, id: &CatalogItemId) -> Option<Item> {
299 let key = ItemKey { id: *id };
300 self.items
301 .get(&key)
302 .map(|v| DurableType::from_key_value(key, v.clone()))
303 }
304
305 pub fn get_items(&self) -> impl Iterator<Item = Item> + use<> {
306 self.items
307 .items()
308 .into_iter()
309 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
310 .sorted_by_key(|Item { id, .. }| *id)
311 }
312
313 pub fn insert_audit_log_event(&mut self, event: VersionedEvent) {
314 self.insert_audit_log_events([event]);
315 }
316
317 pub fn insert_audit_log_events(&mut self, events: impl IntoIterator<Item = VersionedEvent>) {
318 let events = events
319 .into_iter()
320 .map(|event| (AuditLogKey { event }, Diff::ONE, self.op_id));
321 self.audit_log_updates.extend(events);
322 }
323
324 pub fn insert_user_database(
325 &mut self,
326 database_name: &str,
327 owner_id: RoleId,
328 privileges: Vec<MzAclItem>,
329 temporary_oids: &HashSet<u32>,
330 ) -> Result<(DatabaseId, u32), CatalogError> {
331 let id = self.get_and_increment_id(DATABASE_ID_ALLOC_KEY.to_string())?;
332 let id = DatabaseId::User(id);
333 let oid = self.allocate_oid(temporary_oids)?;
334 self.insert_database(id, database_name, owner_id, privileges, oid)?;
335 Ok((id, oid))
336 }
337
338 pub(crate) fn insert_database(
339 &mut self,
340 id: DatabaseId,
341 database_name: &str,
342 owner_id: RoleId,
343 privileges: Vec<MzAclItem>,
344 oid: u32,
345 ) -> Result<u32, CatalogError> {
346 match self.databases.insert(
347 DatabaseKey { id },
348 DatabaseValue {
349 name: database_name.to_string(),
350 owner_id,
351 privileges,
352 oid,
353 },
354 self.op_id,
355 ) {
356 Ok(_) => Ok(oid),
357 Err(_) => Err(SqlCatalogError::DatabaseAlreadyExists(database_name.to_owned()).into()),
358 }
359 }
360
361 pub fn insert_user_schema(
362 &mut self,
363 database_id: DatabaseId,
364 schema_name: &str,
365 owner_id: RoleId,
366 privileges: Vec<MzAclItem>,
367 temporary_oids: &HashSet<u32>,
368 ) -> Result<(SchemaId, u32), CatalogError> {
369 let id = self.get_and_increment_id(SCHEMA_ID_ALLOC_KEY.to_string())?;
370 let id = SchemaId::User(id);
371 let oid = self.allocate_oid(temporary_oids)?;
372 self.insert_schema(
373 id,
374 Some(database_id),
375 schema_name.to_string(),
376 owner_id,
377 privileges,
378 oid,
379 )?;
380 Ok((id, oid))
381 }
382
383 pub fn insert_system_schema(
384 &mut self,
385 schema_id: u64,
386 schema_name: &str,
387 owner_id: RoleId,
388 privileges: Vec<MzAclItem>,
389 oid: u32,
390 ) -> Result<(), CatalogError> {
391 let id = SchemaId::System(schema_id);
392 self.insert_schema(id, None, schema_name.to_string(), owner_id, privileges, oid)
393 }
394
395 pub(crate) fn insert_schema(
396 &mut self,
397 schema_id: SchemaId,
398 database_id: Option<DatabaseId>,
399 schema_name: String,
400 owner_id: RoleId,
401 privileges: Vec<MzAclItem>,
402 oid: u32,
403 ) -> Result<(), CatalogError> {
404 match self.schemas.insert(
405 SchemaKey { id: schema_id },
406 SchemaValue {
407 database_id,
408 name: schema_name.clone(),
409 owner_id,
410 privileges,
411 oid,
412 },
413 self.op_id,
414 ) {
415 Ok(_) => Ok(()),
416 Err(_) => Err(SqlCatalogError::SchemaAlreadyExists(schema_name).into()),
417 }
418 }
419
420 pub fn insert_builtin_role(
421 &mut self,
422 id: RoleId,
423 name: String,
424 attributes: RoleAttributesRaw,
425 membership: RoleMembership,
426 vars: RoleVars,
427 oid: u32,
428 ) -> Result<RoleId, CatalogError> {
429 soft_assert_or_log!(id.is_builtin(), "ID {id:?} is not builtin");
430 self.insert_role(id, name, attributes, membership, vars, oid)?;
431 Ok(id)
432 }
433
434 pub fn insert_user_role(
435 &mut self,
436 name: String,
437 attributes: RoleAttributesRaw,
438 membership: RoleMembership,
439 vars: RoleVars,
440 temporary_oids: &HashSet<u32>,
441 ) -> Result<(RoleId, u32), CatalogError> {
442 let id = self.get_and_increment_id(USER_ROLE_ID_ALLOC_KEY.to_string())?;
443 let id = RoleId::User(id);
444 let oid = self.allocate_oid(temporary_oids)?;
445 self.insert_role(id, name, attributes, membership, vars, oid)?;
446 Ok((id, oid))
447 }
448
449 fn insert_role(
450 &mut self,
451 id: RoleId,
452 name: String,
453 attributes: RoleAttributesRaw,
454 membership: RoleMembership,
455 vars: RoleVars,
456 oid: u32,
457 ) -> Result<(), CatalogError> {
458 if let Some(ref password) = attributes.password {
459 let hash = mz_auth::hash::scram256_hash(
460 password,
461 &attributes
462 .scram_iterations
463 .or_else(|| {
464 soft_panic_or_log!(
465 "Hash iterations must be set if a password is provided."
466 );
467 None
468 })
469 .unwrap_or_else(|| NonZeroU32::new(600_000).expect("known valid")),
472 )
473 .expect("password hash should be valid");
474 match self.role_auth.insert(
475 RoleAuthKey { role_id: id },
476 RoleAuthValue {
477 password_hash: Some(hash),
478 updated_at: SYSTEM_TIME(),
479 },
480 self.op_id,
481 ) {
482 Ok(_) => {}
483 Err(_) => {
484 return Err(SqlCatalogError::RoleAlreadyExists(name).into());
485 }
486 }
487 }
488
489 match self.roles.insert(
490 RoleKey { id },
491 RoleValue {
492 name: name.clone(),
493 attributes: attributes.into(),
494 membership,
495 vars,
496 oid,
497 },
498 self.op_id,
499 ) {
500 Ok(_) => Ok(()),
501 Err(_) => Err(SqlCatalogError::RoleAlreadyExists(name).into()),
502 }
503 }
504
505 pub fn insert_user_cluster(
507 &mut self,
508 cluster_id: ClusterId,
509 cluster_name: &str,
510 introspection_source_indexes: Vec<(&'static BuiltinLog, CatalogItemId, GlobalId)>,
511 owner_id: RoleId,
512 privileges: Vec<MzAclItem>,
513 config: ClusterConfig,
514 temporary_oids: &HashSet<u32>,
515 ) -> Result<(), CatalogError> {
516 self.insert_cluster(
517 cluster_id,
518 cluster_name,
519 introspection_source_indexes,
520 owner_id,
521 privileges,
522 config,
523 temporary_oids,
524 )
525 }
526
527 pub fn insert_system_cluster(
529 &mut self,
530 cluster_name: &str,
531 introspection_source_indexes: Vec<(&'static BuiltinLog, CatalogItemId, GlobalId)>,
532 privileges: Vec<MzAclItem>,
533 owner_id: RoleId,
534 config: ClusterConfig,
535 temporary_oids: &HashSet<u32>,
536 ) -> Result<ClusterId, CatalogError> {
537 let cluster_id = self.get_and_increment_id(SYSTEM_CLUSTER_ID_ALLOC_KEY.to_string())?;
538 let cluster_id = ClusterId::system(cluster_id).ok_or(SqlCatalogError::IdExhaustion)?;
539 self.insert_cluster(
540 cluster_id,
541 cluster_name,
542 introspection_source_indexes,
543 owner_id,
544 privileges,
545 config,
546 temporary_oids,
547 )?;
548 Ok(cluster_id)
549 }
550
551 fn insert_cluster(
552 &mut self,
553 cluster_id: ClusterId,
554 cluster_name: &str,
555 introspection_source_indexes: Vec<(&'static BuiltinLog, CatalogItemId, GlobalId)>,
556 owner_id: RoleId,
557 privileges: Vec<MzAclItem>,
558 config: ClusterConfig,
559 temporary_oids: &HashSet<u32>,
560 ) -> Result<(), CatalogError> {
561 if let Err(_) = self.clusters.insert(
562 ClusterKey { id: cluster_id },
563 ClusterValue {
564 name: cluster_name.to_string(),
565 owner_id,
566 privileges,
567 config,
568 },
569 self.op_id,
570 ) {
571 return Err(SqlCatalogError::ClusterAlreadyExists(cluster_name.to_owned()).into());
572 };
573
574 let amount = usize_to_u64(introspection_source_indexes.len());
575 let oids = self.allocate_oids(amount, temporary_oids)?;
576 let introspection_source_indexes: Vec<_> = introspection_source_indexes
577 .into_iter()
578 .zip_eq(oids)
579 .map(|((builtin, item_id, index_id), oid)| (builtin, item_id, index_id, oid))
580 .collect();
581 for (builtin, item_id, index_id, oid) in introspection_source_indexes {
582 let introspection_source_index = IntrospectionSourceIndex {
583 cluster_id,
584 name: builtin.name.to_string(),
585 item_id,
586 index_id,
587 oid,
588 };
589 let (key, value) = introspection_source_index.into_key_value();
590 self.introspection_sources
591 .insert(key, value, self.op_id)
592 .expect("no uniqueness violation");
593 }
594
595 Ok(())
596 }
597
598 pub fn rename_cluster(
599 &mut self,
600 cluster_id: ClusterId,
601 cluster_name: &str,
602 cluster_to_name: &str,
603 ) -> Result<(), CatalogError> {
604 let key = ClusterKey { id: cluster_id };
605
606 match self.clusters.update(
607 |k, v| {
608 if *k == key {
609 let mut value = v.clone();
610 value.name = cluster_to_name.to_string();
611 Some(value)
612 } else {
613 None
614 }
615 },
616 self.op_id,
617 )? {
618 Diff::ZERO => Err(SqlCatalogError::UnknownCluster(cluster_name.to_string()).into()),
619 Diff::ONE => Ok(()),
620 n => panic!(
621 "Expected to update single cluster {cluster_name} ({cluster_id}), updated {n}"
622 ),
623 }
624 }
625
626 pub fn rename_cluster_replica(
627 &mut self,
628 replica_id: ReplicaId,
629 replica_name: &QualifiedReplica,
630 replica_to_name: &str,
631 ) -> Result<(), CatalogError> {
632 let key = ClusterReplicaKey { id: replica_id };
633
634 match self.cluster_replicas.update(
635 |k, v| {
636 if *k == key {
637 let mut value = v.clone();
638 value.name = replica_to_name.to_string();
639 Some(value)
640 } else {
641 None
642 }
643 },
644 self.op_id,
645 )? {
646 Diff::ZERO => {
647 Err(SqlCatalogError::UnknownClusterReplica(replica_name.to_string()).into())
648 }
649 Diff::ONE => Ok(()),
650 n => panic!(
651 "Expected to update single cluster replica {replica_name} ({replica_id}), updated {n}"
652 ),
653 }
654 }
655
656 pub fn insert_cluster_replica_with_id(
657 &mut self,
658 cluster_id: ClusterId,
659 replica_id: ReplicaId,
660 replica_name: &str,
661 config: ReplicaConfig,
662 owner_id: RoleId,
663 ) -> Result<(), CatalogError> {
664 if let Err(_) = self.cluster_replicas.insert(
665 ClusterReplicaKey { id: replica_id },
666 ClusterReplicaValue {
667 cluster_id,
668 name: replica_name.into(),
669 config,
670 owner_id,
671 },
672 self.op_id,
673 ) {
674 let cluster = self
675 .clusters
676 .get(&ClusterKey { id: cluster_id })
677 .expect("cluster exists");
678 return Err(SqlCatalogError::DuplicateReplica(
679 replica_name.to_string(),
680 cluster.name.to_string(),
681 )
682 .into());
683 };
684 Ok(())
685 }
686
687 pub fn insert_user_network_policy(
688 &mut self,
689 name: String,
690 rules: Vec<NetworkPolicyRule>,
691 privileges: Vec<MzAclItem>,
692 owner_id: RoleId,
693 temporary_oids: &HashSet<u32>,
694 ) -> Result<NetworkPolicyId, CatalogError> {
695 let oid = self.allocate_oid(temporary_oids)?;
696 let id = self.get_and_increment_id(USER_NETWORK_POLICY_ID_ALLOC_KEY.to_string())?;
697 let id = NetworkPolicyId::User(id);
698 self.insert_network_policy(id, name, rules, privileges, owner_id, oid)
699 }
700
701 pub fn insert_network_policy(
702 &mut self,
703 id: NetworkPolicyId,
704 name: String,
705 rules: Vec<NetworkPolicyRule>,
706 privileges: Vec<MzAclItem>,
707 owner_id: RoleId,
708 oid: u32,
709 ) -> Result<NetworkPolicyId, CatalogError> {
710 match self.network_policies.insert(
711 NetworkPolicyKey { id },
712 NetworkPolicyValue {
713 name: name.clone(),
714 rules,
715 privileges,
716 owner_id,
717 oid,
718 },
719 self.op_id,
720 ) {
721 Ok(_) => Ok(id),
722 Err(_) => Err(SqlCatalogError::NetworkPolicyAlreadyExists(name).into()),
723 }
724 }
725
726 pub fn update_introspection_source_index_gids(
731 &mut self,
732 mappings: impl Iterator<
733 Item = (
734 ClusterId,
735 impl Iterator<Item = (String, CatalogItemId, GlobalId, u32)>,
736 ),
737 >,
738 ) -> Result<(), CatalogError> {
739 for (cluster_id, updates) in mappings {
740 for (name, item_id, index_id, oid) in updates {
741 let introspection_source_index = IntrospectionSourceIndex {
742 cluster_id,
743 name,
744 item_id,
745 index_id,
746 oid,
747 };
748 let (key, value) = introspection_source_index.into_key_value();
749
750 let prev = self
751 .introspection_sources
752 .set(key, Some(value), self.op_id)?;
753 if prev.is_none() {
754 return Err(SqlCatalogError::FailedBuiltinSchemaMigration(format!(
755 "{index_id}"
756 ))
757 .into());
758 }
759 }
760 }
761 Ok(())
762 }
763
764 pub fn insert_user_item(
765 &mut self,
766 id: CatalogItemId,
767 global_id: GlobalId,
768 schema_id: SchemaId,
769 item_name: &str,
770 create_sql: String,
771 owner_id: RoleId,
772 privileges: Vec<MzAclItem>,
773 temporary_oids: &HashSet<u32>,
774 versions: BTreeMap<RelationVersion, GlobalId>,
775 ephemeral_owner_session: Option<Uuid>,
776 ) -> Result<u32, CatalogError> {
777 let oid = self.allocate_oid(temporary_oids)?;
778 self.insert_item(
779 id,
780 oid,
781 global_id,
782 schema_id,
783 item_name,
784 create_sql,
785 owner_id,
786 privileges,
787 versions,
788 ephemeral_owner_session,
789 )?;
790 Ok(oid)
791 }
792
793 pub fn insert_item(
794 &mut self,
795 id: CatalogItemId,
796 oid: u32,
797 global_id: GlobalId,
798 schema_id: SchemaId,
799 item_name: &str,
800 create_sql: String,
801 owner_id: RoleId,
802 privileges: Vec<MzAclItem>,
803 extra_versions: BTreeMap<RelationVersion, GlobalId>,
804 ephemeral_owner_session: Option<Uuid>,
805 ) -> Result<(), CatalogError> {
806 match self.items.insert(
807 ItemKey { id },
808 ItemValue {
809 schema_id,
810 name: item_name.to_string(),
811 create_sql,
812 owner_id,
813 privileges,
814 oid,
815 global_id,
816 extra_versions,
817 ephemeral_owner_session,
818 },
819 self.op_id,
820 ) {
821 Ok(_) => Ok(()),
822 Err(_) => Err(SqlCatalogError::ItemAlreadyExists(id, item_name.to_owned()).into()),
823 }
824 }
825
826 pub fn set_item_metadata(
829 &mut self,
830 id: CatalogItemId,
831 metadata: ItemMetadata,
832 ) -> Result<(), CatalogError> {
833 let ItemMetadata {
834 columns,
835 index_columns,
836 } = metadata;
837 let columns = (!columns.is_empty()).then_some(ItemColumnsValue { columns });
840 let key = ItemColumnsKey { id };
841 if self.item_columns.get(&key) != columns.as_ref() {
842 self.item_columns.set(key, columns, self.op_id)?;
843 }
844 let index_columns = (!index_columns.is_empty()).then_some(IndexColumnsValue {
845 columns: index_columns,
846 });
847 let key = IndexColumnsKey { id };
848 if self.index_columns.get(&key) != index_columns.as_ref() {
849 self.index_columns.set(key, index_columns, self.op_id)?;
850 }
851 Ok(())
852 }
853
854 fn delete_item_metadata(&mut self, id: CatalogItemId) {
856 self.item_columns
857 .delete_by_key(ItemColumnsKey { id }, self.op_id);
858 self.index_columns
859 .delete_by_key(IndexColumnsKey { id }, self.op_id);
860 }
861
862 pub fn remove_ephemeral_items(&mut self) {
878 let mut keys = Vec::new();
879 let mut item_ids = BTreeSet::new();
880 let mut global_ids = BTreeSet::new();
881 for (key, value) in self.items.items() {
882 if value.ephemeral_owner_session.is_none() {
883 continue;
884 }
885 item_ids.insert(key.id);
886 global_ids.insert(value.global_id);
887 global_ids.extend(value.extra_versions.values().copied());
888 keys.push(key.clone());
889 }
890 self.items.delete_by_keys(keys, self.op_id);
891 for id in &item_ids {
892 self.delete_item_metadata(*id);
893 }
894
895 let dropped_mappings = self.delete_collection_metadata(global_ids);
903 let mut dropped_shards: BTreeSet<_> = dropped_mappings
904 .into_iter()
905 .map(|(_, shard)| shard)
906 .collect();
907 let live_shards: BTreeSet<_> = self.get_collection_metadata().into_values().collect();
908 dropped_shards.retain(|shard| {
909 let live = live_shards.contains(shard);
910 if live {
911 soft_panic_or_log!(
912 "shard {shard} of a reclaimed ephemeral item is still referenced by a \
913 live collection, not finalizing it"
914 );
915 }
916 !live
917 });
918 self.insert_unfinalized_shards(dropped_shards).expect(
919 "inserting unfinalized shards only fails on duplicate values, which it ignores",
920 );
921
922 self.comments.delete(
925 |key, _value| match key.object_id {
926 CommentObjectId::Table(item_id)
927 | CommentObjectId::View(item_id)
928 | CommentObjectId::MaterializedView(item_id)
929 | CommentObjectId::Source(item_id)
930 | CommentObjectId::Sink(item_id)
931 | CommentObjectId::MetricSink(item_id)
932 | CommentObjectId::Index(item_id)
933 | CommentObjectId::Func(item_id)
934 | CommentObjectId::Connection(item_id)
935 | CommentObjectId::Type(item_id)
936 | CommentObjectId::Secret(item_id) => item_ids.contains(&item_id),
937 CommentObjectId::Role(_)
938 | CommentObjectId::Database(_)
939 | CommentObjectId::Schema(_)
940 | CommentObjectId::Cluster(_)
941 | CommentObjectId::ClusterReplica(_)
942 | CommentObjectId::NetworkPolicy(_) => false,
943 },
944 self.op_id,
945 );
946
947 self.source_references
950 .delete(|key, _value| item_ids.contains(&key.source_id), self.op_id);
951 }
952
953 pub fn get_and_increment_id(&mut self, key: String) -> Result<u64, CatalogError> {
954 Ok(self.get_and_increment_id_by(key, 1)?.into_element())
955 }
956
957 pub fn get_and_increment_id_by(
958 &mut self,
959 key: String,
960 amount: u64,
961 ) -> Result<Vec<u64>, CatalogError> {
962 assert!(
963 key != SYSTEM_ITEM_ALLOC_KEY || !self.durable_catalog.is_bootstrap_complete(),
964 "system item IDs cannot be allocated outside of bootstrap"
965 );
966
967 let current_id = self
968 .id_allocator
969 .items()
970 .get(&IdAllocKey { name: key.clone() })
971 .unwrap_or_else(|| panic!("{key} id allocator missing"))
972 .next_id;
973 let next_id = current_id
974 .checked_add(amount)
975 .ok_or(SqlCatalogError::IdExhaustion)?;
976 let prev = self.id_allocator.set(
977 IdAllocKey { name: key },
978 Some(IdAllocValue { next_id }),
979 self.op_id,
980 )?;
981 assert_eq!(
982 prev,
983 Some(IdAllocValue {
984 next_id: current_id
985 })
986 );
987 Ok((current_id..next_id).collect())
988 }
989
990 pub fn allocate_system_item_ids(
991 &mut self,
992 amount: u64,
993 ) -> Result<Vec<(CatalogItemId, GlobalId)>, CatalogError> {
994 assert!(
995 !self.durable_catalog.is_bootstrap_complete(),
996 "we can only allocate system item IDs during bootstrap"
997 );
998 Ok(self
999 .get_and_increment_id_by(SYSTEM_ITEM_ALLOC_KEY.to_string(), amount)?
1000 .into_iter()
1001 .map(|x| (CatalogItemId::System(x), GlobalId::System(x)))
1003 .collect())
1004 }
1005
1006 pub fn allocate_introspection_source_index_id(
1038 cluster_id: &ClusterId,
1039 log_variant: LogVariant,
1040 ) -> (CatalogItemId, GlobalId) {
1041 let cluster_variant: u8 = match cluster_id {
1042 ClusterId::System(_) => 1,
1043 ClusterId::User(_) => 2,
1044 };
1045 let cluster_id: u64 = cluster_id.inner_id();
1046 const CLUSTER_ID_MASK: u64 = 0xFFFF << 48;
1047 assert_eq!(
1048 CLUSTER_ID_MASK & cluster_id,
1049 0,
1050 "invalid cluster ID: {cluster_id}"
1051 );
1052 let log_variant: u8 = match log_variant {
1053 LogVariant::Timely(TimelyLog::Operates) => 1,
1054 LogVariant::Timely(TimelyLog::Channels) => 2,
1055 LogVariant::Timely(TimelyLog::Elapsed) => 3,
1056 LogVariant::Timely(TimelyLog::Histogram) => 4,
1057 LogVariant::Timely(TimelyLog::Addresses) => 5,
1058 LogVariant::Timely(TimelyLog::Parks) => 6,
1059 LogVariant::Timely(TimelyLog::MessagesSent) => 7,
1060 LogVariant::Timely(TimelyLog::MessagesReceived) => 8,
1061 LogVariant::Timely(TimelyLog::Reachability) => 9,
1062 LogVariant::Timely(TimelyLog::BatchesSent) => 10,
1063 LogVariant::Timely(TimelyLog::BatchesReceived) => 11,
1064 LogVariant::Differential(DifferentialLog::ArrangementBatches) => 12,
1065 LogVariant::Differential(DifferentialLog::ArrangementRecords) => 13,
1066 LogVariant::Differential(DifferentialLog::Sharing) => 14,
1067 LogVariant::Differential(DifferentialLog::BatcherRecords) => 15,
1068 LogVariant::Differential(DifferentialLog::BatcherSize) => 16,
1069 LogVariant::Differential(DifferentialLog::BatcherCapacity) => 17,
1070 LogVariant::Differential(DifferentialLog::BatcherAllocations) => 18,
1071 LogVariant::Compute(ComputeLog::DataflowCurrent) => 19,
1072 LogVariant::Compute(ComputeLog::FrontierCurrent) => 20,
1073 LogVariant::Compute(ComputeLog::PeekCurrent) => 21,
1074 LogVariant::Compute(ComputeLog::PeekDuration) => 22,
1075 LogVariant::Compute(ComputeLog::ImportFrontierCurrent) => 23,
1076 LogVariant::Compute(ComputeLog::ArrangementHeapSize) => 24,
1077 LogVariant::Compute(ComputeLog::ArrangementHeapCapacity) => 25,
1078 LogVariant::Compute(ComputeLog::ArrangementHeapAllocations) => 26,
1079 LogVariant::Compute(ComputeLog::ErrorCount) => 28,
1080 LogVariant::Compute(ComputeLog::HydrationTime) => 29,
1081 LogVariant::Compute(ComputeLog::LirMapping) => 30,
1082 LogVariant::Compute(ComputeLog::DataflowGlobal) => 31,
1083 LogVariant::Compute(ComputeLog::OperatorHydrationStatus) => 32,
1084 LogVariant::Compute(ComputeLog::PrometheusMetrics) => 33,
1085 LogVariant::Compute(ComputeLog::ResourceUsage) => 34,
1086 };
1087
1088 let mut id: u64 = u64::from(cluster_variant) << 56;
1089 id |= cluster_id << 8;
1090 id |= u64::from(log_variant);
1091
1092 (
1093 CatalogItemId::IntrospectionSourceIndex(id),
1094 GlobalId::IntrospectionSourceIndex(id),
1095 )
1096 }
1097
1098 pub fn allocate_user_item_ids(
1099 &mut self,
1100 amount: u64,
1101 ) -> Result<Vec<(CatalogItemId, GlobalId)>, CatalogError> {
1102 Ok(self
1103 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), amount)?
1104 .into_iter()
1105 .map(|x| (CatalogItemId::User(x), GlobalId::User(x)))
1107 .collect())
1108 }
1109
1110 pub fn allocate_system_replica_id(&mut self) -> Result<ReplicaId, CatalogError> {
1111 let id = self.get_and_increment_id(SYSTEM_REPLICA_ID_ALLOC_KEY.to_string())?;
1112 Ok(ReplicaId::System(id))
1113 }
1114
1115 pub fn allocate_audit_log_id(&mut self) -> Result<u64, CatalogError> {
1116 self.get_and_increment_id(AUDIT_LOG_ID_ALLOC_KEY.to_string())
1117 }
1118
1119 #[mz_ore::instrument]
1122 fn allocate_oids(
1123 &mut self,
1124 amount: u64,
1125 temporary_oids: &HashSet<u32>,
1126 ) -> Result<Vec<u32>, CatalogError> {
1127 struct UserOid(u32);
1130
1131 impl UserOid {
1132 fn new(oid: u32) -> Result<UserOid, anyhow::Error> {
1133 if oid < FIRST_USER_OID {
1134 Err(anyhow!("invalid user OID {oid}"))
1135 } else {
1136 Ok(UserOid(oid))
1137 }
1138 }
1139 }
1140
1141 impl std::ops::AddAssign<u32> for UserOid {
1142 fn add_assign(&mut self, rhs: u32) {
1143 let (res, overflow) = self.0.overflowing_add(rhs);
1144 self.0 = if overflow { FIRST_USER_OID + res } else { res };
1145 }
1146 }
1147
1148 if amount > u32::MAX.into() {
1149 return Err(CatalogError::Catalog(SqlCatalogError::OidExhaustion));
1150 }
1151
1152 let mut allocated_oids = HashSet::with_capacity(
1158 self.databases.len()
1159 + self.schemas.len()
1160 + self.roles.len()
1161 + self.items.len()
1162 + self.introspection_sources.len()
1163 + temporary_oids.len(),
1164 );
1165 self.databases.for_values(|_, value| {
1166 allocated_oids.insert(value.oid);
1167 });
1168 self.schemas.for_values(|_, value| {
1169 allocated_oids.insert(value.oid);
1170 });
1171 self.roles.for_values(|_, value| {
1172 allocated_oids.insert(value.oid);
1173 });
1174 self.items.for_values(|_, value| {
1175 allocated_oids.insert(value.oid);
1176 });
1177 self.introspection_sources.for_values(|_, value| {
1178 allocated_oids.insert(value.oid);
1179 });
1180
1181 let is_allocated = |oid| allocated_oids.contains(&oid) || temporary_oids.contains(&oid);
1182
1183 let start_oid: u32 = self
1184 .id_allocator
1185 .items()
1186 .get(&IdAllocKey {
1187 name: OID_ALLOC_KEY.to_string(),
1188 })
1189 .unwrap_or_else(|| panic!("{OID_ALLOC_KEY} id allocator missing"))
1190 .next_id
1191 .try_into()
1192 .expect("we should never persist an oid outside of the u32 range");
1193 let mut current_oid = UserOid::new(start_oid)
1194 .expect("we should never persist an oid outside of user OID range");
1195 let mut oids = Vec::new();
1196 while oids.len() < u64_to_usize(amount) {
1197 if !is_allocated(current_oid.0) {
1198 oids.push(current_oid.0);
1199 }
1200 current_oid += 1;
1201
1202 if current_oid.0 == start_oid && oids.len() < u64_to_usize(amount) {
1203 return Err(CatalogError::Catalog(SqlCatalogError::OidExhaustion));
1205 }
1206 }
1207
1208 let next_id = current_oid.0;
1209 let prev = self.id_allocator.set(
1210 IdAllocKey {
1211 name: OID_ALLOC_KEY.to_string(),
1212 },
1213 Some(IdAllocValue {
1214 next_id: next_id.into(),
1215 }),
1216 self.op_id,
1217 )?;
1218 assert_eq!(
1219 prev,
1220 Some(IdAllocValue {
1221 next_id: start_oid.into(),
1222 })
1223 );
1224
1225 Ok(oids)
1226 }
1227
1228 pub fn allocate_oid(&mut self, temporary_oids: &HashSet<u32>) -> Result<u32, CatalogError> {
1231 self.allocate_oids(1, temporary_oids)
1232 .map(|oids| oids.into_element())
1233 }
1234
1235 pub fn fork_dry_run(&mut self) -> Result<DryRunTransaction<'_>, CatalogError> {
1240 let snapshot = self.current_snapshot();
1241 let upper = self.upper;
1242 Transaction::new(&mut *self.durable_catalog, snapshot, upper).map(DryRunTransaction::new)
1243 }
1244
1245 pub fn current_snapshot(&self) -> Snapshot {
1253 Snapshot {
1254 databases: self.databases.current_items_proto(),
1255 schemas: self.schemas.current_items_proto(),
1256 roles: self.roles.current_items_proto(),
1257 role_auth: self.role_auth.current_items_proto(),
1258 items: self.items.current_items_proto(),
1259 item_columns: self.item_columns.current_items_proto(),
1260 index_columns: self.index_columns.current_items_proto(),
1261 comments: self.comments.current_items_proto(),
1262 clusters: self.clusters.current_items_proto(),
1263 network_policies: self.network_policies.current_items_proto(),
1264 cluster_replica_sizes: self.cluster_replica_sizes.current_items_proto(),
1265 cluster_replicas: self.cluster_replicas.current_items_proto(),
1266 introspection_sources: self.introspection_sources.current_items_proto(),
1267 id_allocator: self.id_allocator.current_items_proto(),
1268 configs: self.configs.current_items_proto(),
1269 settings: self.settings.current_items_proto(),
1270 system_object_mappings: self.system_gid_mapping.current_items_proto(),
1271 system_configurations: self.system_configurations.current_items_proto(),
1272 cluster_system_configurations: self.cluster_system_configurations.current_items_proto(),
1273 replica_system_configurations: self.replica_system_configurations.current_items_proto(),
1274 default_privileges: self.default_privileges.current_items_proto(),
1275 source_references: self.source_references.current_items_proto(),
1276 system_privileges: self.system_privileges.current_items_proto(),
1277 storage_collection_metadata: self.storage_collection_metadata.current_items_proto(),
1278 unfinalized_shards: self.unfinalized_shards.current_items_proto(),
1279 txn_wal_shard: self.txn_wal_shard.current_items_proto(),
1280 }
1281 }
1282
1283 pub(crate) fn insert_id_allocator(
1284 &mut self,
1285 name: String,
1286 next_id: u64,
1287 ) -> Result<(), CatalogError> {
1288 match self.id_allocator.insert(
1289 IdAllocKey { name: name.clone() },
1290 IdAllocValue { next_id },
1291 self.op_id,
1292 ) {
1293 Ok(_) => Ok(()),
1294 Err(_) => Err(SqlCatalogError::IdAllocatorAlreadyExists(name).into()),
1295 }
1296 }
1297
1298 pub fn remove_database(&mut self, id: &DatabaseId) -> Result<(), CatalogError> {
1305 let prev = self
1306 .databases
1307 .set(DatabaseKey { id: *id }, None, self.op_id)?;
1308 if prev.is_some() {
1309 Ok(())
1310 } else {
1311 Err(SqlCatalogError::UnknownDatabase(id.to_string()).into())
1312 }
1313 }
1314
1315 pub fn remove_databases(
1322 &mut self,
1323 databases: &BTreeSet<DatabaseId>,
1324 ) -> Result<(), CatalogError> {
1325 if databases.is_empty() {
1326 return Ok(());
1327 }
1328
1329 let to_remove = databases
1330 .iter()
1331 .map(|id| (DatabaseKey { id: *id }, None))
1332 .collect();
1333 let mut prev = self.databases.set_many(to_remove, self.op_id)?;
1334 prev.retain(|_k, val| val.is_none());
1335
1336 if !prev.is_empty() {
1337 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1338 return Err(SqlCatalogError::UnknownDatabase(err).into());
1339 }
1340
1341 Ok(())
1342 }
1343
1344 pub fn remove_schema(
1351 &mut self,
1352 database_id: &Option<DatabaseId>,
1353 schema_id: &SchemaId,
1354 ) -> Result<(), CatalogError> {
1355 let prev = self
1356 .schemas
1357 .set(SchemaKey { id: *schema_id }, None, self.op_id)?;
1358 if prev.is_some() {
1359 Ok(())
1360 } else {
1361 let database_name = match database_id {
1362 Some(id) => format!("{id}."),
1363 None => "".to_string(),
1364 };
1365 Err(SqlCatalogError::UnknownSchema(format!("{}.{}", database_name, schema_id)).into())
1366 }
1367 }
1368
1369 pub fn remove_schemas(
1376 &mut self,
1377 schemas: &BTreeMap<SchemaId, ResolvedDatabaseSpecifier>,
1378 ) -> Result<(), CatalogError> {
1379 if schemas.is_empty() {
1380 return Ok(());
1381 }
1382
1383 let to_remove = schemas
1384 .iter()
1385 .map(|(schema_id, _)| (SchemaKey { id: *schema_id }, None))
1386 .collect();
1387 let mut prev = self.schemas.set_many(to_remove, self.op_id)?;
1388 prev.retain(|_k, v| v.is_none());
1389
1390 if !prev.is_empty() {
1391 let err = prev
1392 .keys()
1393 .map(|k| {
1394 let db_spec = schemas.get(&k.id).expect("should_exist");
1395 let db_name = match db_spec {
1396 ResolvedDatabaseSpecifier::Id(id) => format!("{id}."),
1397 ResolvedDatabaseSpecifier::Ambient => "".to_string(),
1398 };
1399 format!("{}.{}", db_name, k.id)
1400 })
1401 .join(", ");
1402
1403 return Err(SqlCatalogError::UnknownSchema(err).into());
1404 }
1405
1406 Ok(())
1407 }
1408
1409 pub fn remove_source_references(
1410 &mut self,
1411 source_id: CatalogItemId,
1412 ) -> Result<(), CatalogError> {
1413 let deleted = self
1414 .source_references
1415 .delete_by_key(SourceReferencesKey { source_id }, self.op_id)
1416 .is_some();
1417 if deleted {
1418 Ok(())
1419 } else {
1420 Err(SqlCatalogError::UnknownItem(source_id.to_string()).into())
1421 }
1422 }
1423
1424 pub fn remove_user_roles(&mut self, roles: &BTreeSet<RoleId>) -> Result<(), CatalogError> {
1431 assert!(
1432 roles.iter().all(|id| id.is_user()),
1433 "cannot delete non-user roles"
1434 );
1435 self.remove_roles(roles)
1436 }
1437
1438 pub fn remove_roles(&mut self, roles: &BTreeSet<RoleId>) -> Result<(), CatalogError> {
1445 if roles.is_empty() {
1446 return Ok(());
1447 }
1448
1449 let to_remove_keys = roles
1450 .iter()
1451 .map(|role_id| RoleKey { id: *role_id })
1452 .collect::<Vec<_>>();
1453
1454 let to_remove_roles = to_remove_keys
1455 .iter()
1456 .map(|role_key| (role_key.clone(), None))
1457 .collect();
1458
1459 let mut prev = self.roles.set_many(to_remove_roles, self.op_id)?;
1460
1461 let to_remove_role_auth = to_remove_keys
1462 .iter()
1463 .map(|role_key| {
1464 (
1465 RoleAuthKey {
1466 role_id: role_key.id,
1467 },
1468 None,
1469 )
1470 })
1471 .collect();
1472
1473 let mut role_auth_prev = self.role_auth.set_many(to_remove_role_auth, self.op_id)?;
1474
1475 prev.retain(|_k, v| v.is_none());
1476 if !prev.is_empty() {
1477 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1478 return Err(SqlCatalogError::UnknownRole(err).into());
1479 }
1480
1481 role_auth_prev.retain(|_k, v| v.is_none());
1482 Ok(())
1486 }
1487
1488 pub fn remove_clusters(&mut self, clusters: &BTreeSet<ClusterId>) -> Result<(), CatalogError> {
1495 if clusters.is_empty() {
1496 return Ok(());
1497 }
1498
1499 let to_remove = clusters
1500 .iter()
1501 .map(|cluster_id| (ClusterKey { id: *cluster_id }, None))
1502 .collect();
1503 let mut prev = self.clusters.set_many(to_remove, self.op_id)?;
1504
1505 prev.retain(|_k, v| v.is_none());
1506 if !prev.is_empty() {
1507 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1508 return Err(SqlCatalogError::UnknownCluster(err).into());
1509 }
1510
1511 self.cluster_replicas
1517 .delete(|_k, v| clusters.contains(&v.cluster_id), self.op_id);
1518 self.introspection_sources
1519 .delete(|k, _v| clusters.contains(&k.cluster_id), self.op_id);
1520
1521 Ok(())
1522 }
1523
1524 pub fn remove_cluster_replica(&mut self, id: ReplicaId) -> Result<(), CatalogError> {
1531 let deleted = self
1532 .cluster_replicas
1533 .delete_by_key(ClusterReplicaKey { id }, self.op_id)
1534 .is_some();
1535 if deleted {
1536 Ok(())
1537 } else {
1538 Err(SqlCatalogError::UnknownClusterReplica(id.to_string()).into())
1539 }
1540 }
1541
1542 pub fn remove_cluster_replicas(
1549 &mut self,
1550 replicas: &BTreeSet<ReplicaId>,
1551 ) -> Result<(), CatalogError> {
1552 if replicas.is_empty() {
1553 return Ok(());
1554 }
1555
1556 let to_remove = replicas
1557 .iter()
1558 .map(|replica_id| (ClusterReplicaKey { id: *replica_id }, None))
1559 .collect();
1560 let mut prev = self.cluster_replicas.set_many(to_remove, self.op_id)?;
1561
1562 prev.retain(|_k, v| v.is_none());
1563 if !prev.is_empty() {
1564 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1565 return Err(SqlCatalogError::UnknownClusterReplica(err).into());
1566 }
1567
1568 Ok(())
1569 }
1570
1571 pub fn remove_item(&mut self, id: CatalogItemId) -> Result<(), CatalogError> {
1578 let prev = self.items.set(ItemKey { id }, None, self.op_id)?;
1579 if prev.is_some() {
1580 self.delete_item_metadata(id);
1581 Ok(())
1582 } else {
1583 Err(SqlCatalogError::UnknownItem(id.to_string()).into())
1584 }
1585 }
1586
1587 pub fn remove_items(&mut self, ids: &BTreeSet<CatalogItemId>) -> Result<(), CatalogError> {
1594 if ids.is_empty() {
1595 return Ok(());
1596 }
1597
1598 let ks: Vec<_> = ids.clone().into_iter().map(|id| ItemKey { id }).collect();
1599 let n = self.items.delete_by_keys(ks, self.op_id).len();
1600 for id in ids {
1601 self.delete_item_metadata(*id);
1602 }
1603 if n == ids.len() {
1604 Ok(())
1605 } else {
1606 let item_ids = self.items.items().keys().map(|k| k.id).collect();
1607 let mut unknown = ids.difference(&item_ids);
1608 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1609 }
1610 }
1611
1612 pub fn remove_system_object_mappings(
1619 &mut self,
1620 descriptions: BTreeSet<SystemObjectDescription>,
1621 ) -> Result<(), CatalogError> {
1622 if descriptions.is_empty() {
1623 return Ok(());
1624 }
1625
1626 let ks: Vec<_> = descriptions
1627 .clone()
1628 .into_iter()
1629 .map(|desc| GidMappingKey {
1630 schema_name: desc.schema_name,
1631 object_type: desc.object_type,
1632 object_name: desc.object_name,
1633 })
1634 .collect();
1635 let n = self.system_gid_mapping.delete_by_keys(ks, self.op_id).len();
1636
1637 if n == descriptions.len() {
1638 Ok(())
1639 } else {
1640 let item_descriptions = self
1641 .system_gid_mapping
1642 .items()
1643 .keys()
1644 .map(|k| SystemObjectDescription {
1645 schema_name: k.schema_name.clone(),
1646 object_type: k.object_type.clone(),
1647 object_name: k.object_name.clone(),
1648 })
1649 .collect();
1650 let mut unknown = descriptions.difference(&item_descriptions).map(|desc| {
1651 format!(
1652 "{} {}.{}",
1653 desc.object_type, desc.schema_name, desc.object_name
1654 )
1655 });
1656 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1657 }
1658 }
1659
1660 pub fn remove_introspection_source_indexes(
1667 &mut self,
1668 introspection_source_indexes: BTreeSet<(ClusterId, String)>,
1669 ) -> Result<(), CatalogError> {
1670 if introspection_source_indexes.is_empty() {
1671 return Ok(());
1672 }
1673
1674 let ks: Vec<_> = introspection_source_indexes
1675 .clone()
1676 .into_iter()
1677 .map(|(cluster_id, name)| ClusterIntrospectionSourceIndexKey { cluster_id, name })
1678 .collect();
1679 let n = self
1680 .introspection_sources
1681 .delete_by_keys(ks, self.op_id)
1682 .len();
1683 if n == introspection_source_indexes.len() {
1684 Ok(())
1685 } else {
1686 let txn_indexes = self
1687 .introspection_sources
1688 .items()
1689 .keys()
1690 .map(|k| (k.cluster_id, k.name.clone()))
1691 .collect();
1692 let mut unknown = introspection_source_indexes
1693 .difference(&txn_indexes)
1694 .map(|(cluster_id, name)| format!("{cluster_id} {name}"));
1695 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1696 }
1697 }
1698
1699 pub fn update_item(&mut self, id: CatalogItemId, item: Item) -> Result<(), CatalogError> {
1706 let updated =
1707 self.items
1708 .update_by_key(ItemKey { id }, item.into_key_value().1, self.op_id)?;
1709 if updated {
1710 Ok(())
1711 } else {
1712 Err(SqlCatalogError::UnknownItem(id.to_string()).into())
1713 }
1714 }
1715
1716 pub fn update_items(
1724 &mut self,
1725 items: BTreeMap<CatalogItemId, Item>,
1726 ) -> Result<(), CatalogError> {
1727 if items.is_empty() {
1728 return Ok(());
1729 }
1730
1731 let update_ids: BTreeSet<_> = items.keys().cloned().collect();
1732 let kvs: Vec<_> = items
1733 .clone()
1734 .into_iter()
1735 .map(|(id, item)| (ItemKey { id }, item.into_key_value().1))
1736 .collect();
1737 let n = self.items.update_by_keys(kvs, self.op_id)?;
1738 let n = usize::try_from(n.into_inner()).expect("Must be positive and fit in usize");
1739 if n == update_ids.len() {
1740 Ok(())
1741 } else {
1742 let item_ids: BTreeSet<_> = self.items.items().keys().map(|k| k.id).collect();
1743 let mut unknown = update_ids.difference(&item_ids);
1744 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1745 }
1746 }
1747
1748 pub fn update_role(
1756 &mut self,
1757 id: RoleId,
1758 role: Role,
1759 password: PasswordAction,
1760 ) -> Result<(), CatalogError> {
1761 let key = RoleKey { id };
1762 if self.roles.get(&key).is_some() {
1763 let auth_key = RoleAuthKey { role_id: id };
1764
1765 match password {
1766 PasswordAction::Set(new_password) => {
1767 let hash = mz_auth::hash::scram256_hash(
1768 &new_password.password,
1769 &new_password.scram_iterations,
1770 )
1771 .expect("password hash should be valid");
1772 let value = RoleAuthValue {
1773 password_hash: Some(hash),
1774 updated_at: SYSTEM_TIME(),
1775 };
1776
1777 if self.role_auth.get(&auth_key).is_some() {
1778 self.role_auth
1779 .update_by_key(auth_key.clone(), value, self.op_id)?;
1780 } else {
1781 self.role_auth.insert(auth_key.clone(), value, self.op_id)?;
1782 }
1783 }
1784 PasswordAction::Clear => {
1785 let value = RoleAuthValue {
1786 password_hash: None,
1787 updated_at: SYSTEM_TIME(),
1788 };
1789 if self.role_auth.get(&auth_key).is_some() {
1790 self.role_auth
1791 .update_by_key(auth_key.clone(), value, self.op_id)?;
1792 }
1793 }
1794 PasswordAction::NoChange => {}
1795 }
1796
1797 self.roles
1798 .update_by_key(key, role.into_key_value().1, self.op_id)?;
1799
1800 Ok(())
1801 } else {
1802 Err(SqlCatalogError::UnknownRole(id.to_string()).into())
1803 }
1804 }
1805
1806 pub fn update_roles_without_auth(
1817 &mut self,
1818 roles: BTreeMap<RoleId, Role>,
1819 ) -> Result<(), CatalogError> {
1820 if roles.is_empty() {
1821 return Ok(());
1822 }
1823
1824 let update_role_ids: BTreeSet<_> = roles.keys().cloned().collect();
1825 let kvs: Vec<_> = roles
1826 .into_iter()
1827 .map(|(id, role)| (RoleKey { id }, role.into_key_value().1))
1828 .collect();
1829 let n = self.roles.update_by_keys(kvs, self.op_id)?;
1830 let n = usize::try_from(n.into_inner()).expect("Must be positive and fit in usize");
1831
1832 if n == update_role_ids.len() {
1833 Ok(())
1834 } else {
1835 let role_ids: BTreeSet<_> = self.roles.items().keys().map(|k| k.id).collect();
1836 let mut unknown = update_role_ids.difference(&role_ids);
1837 Err(SqlCatalogError::UnknownRole(unknown.join(", ")).into())
1838 }
1839 }
1840
1841 pub fn update_system_object_mappings(
1846 &mut self,
1847 mappings: BTreeMap<CatalogItemId, SystemObjectMapping>,
1848 ) -> Result<(), CatalogError> {
1849 if mappings.is_empty() {
1850 return Ok(());
1851 }
1852
1853 let n = self.system_gid_mapping.update(
1854 |_k, v| {
1855 if let Some(mapping) = mappings.get(&CatalogItemId::from(v.catalog_id)) {
1856 let (_, new_value) = mapping.clone().into_key_value();
1857 Some(new_value)
1858 } else {
1859 None
1860 }
1861 },
1862 self.op_id,
1863 )?;
1864
1865 if usize::try_from(n.into_inner()).expect("update diff should fit into usize")
1866 != mappings.len()
1867 {
1868 let id_str = mappings.keys().map(|id| id.to_string()).join(",");
1869 return Err(SqlCatalogError::FailedBuiltinSchemaMigration(id_str).into());
1870 }
1871
1872 Ok(())
1873 }
1874
1875 pub fn update_cluster(&mut self, id: ClusterId, cluster: Cluster) -> Result<(), CatalogError> {
1882 let updated = self.clusters.update_by_key(
1883 ClusterKey { id },
1884 cluster.into_key_value().1,
1885 self.op_id,
1886 )?;
1887 if updated {
1888 Ok(())
1889 } else {
1890 Err(SqlCatalogError::UnknownCluster(id.to_string()).into())
1891 }
1892 }
1893
1894 pub fn update_cluster_replica(
1901 &mut self,
1902 replica_id: ReplicaId,
1903 replica: ClusterReplica,
1904 ) -> Result<(), CatalogError> {
1905 let updated = self.cluster_replicas.update_by_key(
1906 ClusterReplicaKey { id: replica_id },
1907 replica.into_key_value().1,
1908 self.op_id,
1909 )?;
1910 if updated {
1911 Ok(())
1912 } else {
1913 Err(SqlCatalogError::UnknownClusterReplica(replica_id.to_string()).into())
1914 }
1915 }
1916
1917 pub fn update_database(
1924 &mut self,
1925 id: DatabaseId,
1926 database: Database,
1927 ) -> Result<(), CatalogError> {
1928 let updated = self.databases.update_by_key(
1929 DatabaseKey { id },
1930 database.into_key_value().1,
1931 self.op_id,
1932 )?;
1933 if updated {
1934 Ok(())
1935 } else {
1936 Err(SqlCatalogError::UnknownDatabase(id.to_string()).into())
1937 }
1938 }
1939
1940 pub fn update_schema(
1947 &mut self,
1948 schema_id: SchemaId,
1949 schema: Schema,
1950 ) -> Result<(), CatalogError> {
1951 let updated = self.schemas.update_by_key(
1952 SchemaKey { id: schema_id },
1953 schema.into_key_value().1,
1954 self.op_id,
1955 )?;
1956 if updated {
1957 Ok(())
1958 } else {
1959 Err(SqlCatalogError::UnknownSchema(schema_id.to_string()).into())
1960 }
1961 }
1962
1963 pub fn update_network_policy(
1970 &mut self,
1971 id: NetworkPolicyId,
1972 network_policy: NetworkPolicy,
1973 ) -> Result<(), CatalogError> {
1974 let updated = self.network_policies.update_by_key(
1975 NetworkPolicyKey { id },
1976 network_policy.into_key_value().1,
1977 self.op_id,
1978 )?;
1979 if updated {
1980 Ok(())
1981 } else {
1982 Err(SqlCatalogError::UnknownNetworkPolicy(id.to_string()).into())
1983 }
1984 }
1985 pub fn remove_network_policies(
1992 &mut self,
1993 network_policies: &BTreeSet<NetworkPolicyId>,
1994 ) -> Result<(), CatalogError> {
1995 if network_policies.is_empty() {
1996 return Ok(());
1997 }
1998
1999 let to_remove = network_policies
2000 .iter()
2001 .map(|policy_id| (NetworkPolicyKey { id: *policy_id }, None))
2002 .collect();
2003 let mut prev = self.network_policies.set_many(to_remove, self.op_id)?;
2004 assert!(
2005 prev.iter().all(|(k, _)| k.id.is_user()),
2006 "cannot delete non-user network policy"
2007 );
2008
2009 prev.retain(|_k, v| v.is_none());
2010 if !prev.is_empty() {
2011 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
2012 return Err(SqlCatalogError::UnknownNetworkPolicy(err).into());
2013 }
2014
2015 Ok(())
2016 }
2017 pub fn set_default_privilege(
2021 &mut self,
2022 role_id: RoleId,
2023 database_id: Option<DatabaseId>,
2024 schema_id: Option<SchemaId>,
2025 object_type: ObjectType,
2026 grantee: RoleId,
2027 privileges: Option<AclMode>,
2028 ) -> Result<(), CatalogError> {
2029 self.default_privileges.set(
2030 DefaultPrivilegesKey {
2031 role_id,
2032 database_id,
2033 schema_id,
2034 object_type,
2035 grantee,
2036 },
2037 privileges.map(|privileges| DefaultPrivilegesValue { privileges }),
2038 self.op_id,
2039 )?;
2040 Ok(())
2041 }
2042
2043 pub fn set_default_privileges(
2045 &mut self,
2046 default_privileges: Vec<DefaultPrivilege>,
2047 ) -> Result<(), CatalogError> {
2048 if default_privileges.is_empty() {
2049 return Ok(());
2050 }
2051
2052 let default_privileges = default_privileges
2053 .into_iter()
2054 .map(DurableType::into_key_value)
2055 .map(|(k, v)| (k, Some(v)))
2056 .collect();
2057 self.default_privileges
2058 .set_many(default_privileges, self.op_id)?;
2059 Ok(())
2060 }
2061
2062 pub fn set_system_privilege(
2066 &mut self,
2067 grantee: RoleId,
2068 grantor: RoleId,
2069 acl_mode: Option<AclMode>,
2070 ) -> Result<(), CatalogError> {
2071 self.system_privileges.set(
2072 SystemPrivilegesKey { grantee, grantor },
2073 acl_mode.map(|acl_mode| SystemPrivilegesValue { acl_mode }),
2074 self.op_id,
2075 )?;
2076 Ok(())
2077 }
2078
2079 pub fn set_system_privileges(
2081 &mut self,
2082 system_privileges: Vec<MzAclItem>,
2083 ) -> Result<(), CatalogError> {
2084 if system_privileges.is_empty() {
2085 return Ok(());
2086 }
2087
2088 let system_privileges = system_privileges
2089 .into_iter()
2090 .map(DurableType::into_key_value)
2091 .map(|(k, v)| (k, Some(v)))
2092 .collect();
2093 self.system_privileges
2094 .set_many(system_privileges, self.op_id)?;
2095 Ok(())
2096 }
2097
2098 pub fn set_setting(&mut self, name: String, value: Option<String>) -> Result<(), CatalogError> {
2100 self.settings.set(
2101 SettingKey { name },
2102 value.map(|value| SettingValue { value }),
2103 self.op_id,
2104 )?;
2105 Ok(())
2106 }
2107
2108 pub fn set_catalog_content_version(&mut self, version: String) -> Result<(), CatalogError> {
2109 self.set_setting(CATALOG_CONTENT_VERSION_KEY.to_string(), Some(version))
2110 }
2111
2112 pub fn insert_introspection_source_indexes(
2114 &mut self,
2115 introspection_source_indexes: Vec<(ClusterId, String, CatalogItemId, GlobalId)>,
2116 temporary_oids: &HashSet<u32>,
2117 ) -> Result<(), CatalogError> {
2118 if introspection_source_indexes.is_empty() {
2119 return Ok(());
2120 }
2121
2122 let amount = usize_to_u64(introspection_source_indexes.len());
2123 let oids = self.allocate_oids(amount, temporary_oids)?;
2124 let introspection_source_indexes: Vec<_> = introspection_source_indexes
2125 .into_iter()
2126 .zip_eq(oids)
2127 .map(
2128 |((cluster_id, name, item_id, index_id), oid)| IntrospectionSourceIndex {
2129 cluster_id,
2130 name,
2131 item_id,
2132 index_id,
2133 oid,
2134 },
2135 )
2136 .collect();
2137
2138 for introspection_source_index in introspection_source_indexes {
2139 let (key, value) = introspection_source_index.into_key_value();
2140 self.introspection_sources.insert(key, value, self.op_id)?;
2141 }
2142
2143 Ok(())
2144 }
2145
2146 pub fn set_system_object_mappings(
2148 &mut self,
2149 mappings: Vec<SystemObjectMapping>,
2150 ) -> Result<(), CatalogError> {
2151 if mappings.is_empty() {
2152 return Ok(());
2153 }
2154
2155 let mappings = mappings
2156 .into_iter()
2157 .map(DurableType::into_key_value)
2158 .map(|(k, v)| (k, Some(v)))
2159 .collect();
2160 self.system_gid_mapping.set_many(mappings, self.op_id)?;
2161 Ok(())
2162 }
2163
2164 pub fn set_replicas(&mut self, replicas: Vec<ClusterReplica>) -> Result<(), CatalogError> {
2166 if replicas.is_empty() {
2167 return Ok(());
2168 }
2169
2170 let replicas = replicas
2171 .into_iter()
2172 .map(DurableType::into_key_value)
2173 .map(|(k, v)| (k, Some(v)))
2174 .collect();
2175 self.cluster_replicas.set_many(replicas, self.op_id)?;
2176 Ok(())
2177 }
2178
2179 pub fn set_config(&mut self, key: String, value: Option<u64>) -> Result<(), CatalogError> {
2181 match value {
2182 Some(value) => {
2183 let config = Config { key, value };
2184 let (key, value) = config.into_key_value();
2185 self.configs.set(key, Some(value), self.op_id)?;
2186 }
2187 None => {
2188 self.configs.set(ConfigKey { key }, None, self.op_id)?;
2189 }
2190 }
2191 Ok(())
2192 }
2193
2194 pub fn get_config(&self, key: String) -> Option<u64> {
2196 self.configs
2197 .get(&ConfigKey { key })
2198 .map(|entry| entry.value)
2199 }
2200
2201 pub fn get_setting(&self, name: String) -> Option<&str> {
2203 self.settings
2204 .get(&SettingKey { name })
2205 .map(|entry| &*entry.value)
2206 }
2207
2208 pub fn get_builtin_migration_shard(&self) -> Option<ShardId> {
2209 self.get_setting(BUILTIN_MIGRATION_SHARD_KEY.to_string())
2210 .map(|shard_id| shard_id.parse().expect("valid ShardId"))
2211 }
2212
2213 pub fn set_builtin_migration_shard(&mut self, shard_id: ShardId) -> Result<(), CatalogError> {
2214 self.set_setting(
2215 BUILTIN_MIGRATION_SHARD_KEY.to_string(),
2216 Some(shard_id.to_string()),
2217 )
2218 }
2219
2220 pub fn get_expression_cache_shard(&self) -> Option<ShardId> {
2221 self.get_setting(EXPRESSION_CACHE_SHARD_KEY.to_string())
2222 .map(|shard_id| shard_id.parse().expect("valid ShardId"))
2223 }
2224
2225 pub fn set_expression_cache_shard(&mut self, shard_id: ShardId) -> Result<(), CatalogError> {
2226 self.set_setting(
2227 EXPRESSION_CACHE_SHARD_KEY.to_string(),
2228 Some(shard_id.to_string()),
2229 )
2230 }
2231
2232 pub fn set_0dt_deployment_max_wait(&mut self, value: Duration) -> Result<(), CatalogError> {
2238 self.set_config(
2239 WITH_0DT_DEPLOYMENT_MAX_WAIT.into(),
2240 Some(
2241 value
2242 .as_millis()
2243 .try_into()
2244 .expect("max wait fits into u64"),
2245 ),
2246 )
2247 }
2248
2249 pub fn set_0dt_deployment_ddl_check_interval(
2256 &mut self,
2257 value: Duration,
2258 ) -> Result<(), CatalogError> {
2259 self.set_config(
2260 WITH_0DT_DEPLOYMENT_DDL_CHECK_INTERVAL.into(),
2261 Some(
2262 value
2263 .as_millis()
2264 .try_into()
2265 .expect("ddl check interval fits into u64"),
2266 ),
2267 )
2268 }
2269
2270 pub fn set_enable_0dt_deployment_panic_after_timeout(
2276 &mut self,
2277 value: bool,
2278 ) -> Result<(), CatalogError> {
2279 self.set_config(
2280 ENABLE_0DT_DEPLOYMENT_PANIC_AFTER_TIMEOUT.into(),
2281 Some(u64::from(value)),
2282 )
2283 }
2284
2285 pub fn reset_0dt_deployment_max_wait(&mut self) -> Result<(), CatalogError> {
2291 self.set_config(WITH_0DT_DEPLOYMENT_MAX_WAIT.into(), None)
2292 }
2293
2294 pub fn reset_0dt_deployment_ddl_check_interval(&mut self) -> Result<(), CatalogError> {
2301 self.set_config(WITH_0DT_DEPLOYMENT_DDL_CHECK_INTERVAL.into(), None)
2302 }
2303
2304 pub fn reset_enable_0dt_deployment_panic_after_timeout(&mut self) -> Result<(), CatalogError> {
2311 self.set_config(ENABLE_0DT_DEPLOYMENT_PANIC_AFTER_TIMEOUT.into(), None)
2312 }
2313
2314 pub fn set_system_config_synced_once(&mut self) -> Result<(), CatalogError> {
2316 self.set_config(SYSTEM_CONFIG_SYNCED_KEY.into(), Some(1))
2317 }
2318
2319 pub fn update_comment(
2320 &mut self,
2321 object_id: CommentObjectId,
2322 sub_component: Option<usize>,
2323 comment: Option<String>,
2324 ) -> Result<(), CatalogError> {
2325 let key = CommentKey {
2326 object_id,
2327 sub_component,
2328 };
2329 let value = comment.map(|c| CommentValue { comment: c });
2330 self.comments.set(key, value, self.op_id)?;
2331
2332 Ok(())
2333 }
2334
2335 pub fn drop_comments(
2336 &mut self,
2337 object_ids: &BTreeSet<CommentObjectId>,
2338 ) -> Result<(), CatalogError> {
2339 if object_ids.is_empty() {
2340 return Ok(());
2341 }
2342
2343 self.comments
2344 .delete(|k, _v| object_ids.contains(&k.object_id), self.op_id);
2345 Ok(())
2346 }
2347
2348 pub fn update_source_references(
2349 &mut self,
2350 source_id: CatalogItemId,
2351 references: Vec<SourceReference>,
2352 updated_at: u64,
2353 ) -> Result<(), CatalogError> {
2354 let key = SourceReferencesKey { source_id };
2355 let value = SourceReferencesValue {
2356 references,
2357 updated_at,
2358 };
2359 self.source_references.set(key, Some(value), self.op_id)?;
2360 Ok(())
2361 }
2362
2363 pub fn upsert_system_config(&mut self, name: &str, value: String) -> Result<(), CatalogError> {
2365 let key = ServerConfigurationKey {
2366 name: name.to_string(),
2367 };
2368 let value = ServerConfigurationValue { value };
2369 self.system_configurations
2370 .set(key, Some(value), self.op_id)?;
2371 Ok(())
2372 }
2373
2374 pub fn remove_system_config(&mut self, name: &str) {
2376 let key = ServerConfigurationKey {
2377 name: name.to_string(),
2378 };
2379 self.system_configurations
2380 .set(key, None, self.op_id)
2381 .expect("cannot have uniqueness violation");
2382 }
2383
2384 pub fn clear_system_configs(&mut self) {
2386 self.system_configurations.delete(|_k, _v| true, self.op_id);
2387 }
2388
2389 pub fn get_cluster_system_configurations(
2391 &self,
2392 ) -> impl Iterator<Item = ClusterSystemConfiguration> + use<'_> {
2393 self.cluster_system_configurations
2394 .items()
2395 .into_iter()
2396 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2397 }
2398
2399 pub fn upsert_cluster_system_config(
2402 &mut self,
2403 cluster_id: ClusterId,
2404 name: &str,
2405 value: String,
2406 ) -> Result<(), CatalogError> {
2407 let key = ClusterSystemConfigurationKey {
2408 cluster_id,
2409 name: name.to_string(),
2410 };
2411 let value = ClusterSystemConfigurationValue { value };
2412 self.cluster_system_configurations
2413 .set(key, Some(value), self.op_id)?;
2414 Ok(())
2415 }
2416
2417 pub fn remove_cluster_system_config(&mut self, cluster_id: ClusterId, name: &str) {
2420 let key = ClusterSystemConfigurationKey {
2421 cluster_id,
2422 name: name.to_string(),
2423 };
2424 self.cluster_system_configurations
2425 .set(key, None, self.op_id)
2426 .expect("cannot have uniqueness violation");
2427 }
2428
2429 pub fn get_replica_system_configurations(
2431 &self,
2432 ) -> impl Iterator<Item = ReplicaSystemConfiguration> + use<'_> {
2433 self.replica_system_configurations
2434 .items()
2435 .into_iter()
2436 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2437 }
2438
2439 pub fn upsert_replica_system_config(
2442 &mut self,
2443 replica_id: ReplicaId,
2444 name: &str,
2445 value: String,
2446 ) -> Result<(), CatalogError> {
2447 let key = ReplicaSystemConfigurationKey {
2448 replica_id,
2449 name: name.to_string(),
2450 };
2451 let value = ReplicaSystemConfigurationValue { value };
2452 self.replica_system_configurations
2453 .set(key, Some(value), self.op_id)?;
2454 Ok(())
2455 }
2456
2457 pub fn remove_replica_system_config(&mut self, replica_id: ReplicaId, name: &str) {
2460 let key = ReplicaSystemConfigurationKey {
2461 replica_id,
2462 name: name.to_string(),
2463 };
2464 self.replica_system_configurations
2465 .set(key, None, self.op_id)
2466 .expect("cannot have uniqueness violation");
2467 }
2468
2469 pub(crate) fn insert_config(&mut self, key: String, value: u64) -> Result<(), CatalogError> {
2470 match self.configs.insert(
2471 ConfigKey { key: key.clone() },
2472 ConfigValue { value },
2473 self.op_id,
2474 ) {
2475 Ok(_) => Ok(()),
2476 Err(_) => Err(SqlCatalogError::ConfigAlreadyExists(key).into()),
2477 }
2478 }
2479
2480 pub fn get_clusters(&self) -> impl Iterator<Item = Cluster> + use<'_> {
2481 self.clusters
2482 .items()
2483 .into_iter()
2484 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2485 }
2486
2487 pub fn get_cluster_replicas(&self) -> impl Iterator<Item = ClusterReplica> + use<'_> {
2488 self.cluster_replicas
2489 .items()
2490 .into_iter()
2491 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2492 }
2493
2494 pub fn get_databases(&self) -> impl Iterator<Item = Database> + use<'_> {
2495 self.databases
2496 .items()
2497 .into_iter()
2498 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2499 }
2500
2501 pub fn get_roles(&self) -> impl Iterator<Item = Role> + use<'_> {
2502 self.roles
2503 .items()
2504 .into_iter()
2505 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2506 }
2507
2508 pub fn get_network_policies(&self) -> impl Iterator<Item = NetworkPolicy> + use<'_> {
2509 self.network_policies
2510 .items()
2511 .into_iter()
2512 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2513 }
2514
2515 pub fn get_cluster_replica_sizes(&self) -> impl Iterator<Item = ClusterReplicaSize> + use<'_> {
2516 self.cluster_replica_sizes
2517 .items()
2518 .into_iter()
2519 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2520 }
2521
2522 pub fn insert_system_cluster_replica_size(
2523 &mut self,
2524 name: String,
2525 allocation: ReplicaAllocation,
2526 ) -> Result<ClusterReplicaSizeId, CatalogError> {
2527 let id = self.get_and_increment_id(SYSTEM_CLUSTER_REPLICA_SIZE_ID_ALLOC_KEY.to_string())?;
2528 let id = ClusterReplicaSizeId::System(id);
2529 let (key, value) = ClusterReplicaSize {
2530 id,
2531 name,
2532 allocation,
2533 }
2534 .into_key_value();
2535 self.cluster_replica_sizes.insert(key, value, self.op_id)?;
2536 Ok(id)
2537 }
2538
2539 pub fn update_cluster_replica_size(
2540 &mut self,
2541 size: ClusterReplicaSize,
2542 ) -> Result<(), CatalogError> {
2543 let id = size.id;
2544 let (key, value) = size.into_key_value();
2545 if self
2546 .cluster_replica_sizes
2547 .update_by_key(key, value, self.op_id)?
2548 {
2549 Ok(())
2550 } else {
2551 Err(SqlCatalogError::UnknownClusterReplicaSize(id.to_string()).into())
2552 }
2553 }
2554
2555 pub fn remove_cluster_replica_size(
2556 &mut self,
2557 id: ClusterReplicaSizeId,
2558 ) -> Result<(), CatalogError> {
2559 match self
2560 .cluster_replica_sizes
2561 .delete_by_key(ClusterReplicaSizeKey { id }, self.op_id)
2562 {
2563 Some(_) => Ok(()),
2564 None => Err(SqlCatalogError::UnknownClusterReplicaSize(id.to_string()).into()),
2565 }
2566 }
2567
2568 pub fn get_system_object_mappings(
2569 &self,
2570 ) -> impl Iterator<Item = SystemObjectMapping> + use<'_> {
2571 self.system_gid_mapping
2572 .items()
2573 .into_iter()
2574 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2575 }
2576
2577 pub fn get_schemas(&self) -> impl Iterator<Item = Schema> + use<'_> {
2578 self.schemas
2579 .items()
2580 .into_iter()
2581 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2582 }
2583
2584 pub fn get_system_configurations(&self) -> impl Iterator<Item = SystemConfiguration> + use<'_> {
2585 self.system_configurations
2586 .items()
2587 .into_iter()
2588 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2589 }
2590
2591 pub fn get_schema(&self, id: &SchemaId) -> Option<Schema> {
2592 let key = SchemaKey { id: *id };
2593 self.schemas
2594 .get(&key)
2595 .map(|v| DurableType::from_key_value(key, v.clone()))
2596 }
2597
2598 pub fn get_introspection_source_indexes(
2599 &self,
2600 cluster_id: ClusterId,
2601 ) -> BTreeMap<&str, (GlobalId, u32)> {
2602 self.introspection_sources
2603 .items()
2604 .into_iter()
2605 .filter(|(k, _v)| k.cluster_id == cluster_id)
2606 .map(|(k, v)| (k.name.as_str(), (v.global_id.into(), v.oid)))
2607 .collect()
2608 }
2609
2610 pub fn get_catalog_content_version(&self) -> Option<&str> {
2611 self.settings
2612 .get(&SettingKey {
2613 name: CATALOG_CONTENT_VERSION_KEY.to_string(),
2614 })
2615 .map(|value| &*value.value)
2616 }
2617
2618 pub fn get_authentication_mock_nonce(&self) -> Option<String> {
2619 self.settings
2620 .get(&SettingKey {
2621 name: MOCK_AUTHENTICATION_NONCE_KEY.to_string(),
2622 })
2623 .map(|value| value.value.clone())
2624 }
2625
2626 #[must_use]
2632 pub fn get_and_commit_op_updates(&mut self) -> Vec<StateUpdate> {
2633 let updates = self.get_op_updates();
2634 self.commit_op();
2635 updates
2636 }
2637
2638 fn get_op_updates(&self) -> Vec<StateUpdate> {
2639 fn get_collection_op_updates<'a, T>(
2640 table_txn: &'a TableTransaction<T::Key, T::Value>,
2641 kind_fn: impl Fn(T) -> StateUpdateKind + 'a,
2642 op: Timestamp,
2643 ) -> impl Iterator<Item = (StateUpdateKind, StateDiff)> + 'a
2644 where
2645 T::Key: Ord + Eq + Clone + Debug,
2646 T::Value: Ord + Clone + Debug,
2647 T: DurableType,
2648 {
2649 table_txn
2650 .pending
2651 .iter()
2652 .flat_map(|(k, vs)| vs.into_iter().map(move |v| (k, v)))
2653 .filter_map(move |(k, v)| {
2654 if v.ts == op {
2655 let key = k.clone();
2656 let value = v.value.clone();
2657 let diff = v.diff.clone().try_into().expect("invalid diff");
2658 let update = DurableType::from_key_value(key, value);
2659 let kind = kind_fn(update);
2660 Some((kind, diff))
2661 } else {
2662 None
2663 }
2664 })
2665 }
2666
2667 fn get_large_collection_op_updates<'a, T>(
2668 collection: &'a Vec<(T::Key, Diff, Timestamp)>,
2669 kind_fn: impl Fn(T) -> StateUpdateKind + 'a,
2670 op: Timestamp,
2671 ) -> impl Iterator<Item = (StateUpdateKind, StateDiff)> + 'a
2672 where
2673 T::Key: Ord + Eq + Clone + Debug,
2674 T: DurableType<Value = ()>,
2675 {
2676 collection.iter().filter_map(move |(k, diff, ts)| {
2677 if *ts == op {
2678 let key = k.clone();
2679 let diff = diff.clone().try_into().expect("invalid diff");
2680 let update = DurableType::from_key_value(key, ());
2681 let kind = kind_fn(update);
2682 Some((kind, diff))
2683 } else {
2684 None
2685 }
2686 })
2687 }
2688
2689 let Transaction {
2690 durable_catalog: _,
2691 databases,
2692 schemas,
2693 items,
2694 comments,
2695 roles,
2696 role_auth,
2697 clusters,
2698 network_policies,
2699 cluster_replica_sizes,
2700 cluster_replicas,
2701 introspection_sources,
2702 system_gid_mapping,
2703 system_configurations,
2704 cluster_system_configurations,
2705 replica_system_configurations,
2706 default_privileges,
2707 source_references,
2708 system_privileges,
2709 audit_log_updates,
2710 storage_collection_metadata,
2711 unfinalized_shards,
2712 id_allocator: _,
2714 configs: _,
2715 settings: _,
2716 txn_wal_shard: _,
2717 item_columns: _,
2719 index_columns: _,
2720 upper,
2721 op_id: _,
2722 commit_capability: _,
2723 } = &self;
2724
2725 let updates = std::iter::empty()
2726 .chain(get_collection_op_updates(
2727 roles,
2728 StateUpdateKind::Role,
2729 self.op_id,
2730 ))
2731 .chain(get_collection_op_updates(
2732 role_auth,
2733 StateUpdateKind::RoleAuth,
2734 self.op_id,
2735 ))
2736 .chain(get_collection_op_updates(
2737 databases,
2738 StateUpdateKind::Database,
2739 self.op_id,
2740 ))
2741 .chain(get_collection_op_updates(
2742 schemas,
2743 StateUpdateKind::Schema,
2744 self.op_id,
2745 ))
2746 .chain(get_collection_op_updates(
2747 default_privileges,
2748 StateUpdateKind::DefaultPrivilege,
2749 self.op_id,
2750 ))
2751 .chain(get_collection_op_updates(
2752 system_privileges,
2753 StateUpdateKind::SystemPrivilege,
2754 self.op_id,
2755 ))
2756 .chain(get_collection_op_updates(
2757 system_configurations,
2758 StateUpdateKind::SystemConfiguration,
2759 self.op_id,
2760 ))
2761 .chain(get_collection_op_updates(
2762 cluster_system_configurations,
2763 StateUpdateKind::ClusterSystemConfiguration,
2764 self.op_id,
2765 ))
2766 .chain(get_collection_op_updates(
2767 replica_system_configurations,
2768 StateUpdateKind::ReplicaSystemConfiguration,
2769 self.op_id,
2770 ))
2771 .chain(get_collection_op_updates(
2772 clusters,
2773 StateUpdateKind::Cluster,
2774 self.op_id,
2775 ))
2776 .chain(get_collection_op_updates(
2777 network_policies,
2778 StateUpdateKind::NetworkPolicy,
2779 self.op_id,
2780 ))
2781 .chain(get_collection_op_updates(
2782 cluster_replica_sizes,
2783 StateUpdateKind::ClusterReplicaSize,
2784 self.op_id,
2785 ))
2786 .chain(get_collection_op_updates(
2787 introspection_sources,
2788 StateUpdateKind::IntrospectionSourceIndex,
2789 self.op_id,
2790 ))
2791 .chain(get_collection_op_updates(
2792 cluster_replicas,
2793 StateUpdateKind::ClusterReplica,
2794 self.op_id,
2795 ))
2796 .chain(get_collection_op_updates(
2797 system_gid_mapping,
2798 StateUpdateKind::SystemObjectMapping,
2799 self.op_id,
2800 ))
2801 .chain(get_collection_op_updates(
2802 items,
2803 StateUpdateKind::Item,
2804 self.op_id,
2805 ))
2806 .chain(get_collection_op_updates(
2807 comments,
2808 StateUpdateKind::Comment,
2809 self.op_id,
2810 ))
2811 .chain(get_collection_op_updates(
2812 source_references,
2813 StateUpdateKind::SourceReferences,
2814 self.op_id,
2815 ))
2816 .chain(get_collection_op_updates(
2817 storage_collection_metadata,
2818 StateUpdateKind::StorageCollectionMetadata,
2819 self.op_id,
2820 ))
2821 .chain(get_collection_op_updates(
2822 unfinalized_shards,
2823 StateUpdateKind::UnfinalizedShard,
2824 self.op_id,
2825 ))
2826 .chain(get_large_collection_op_updates(
2827 audit_log_updates,
2828 StateUpdateKind::AuditLog,
2829 self.op_id,
2830 ))
2831 .map(|(kind, diff)| StateUpdate {
2832 kind,
2833 ts: upper.clone(),
2834 diff,
2835 })
2836 .collect();
2837
2838 updates
2839 }
2840
2841 pub fn is_savepoint(&self) -> bool {
2842 self.durable_catalog.is_savepoint()
2843 }
2844
2845 pub fn is_read_only(&self) -> bool {
2846 self.durable_catalog.is_read_only()
2847 }
2848
2849 fn commit_op(&mut self) {
2850 self.op_id += 1;
2851 }
2852
2853 pub fn op_id(&self) -> Timestamp {
2854 self.op_id
2855 }
2856
2857 pub fn upper(&self) -> mz_repr::Timestamp {
2858 self.upper
2859 }
2860
2861 fn ensure_committable(&self) -> Result<(), CatalogError> {
2862 match self.commit_capability {
2863 Some(_) => Ok(()),
2864 None => Err(DurableCatalogError::DryRunTransaction.into()),
2865 }
2866 }
2867
2868 pub(super) async fn ensure_not_out_of_sync(&mut self) -> Result<(), CatalogError> {
2870 self.durable_catalog
2871 .ensure_not_out_of_sync(self.upper)
2872 .await
2873 }
2874
2875 pub(crate) fn into_parts(
2876 self,
2877 ) -> Result<(TransactionBatch, &'a mut dyn DurableCatalogState), CatalogError> {
2878 let commit_capability = self
2879 .commit_capability
2880 .ok_or(DurableCatalogError::DryRunTransaction)?;
2881 let audit_log_updates = self
2882 .audit_log_updates
2883 .into_iter()
2884 .map(|(k, diff, _op)| (k.into_proto(), (), diff))
2885 .collect();
2886
2887 let txn_batch = TransactionBatch {
2888 databases: self.databases.pending(),
2889 schemas: self.schemas.pending(),
2890 items: self.items.pending(),
2891 item_columns: self.item_columns.pending(),
2892 index_columns: self.index_columns.pending(),
2893 comments: self.comments.pending(),
2894 roles: self.roles.pending(),
2895 role_auth: self.role_auth.pending(),
2896 clusters: self.clusters.pending(),
2897 cluster_replicas: self.cluster_replicas.pending(),
2898 network_policies: self.network_policies.pending(),
2899 cluster_replica_sizes: self.cluster_replica_sizes.pending(),
2900 introspection_sources: self.introspection_sources.pending(),
2901 id_allocator: self.id_allocator.pending(),
2902 configs: self.configs.pending(),
2903 source_references: self.source_references.pending(),
2904 settings: self.settings.pending(),
2905 system_gid_mapping: self.system_gid_mapping.pending(),
2906 system_configurations: self.system_configurations.pending(),
2907 cluster_system_configurations: self.cluster_system_configurations.pending(),
2908 replica_system_configurations: self.replica_system_configurations.pending(),
2909 default_privileges: self.default_privileges.pending(),
2910 system_privileges: self.system_privileges.pending(),
2911 storage_collection_metadata: self.storage_collection_metadata.pending(),
2912 unfinalized_shards: self.unfinalized_shards.pending(),
2913 txn_wal_shard: self.txn_wal_shard.pending(),
2914 audit_log_updates,
2915 upper: self.upper,
2916 _commit_capability: commit_capability,
2917 };
2918 Ok((txn_batch, self.durable_catalog))
2919 }
2920
2921 #[mz_ore::instrument(level = "debug")]
2937 pub(crate) async fn commit_internal(
2938 self,
2939 commit_ts: mz_repr::Timestamp,
2940 ) -> Result<(&'a mut dyn DurableCatalogState, mz_repr::Timestamp), CatalogError> {
2941 self.ensure_committable()?;
2942 let (mut txn_batch, durable_catalog) = self.into_parts()?;
2943 let TransactionBatch {
2944 databases,
2945 schemas,
2946 items,
2947 item_columns,
2948 index_columns,
2949 comments,
2950 roles,
2951 role_auth,
2952 clusters,
2953 cluster_replicas,
2954 network_policies,
2955 cluster_replica_sizes,
2956 introspection_sources,
2957 id_allocator,
2958 configs,
2959 source_references,
2960 settings,
2961 system_gid_mapping,
2962 system_configurations,
2963 cluster_system_configurations,
2964 replica_system_configurations,
2965 default_privileges,
2966 system_privileges,
2967 storage_collection_metadata,
2968 unfinalized_shards,
2969 txn_wal_shard,
2970 audit_log_updates,
2971 upper: _,
2972 _commit_capability: _,
2973 } = &mut txn_batch;
2974 differential_dataflow::consolidation::consolidate_updates(databases);
2977 differential_dataflow::consolidation::consolidate_updates(schemas);
2978 differential_dataflow::consolidation::consolidate_updates(items);
2979 differential_dataflow::consolidation::consolidate_updates(item_columns);
2980 differential_dataflow::consolidation::consolidate_updates(index_columns);
2981 differential_dataflow::consolidation::consolidate_updates(comments);
2982 differential_dataflow::consolidation::consolidate_updates(roles);
2983 differential_dataflow::consolidation::consolidate_updates(role_auth);
2984 differential_dataflow::consolidation::consolidate_updates(clusters);
2985 differential_dataflow::consolidation::consolidate_updates(cluster_replicas);
2986 differential_dataflow::consolidation::consolidate_updates(network_policies);
2987 differential_dataflow::consolidation::consolidate_updates(cluster_replica_sizes);
2988 differential_dataflow::consolidation::consolidate_updates(introspection_sources);
2989 differential_dataflow::consolidation::consolidate_updates(id_allocator);
2990 differential_dataflow::consolidation::consolidate_updates(configs);
2991 differential_dataflow::consolidation::consolidate_updates(settings);
2992 differential_dataflow::consolidation::consolidate_updates(source_references);
2993 differential_dataflow::consolidation::consolidate_updates(system_gid_mapping);
2994 differential_dataflow::consolidation::consolidate_updates(system_configurations);
2995 differential_dataflow::consolidation::consolidate_updates(cluster_system_configurations);
2996 differential_dataflow::consolidation::consolidate_updates(replica_system_configurations);
2997 differential_dataflow::consolidation::consolidate_updates(default_privileges);
2998 differential_dataflow::consolidation::consolidate_updates(system_privileges);
2999 differential_dataflow::consolidation::consolidate_updates(storage_collection_metadata);
3000 differential_dataflow::consolidation::consolidate_updates(unfinalized_shards);
3001 differential_dataflow::consolidation::consolidate_updates(txn_wal_shard);
3002 differential_dataflow::consolidation::consolidate_updates(audit_log_updates);
3003
3004 let upper = durable_catalog
3005 .commit_transaction(txn_batch, commit_ts)
3006 .await?;
3007 Ok((durable_catalog, upper))
3008 }
3009
3010 #[mz_ore::instrument(level = "debug")]
3031 pub async fn commit(self, commit_ts: mz_repr::Timestamp) -> Result<(), CatalogError> {
3032 self.ensure_committable()?;
3033 let op_updates = self.get_op_updates();
3034 assert!(
3035 op_updates.is_empty(),
3036 "unconsumed transaction updates: {op_updates:?}"
3037 );
3038
3039 let (durable_storage, upper) = self.commit_internal(commit_ts).await?;
3040 let updates = durable_storage.sync_updates(upper).await?;
3042 soft_assert_no_log!(
3051 durable_storage.is_read_only()
3052 || updates
3053 .iter()
3054 .all(|update| update.ts >= commit_ts && update.ts < upper),
3055 "unconsumed updates existed before transaction commit: commit_ts={commit_ts:?}, upper={upper:?}, updates:{updates:?}"
3056 );
3057 Ok(())
3058 }
3059}
3060
3061use crate::durable::async_trait;
3062
3063use super::objects::{RoleAuthKey, RoleAuthValue};
3064
3065#[async_trait]
3066impl StorageTxn for Transaction<'_> {
3067 fn get_collection_metadata(&self) -> BTreeMap<GlobalId, ShardId> {
3068 self.storage_collection_metadata
3069 .items()
3070 .into_iter()
3071 .map(
3072 |(
3073 StorageCollectionMetadataKey { id },
3074 StorageCollectionMetadataValue { shard },
3075 )| { (*id, shard.clone()) },
3076 )
3077 .collect()
3078 }
3079
3080 fn insert_collection_metadata(
3081 &mut self,
3082 metadata: BTreeMap<GlobalId, ShardId>,
3083 ) -> Result<(), StorageError> {
3084 for (id, shard) in metadata {
3085 self.storage_collection_metadata
3086 .insert(
3087 StorageCollectionMetadataKey { id },
3088 StorageCollectionMetadataValue {
3089 shard: shard.clone(),
3090 },
3091 self.op_id,
3092 )
3093 .map_err(|err| match err {
3094 DurableCatalogError::DuplicateKey => {
3095 StorageError::CollectionMetadataAlreadyExists(id)
3096 }
3097 DurableCatalogError::UniquenessViolation => {
3098 StorageError::PersistShardAlreadyInUse(shard)
3099 }
3100 err => StorageError::Generic(anyhow::anyhow!(err)),
3101 })?;
3102 }
3103 Ok(())
3104 }
3105
3106 fn delete_collection_metadata(&mut self, ids: BTreeSet<GlobalId>) -> Vec<(GlobalId, ShardId)> {
3107 let ks: Vec<_> = ids
3108 .into_iter()
3109 .map(|id| StorageCollectionMetadataKey { id })
3110 .collect();
3111 self.storage_collection_metadata
3112 .delete_by_keys(ks, self.op_id)
3113 .into_iter()
3114 .map(
3115 |(
3116 StorageCollectionMetadataKey { id },
3117 StorageCollectionMetadataValue { shard },
3118 )| (id, shard),
3119 )
3120 .collect()
3121 }
3122
3123 fn get_unfinalized_shards(&self) -> BTreeSet<ShardId> {
3124 self.unfinalized_shards
3125 .items()
3126 .into_iter()
3127 .map(|(UnfinalizedShardKey { shard }, ())| *shard)
3128 .collect()
3129 }
3130
3131 fn insert_unfinalized_shards(&mut self, s: BTreeSet<ShardId>) -> Result<(), StorageError> {
3132 for shard in s {
3133 match self
3134 .unfinalized_shards
3135 .insert(UnfinalizedShardKey { shard }, (), self.op_id)
3136 {
3137 Ok(()) | Err(DurableCatalogError::DuplicateKey) => {}
3139 Err(e) => Err(StorageError::Generic(anyhow::anyhow!(e)))?,
3140 };
3141 }
3142 Ok(())
3143 }
3144
3145 fn remove_unfinalized_shards(&mut self, shards: BTreeSet<ShardId>) {
3146 let ks: Vec<_> = shards
3147 .into_iter()
3148 .map(|shard| UnfinalizedShardKey { shard })
3149 .collect();
3150 let _ = self.unfinalized_shards.delete_by_keys(ks, self.op_id);
3151 }
3152
3153 fn get_txn_wal_shard(&self) -> Option<ShardId> {
3154 self.txn_wal_shard
3155 .values()
3156 .iter()
3157 .next()
3158 .map(|TxnWalShardValue { shard }| *shard)
3159 }
3160
3161 fn write_txn_wal_shard(&mut self, shard: ShardId) -> Result<(), StorageError> {
3162 self.txn_wal_shard
3163 .insert((), TxnWalShardValue { shard }, self.op_id)
3164 .map_err(|err| match err {
3165 DurableCatalogError::DuplicateKey => StorageError::TxnWalShardAlreadyExists,
3166 err => StorageError::Generic(anyhow::anyhow!(err)),
3167 })
3168 }
3169}
3170
3171#[derive(Debug, PartialEq)]
3173pub struct TransactionBatch {
3174 pub(crate) databases: Vec<(proto::DatabaseKey, proto::DatabaseValue, Diff)>,
3175 pub(crate) schemas: Vec<(proto::SchemaKey, proto::SchemaValue, Diff)>,
3176 pub(crate) items: Vec<(proto::ItemKey, proto::ItemValue, Diff)>,
3177 pub(crate) item_columns: Vec<(proto::ItemColumnsKey, proto::ItemColumnsValue, Diff)>,
3178 pub(crate) index_columns: Vec<(proto::IndexColumnsKey, proto::IndexColumnsValue, Diff)>,
3179 pub(crate) comments: Vec<(proto::CommentKey, proto::CommentValue, Diff)>,
3180 pub(crate) roles: Vec<(proto::RoleKey, proto::RoleValue, Diff)>,
3181 pub(crate) role_auth: Vec<(proto::RoleAuthKey, proto::RoleAuthValue, Diff)>,
3182 pub(crate) clusters: Vec<(proto::ClusterKey, proto::ClusterValue, Diff)>,
3183 pub(crate) cluster_replicas: Vec<(proto::ClusterReplicaKey, proto::ClusterReplicaValue, Diff)>,
3184 pub(crate) network_policies: Vec<(proto::NetworkPolicyKey, proto::NetworkPolicyValue, Diff)>,
3185 pub(crate) cluster_replica_sizes: Vec<(
3186 proto::ClusterReplicaSizeKey,
3187 proto::ClusterReplicaSizeValue,
3188 Diff,
3189 )>,
3190 pub(crate) introspection_sources: Vec<(
3191 proto::ClusterIntrospectionSourceIndexKey,
3192 proto::ClusterIntrospectionSourceIndexValue,
3193 Diff,
3194 )>,
3195 pub(crate) id_allocator: Vec<(proto::IdAllocKey, proto::IdAllocValue, Diff)>,
3196 pub(crate) configs: Vec<(proto::ConfigKey, proto::ConfigValue, Diff)>,
3197 pub(crate) settings: Vec<(proto::SettingKey, proto::SettingValue, Diff)>,
3198 pub(crate) system_gid_mapping: Vec<(proto::GidMappingKey, proto::GidMappingValue, Diff)>,
3199 pub(crate) system_configurations: Vec<(
3200 proto::ServerConfigurationKey,
3201 proto::ServerConfigurationValue,
3202 Diff,
3203 )>,
3204 pub(crate) cluster_system_configurations: Vec<(
3205 proto::ClusterSystemConfigurationKey,
3206 proto::ClusterSystemConfigurationValue,
3207 Diff,
3208 )>,
3209 pub(crate) replica_system_configurations: Vec<(
3210 proto::ReplicaSystemConfigurationKey,
3211 proto::ReplicaSystemConfigurationValue,
3212 Diff,
3213 )>,
3214 pub(crate) default_privileges: Vec<(
3215 proto::DefaultPrivilegesKey,
3216 proto::DefaultPrivilegesValue,
3217 Diff,
3218 )>,
3219 pub(crate) source_references: Vec<(
3220 proto::SourceReferencesKey,
3221 proto::SourceReferencesValue,
3222 Diff,
3223 )>,
3224 pub(crate) system_privileges: Vec<(
3225 proto::SystemPrivilegesKey,
3226 proto::SystemPrivilegesValue,
3227 Diff,
3228 )>,
3229 pub(crate) storage_collection_metadata: Vec<(
3230 proto::StorageCollectionMetadataKey,
3231 proto::StorageCollectionMetadataValue,
3232 Diff,
3233 )>,
3234 pub(crate) unfinalized_shards: Vec<(proto::UnfinalizedShardKey, (), Diff)>,
3235 pub(crate) txn_wal_shard: Vec<((), proto::TxnWalShardValue, Diff)>,
3236 pub(crate) audit_log_updates: Vec<(proto::AuditLogKey, (), Diff)>,
3237 pub(crate) upper: mz_repr::Timestamp,
3239 _commit_capability: CommitCapability,
3242}
3243
3244impl TransactionBatch {
3245 pub fn is_empty(&self) -> bool {
3246 let TransactionBatch {
3247 databases,
3248 schemas,
3249 items,
3250 item_columns,
3251 index_columns,
3252 comments,
3253 roles,
3254 role_auth,
3255 clusters,
3256 cluster_replicas,
3257 network_policies,
3258 cluster_replica_sizes,
3259 introspection_sources,
3260 id_allocator,
3261 configs,
3262 settings,
3263 source_references,
3264 system_gid_mapping,
3265 system_configurations,
3266 cluster_system_configurations,
3267 replica_system_configurations,
3268 default_privileges,
3269 system_privileges,
3270 storage_collection_metadata,
3271 unfinalized_shards,
3272 txn_wal_shard,
3273 audit_log_updates,
3274 upper: _,
3275 _commit_capability: _,
3276 } = self;
3277 databases.is_empty()
3278 && schemas.is_empty()
3279 && items.is_empty()
3280 && item_columns.is_empty()
3281 && index_columns.is_empty()
3282 && comments.is_empty()
3283 && roles.is_empty()
3284 && role_auth.is_empty()
3285 && clusters.is_empty()
3286 && cluster_replicas.is_empty()
3287 && network_policies.is_empty()
3288 && cluster_replica_sizes.is_empty()
3289 && introspection_sources.is_empty()
3290 && id_allocator.is_empty()
3291 && configs.is_empty()
3292 && settings.is_empty()
3293 && source_references.is_empty()
3294 && system_gid_mapping.is_empty()
3295 && system_configurations.is_empty()
3296 && cluster_system_configurations.is_empty()
3297 && replica_system_configurations.is_empty()
3298 && default_privileges.is_empty()
3299 && system_privileges.is_empty()
3300 && storage_collection_metadata.is_empty()
3301 && unfinalized_shards.is_empty()
3302 && txn_wal_shard.is_empty()
3303 && audit_log_updates.is_empty()
3304 }
3305}
3306
3307#[derive(Debug, Clone, PartialEq, Eq)]
3308struct TransactionUpdate<V> {
3309 value: V,
3310 ts: Timestamp,
3311 diff: Diff,
3312}
3313
3314trait UniqueName {
3316 const HAS_UNIQUE_NAME: bool;
3319 fn unique_name(&self) -> &str;
3321}
3322
3323mod unique_name {
3324 use crate::durable::objects::*;
3325
3326 macro_rules! impl_unique_name {
3327 ($($t:ty),* $(,)?) => {
3328 $(
3329 impl crate::durable::transaction::UniqueName for $t {
3330 const HAS_UNIQUE_NAME: bool = true;
3331 fn unique_name(&self) -> &str {
3332 &self.name
3333 }
3334 }
3335 )*
3336 };
3337 }
3338
3339 macro_rules! impl_no_unique_name {
3340 ($($t:ty),* $(,)?) => {
3341 $(
3342 impl crate::durable::transaction::UniqueName for $t {
3343 const HAS_UNIQUE_NAME: bool = false;
3344 fn unique_name(&self) -> &str {
3345 ""
3346 }
3347 }
3348 )*
3349 };
3350 }
3351
3352 impl_unique_name! {
3353 ClusterReplicaSizeValue,
3354 ClusterReplicaValue,
3355 ClusterValue,
3356 DatabaseValue,
3357 ItemValue,
3358 NetworkPolicyValue,
3359 RoleValue,
3360 SchemaValue,
3361 }
3362
3363 impl_no_unique_name!(
3364 (),
3365 ClusterIntrospectionSourceIndexValue,
3366 ClusterSystemConfigurationValue,
3367 CommentValue,
3368 ConfigValue,
3369 DefaultPrivilegesValue,
3370 GidMappingValue,
3371 IdAllocValue,
3372 IndexColumnsValue,
3373 ItemColumnsValue,
3374 ReplicaSystemConfigurationValue,
3375 ServerConfigurationValue,
3376 SettingValue,
3377 SourceReferencesValue,
3378 StorageCollectionMetadataValue,
3379 SystemPrivilegesValue,
3380 TxnWalShardValue,
3381 RoleAuthValue,
3382 );
3383
3384 #[cfg(test)]
3385 mod test {
3386 impl_no_unique_name!(String,);
3387 }
3388}
3389
3390#[derive(Debug)]
3398struct UniquenessCheck<V> {
3399 violation: fn(a: &V, b: &V) -> bool,
3400 is_unique_key_unchanged_after_update: fn(prev: &V, next: &V) -> bool,
3401}
3402
3403#[derive(Debug)]
3413struct TableTransaction<K, V> {
3414 initial: BTreeMap<K, V>,
3415 pending: BTreeMap<K, Vec<TransactionUpdate<V>>>,
3418 uniqueness_check: Option<UniquenessCheck<V>>,
3420}
3421
3422impl<K, V> TableTransaction<K, V>
3423where
3424 K: Ord + Eq + Clone + Debug,
3425 V: Ord + Clone + Debug + UniqueName,
3426{
3427 fn new<KP, VP>(initial: BTreeMap<KP, VP>) -> Result<Self, TryFromProtoError>
3434 where
3435 K: RustType<KP>,
3436 V: RustType<VP>,
3437 {
3438 let initial = initial
3439 .into_iter()
3440 .map(RustType::from_proto)
3441 .collect::<Result<_, _>>()?;
3442
3443 Ok(Self {
3444 initial,
3445 pending: BTreeMap::new(),
3446 uniqueness_check: None,
3447 })
3448 }
3449
3450 fn new_with_uniqueness_fn<KP, VP>(
3453 initial: BTreeMap<KP, VP>,
3454 uniqueness_violation: fn(a: &V, b: &V) -> bool,
3455 is_unique_key_unchanged_after_update: fn(prev: &V, next: &V) -> bool,
3456 ) -> Result<Self, TryFromProtoError>
3457 where
3458 K: RustType<KP>,
3459 V: RustType<VP>,
3460 {
3461 let initial = initial
3462 .into_iter()
3463 .map(RustType::from_proto)
3464 .collect::<Result<_, _>>()?;
3465
3466 Ok(Self {
3467 initial,
3468 pending: BTreeMap::new(),
3469 uniqueness_check: Some(UniquenessCheck {
3470 violation: uniqueness_violation,
3471 is_unique_key_unchanged_after_update,
3472 }),
3473 })
3474 }
3475
3476 fn pending<KP, VP>(self) -> Vec<(KP, VP, Diff)>
3479 where
3480 K: RustType<KP>,
3481 V: RustType<VP>,
3482 {
3483 soft_assert_no_log!(self.verify().is_ok());
3484 self.pending
3487 .into_iter()
3488 .flat_map(|(k, v)| {
3489 let mut v: Vec<_> = v
3490 .into_iter()
3491 .map(|TransactionUpdate { value, ts: _, diff }| (value, diff))
3492 .collect();
3493 differential_dataflow::consolidation::consolidate(&mut v);
3494 v.into_iter().map(move |(v, diff)| (k.clone(), v, diff))
3495 })
3496 .map(|(key, val, diff)| (key.into_proto(), val.into_proto(), diff))
3497 .collect()
3498 }
3499
3500 fn verify(&self) -> Result<(), DurableCatalogError> {
3505 if let Some(check) = &self.uniqueness_check {
3506 let items = self.values();
3508 if V::HAS_UNIQUE_NAME {
3509 let by_name: BTreeMap<_, _> = items
3510 .iter()
3511 .enumerate()
3512 .map(|(v, vi)| (vi.unique_name(), (v, vi)))
3513 .collect();
3514 for (i, vi) in items.iter().enumerate() {
3515 if let Some((j, vj)) = by_name.get(vi.unique_name()) {
3516 if i != *j && (check.violation)(vi, *vj) {
3517 return Err(DurableCatalogError::UniquenessViolation);
3518 }
3519 }
3520 }
3521 } else {
3522 for (i, vi) in items.iter().enumerate() {
3523 for (j, vj) in items.iter().enumerate() {
3524 if i != j && (check.violation)(vi, vj) {
3525 return Err(DurableCatalogError::UniquenessViolation);
3526 }
3527 }
3528 }
3529 }
3530 }
3531 soft_assert_no_log!(
3532 self.pending
3533 .values()
3534 .all(|pending| { pending.is_sorted_by(|a, b| a.ts <= b.ts) }),
3535 "pending should be sorted by timestamp: {:?}",
3536 self.pending
3537 );
3538 Ok(())
3539 }
3540
3541 fn verify_keys<'a>(
3546 &self,
3547 keys: impl IntoIterator<Item = &'a K>,
3548 ) -> Result<(), DurableCatalogError>
3549 where
3550 K: 'a,
3551 {
3552 if let Some(check) = &self.uniqueness_check {
3553 let entries: Vec<_> = keys
3554 .into_iter()
3555 .filter_map(|key| self.get(key).map(|value| (key, value)))
3556 .collect();
3557 for (ki, vi) in self.items() {
3559 for (kj, vj) in &entries {
3560 if ki != *kj && (check.violation)(vi, vj) {
3561 return Err(DurableCatalogError::UniquenessViolation);
3562 }
3563 }
3564 }
3565 }
3566 soft_assert_no_log!(self.verify().is_ok());
3567 Ok(())
3568 }
3569
3570 fn for_values<'a, F: FnMut(&'a K, &'a V)>(&'a self, mut f: F) {
3573 let mut seen = BTreeSet::new();
3574 for k in self.pending.keys() {
3575 seen.insert(k);
3576 let v = self.get(k);
3577 if let Some(v) = v {
3580 f(k, v);
3581 }
3582 }
3583 for (k, v) in self.initial.iter() {
3584 if !seen.contains(k) {
3586 f(k, v);
3587 }
3588 }
3589 }
3590
3591 fn get(&self, k: &K) -> Option<&V> {
3593 let pending = self.pending.get(k).map(Vec::as_slice).unwrap_or_default();
3594 let mut updates = Vec::with_capacity(pending.len() + 1);
3595 if let Some(initial) = self.initial.get(k) {
3596 updates.push((initial, Diff::ONE));
3597 }
3598 updates.extend(
3599 pending
3600 .into_iter()
3601 .map(|TransactionUpdate { value, ts: _, diff }| (value, *diff)),
3602 );
3603
3604 differential_dataflow::consolidation::consolidate(&mut updates);
3605 assert!(updates.len() <= 1);
3606 updates.into_iter().next().map(|(v, _)| v)
3607 }
3608
3609 #[cfg(test)]
3614 fn items_cloned(&self) -> BTreeMap<K, V> {
3615 let mut items = BTreeMap::new();
3616 self.for_values(|k, v| {
3617 items.insert(k.clone(), v.clone());
3618 });
3619 items
3620 }
3621
3622 fn current_items_proto<KP, VP>(&self) -> BTreeMap<KP, VP>
3626 where
3627 K: RustType<KP>,
3628 V: RustType<VP>,
3629 KP: Ord,
3630 {
3631 let mut items = BTreeMap::new();
3632 self.for_values(|k, v| {
3633 items.insert(k.into_proto(), v.into_proto());
3634 });
3635 items
3636 }
3637
3638 fn items(&self) -> BTreeMap<&K, &V> {
3641 let mut items = BTreeMap::new();
3642 self.for_values(|k, v| {
3643 items.insert(k, v);
3644 });
3645 items
3646 }
3647
3648 fn values(&self) -> BTreeSet<&V> {
3650 let mut items = BTreeSet::new();
3651 self.for_values(|_, v| {
3652 items.insert(v);
3653 });
3654 items
3655 }
3656
3657 fn len(&self) -> usize {
3659 let mut count = 0;
3660 self.for_values(|_, _| {
3661 count += 1;
3662 });
3663 count
3664 }
3665
3666 fn for_values_mut<F: FnMut(&mut BTreeMap<K, Vec<TransactionUpdate<V>>>, &K, &V)>(
3670 &mut self,
3671 mut f: F,
3672 ) {
3673 let mut pending = BTreeMap::new();
3674 self.for_values(|k, v| f(&mut pending, k, v));
3675 for (k, updates) in pending {
3676 self.pending.entry(k).or_default().extend(updates);
3677 }
3678 }
3679
3680 fn insert(&mut self, k: K, v: V, ts: Timestamp) -> Result<(), DurableCatalogError> {
3684 let mut violation = None;
3685 let uniqueness_violation = self.uniqueness_check.as_ref().map(|check| check.violation);
3686 self.for_values(|for_k, for_v| {
3687 if &k == for_k {
3688 violation = Some(DurableCatalogError::DuplicateKey);
3689 }
3690 if let Some(uniqueness_violation) = uniqueness_violation {
3691 if uniqueness_violation(for_v, &v) {
3692 violation = Some(DurableCatalogError::UniquenessViolation);
3693 }
3694 }
3695 });
3696 if let Some(violation) = violation {
3697 return Err(violation);
3698 }
3699 self.pending.entry(k).or_default().push(TransactionUpdate {
3700 value: v,
3701 ts,
3702 diff: Diff::ONE,
3703 });
3704 soft_assert_no_log!(self.verify().is_ok());
3705 Ok(())
3706 }
3707
3708 fn update<F: Fn(&K, &V) -> Option<V>>(
3717 &mut self,
3718 f: F,
3719 ts: Timestamp,
3720 ) -> Result<Diff, DurableCatalogError> {
3721 let mut changed = Diff::ZERO;
3722 let mut keys = BTreeSet::new();
3723 let pending = self.pending.clone();
3725 self.for_values_mut(|p, k, v| {
3726 if let Some(next) = f(k, v) {
3727 changed += Diff::ONE;
3728 keys.insert(k.clone());
3729 let updates = p.entry(k.clone()).or_default();
3730 updates.push(TransactionUpdate {
3731 value: v.clone(),
3732 ts,
3733 diff: Diff::MINUS_ONE,
3734 });
3735 updates.push(TransactionUpdate {
3736 value: next,
3737 ts,
3738 diff: Diff::ONE,
3739 });
3740 }
3741 });
3742 if let Err(err) = self.verify_keys(&keys) {
3744 self.pending = pending;
3745 Err(err)
3746 } else {
3747 Ok(changed)
3748 }
3749 }
3750
3751 fn update_by_key(&mut self, k: K, v: V, ts: Timestamp) -> Result<bool, DurableCatalogError> {
3756 if let Some(cur_v) = self.get(&k) {
3757 if v != *cur_v {
3758 self.set(k, Some(v), ts)?;
3759 }
3760 Ok(true)
3761 } else {
3762 Ok(false)
3763 }
3764 }
3765
3766 fn update_by_keys(
3771 &mut self,
3772 kvs: impl IntoIterator<Item = (K, V)>,
3773 ts: Timestamp,
3774 ) -> Result<Diff, DurableCatalogError> {
3775 let kvs: Vec<_> = kvs
3776 .into_iter()
3777 .filter_map(|(k, v)| match self.get(&k) {
3778 Some(cur_v) => Some((*cur_v == v, k, v)),
3780 None => None,
3781 })
3782 .collect();
3783 let changed = kvs.len();
3784 let changed =
3785 Diff::try_from(changed).map_err(|e| DurableCatalogError::Internal(e.to_string()))?;
3786 let kvs = kvs
3787 .into_iter()
3788 .filter(|(no_op, _, _)| !no_op)
3790 .map(|(_, k, v)| (k, Some(v)))
3791 .collect();
3792 self.set_many(kvs, ts)?;
3793 Ok(changed)
3794 }
3795
3796 fn update_needs_uniqueness_check(&self, prev: Option<&V>, next: Option<&V>) -> bool {
3798 match (&self.uniqueness_check, prev, next) {
3799 (None, _, _) | (_, _, None) => false,
3801 (Some(check), Some(prev), Some(next)) => {
3803 !(check.is_unique_key_unchanged_after_update)(prev, next)
3804 }
3805 (Some(_), None, Some(_)) => true,
3807 }
3808 }
3809
3810 fn set(&mut self, k: K, v: Option<V>, ts: Timestamp) -> Result<Option<V>, DurableCatalogError> {
3817 let prev = self.get(&k).cloned();
3818 let needs_uniqueness_check = self.update_needs_uniqueness_check(prev.as_ref(), v.as_ref());
3819 let entry = self.pending.entry(k.clone()).or_default();
3820 let restore_len = entry.len();
3821
3822 match (v, prev.clone()) {
3823 (Some(v), Some(prev)) => {
3824 entry.push(TransactionUpdate {
3825 value: prev,
3826 ts,
3827 diff: Diff::MINUS_ONE,
3828 });
3829 entry.push(TransactionUpdate {
3830 value: v,
3831 ts,
3832 diff: Diff::ONE,
3833 });
3834 }
3835 (Some(v), None) => {
3836 entry.push(TransactionUpdate {
3837 value: v,
3838 ts,
3839 diff: Diff::ONE,
3840 });
3841 }
3842 (None, Some(prev)) => {
3843 entry.push(TransactionUpdate {
3844 value: prev,
3845 ts,
3846 diff: Diff::MINUS_ONE,
3847 });
3848 }
3849 (None, None) => {}
3850 }
3851
3852 if needs_uniqueness_check {
3854 if let Err(err) = self.verify_keys([&k]) {
3855 let pending = self.pending.get_mut(&k).expect("inserted above");
3858 pending.truncate(restore_len);
3859 return Err(err);
3860 }
3861 }
3862 Ok(prev)
3863 }
3864
3865 fn set_many(
3870 &mut self,
3871 kvs: BTreeMap<K, Option<V>>,
3872 ts: Timestamp,
3873 ) -> Result<BTreeMap<K, Option<V>>, DurableCatalogError> {
3874 if kvs.is_empty() {
3875 return Ok(BTreeMap::new());
3876 }
3877
3878 let mut prevs = BTreeMap::new();
3879 let mut restores = BTreeMap::new();
3880 let mut keys_to_verify_uniqueness = Vec::new();
3882
3883 for (k, v) in kvs {
3884 let prev = self.get(&k).cloned();
3885 if self.update_needs_uniqueness_check(prev.as_ref(), v.as_ref()) {
3886 keys_to_verify_uniqueness.push(k.clone());
3887 }
3888 let entry = self.pending.entry(k.clone()).or_default();
3889 restores.insert(k.clone(), entry.len());
3890
3891 match (v, prev.clone()) {
3892 (Some(v), Some(prev)) => {
3893 entry.push(TransactionUpdate {
3894 value: prev,
3895 ts,
3896 diff: Diff::MINUS_ONE,
3897 });
3898 entry.push(TransactionUpdate {
3899 value: v,
3900 ts,
3901 diff: Diff::ONE,
3902 });
3903 }
3904 (Some(v), None) => {
3905 entry.push(TransactionUpdate {
3906 value: v,
3907 ts,
3908 diff: Diff::ONE,
3909 });
3910 }
3911 (None, Some(prev)) => {
3912 entry.push(TransactionUpdate {
3913 value: prev,
3914 ts,
3915 diff: Diff::MINUS_ONE,
3916 });
3917 }
3918 (None, None) => {}
3919 }
3920
3921 prevs.insert(k, prev);
3922 }
3923
3924 if let Err(err) = self.verify_keys(keys_to_verify_uniqueness.iter()) {
3926 for (k, restore_len) in restores {
3927 let pending = self.pending.get_mut(&k).expect("inserted above");
3930 pending.truncate(restore_len);
3931 }
3932 Err(err)
3933 } else {
3934 Ok(prevs)
3935 }
3936 }
3937
3938 fn delete<F: Fn(&K, &V) -> bool>(&mut self, f: F, ts: Timestamp) -> Vec<(K, V)> {
3944 let mut deleted = Vec::new();
3945 self.for_values_mut(|p, k, v| {
3946 if f(k, v) {
3947 deleted.push((k.clone(), v.clone()));
3948 p.entry(k.clone()).or_default().push(TransactionUpdate {
3949 value: v.clone(),
3950 ts,
3951 diff: Diff::MINUS_ONE,
3952 });
3953 }
3954 });
3955 soft_assert_no_log!(self.verify().is_ok());
3956 deleted
3957 }
3958
3959 fn delete_by_key(&mut self, k: K, ts: Timestamp) -> Option<V> {
3963 self.set(k, None, ts)
3964 .expect("deleting an entry cannot violate uniqueness")
3965 }
3966
3967 fn delete_by_keys(&mut self, ks: impl IntoIterator<Item = K>, ts: Timestamp) -> Vec<(K, V)> {
3971 let kvs = ks.into_iter().map(|k| (k, None)).collect();
3972 let prevs = self
3973 .set_many(kvs, ts)
3974 .expect("deleting entries cannot violate uniqueness");
3975 prevs
3976 .into_iter()
3977 .filter_map(|(k, v)| v.map(|v| (k, v)))
3978 .collect()
3979 }
3980}
3981
3982#[cfg(test)]
3983#[allow(clippy::unwrap_used)]
3984mod tests {
3985 use super::*;
3986
3987 use mz_controller::clusters::ReplicaLogging;
3988 use mz_ore::now::SYSTEM_TIME;
3989 use mz_ore::{assert_none, assert_ok};
3990 use mz_persist_client::cache::PersistClientCache;
3991 use mz_persist_types::PersistLocation;
3992 use semver::Version;
3993
3994 use crate::durable::{
3995 ReplicaConfig, ReplicaLocation, TestCatalogStateBuilder, test_bootstrap_args,
3996 };
3997 use crate::memory;
3998
3999 #[mz_ore::test]
4000 fn test_table_transaction_simple() {
4001 fn uniqueness_violation(a: &String, b: &String) -> bool {
4002 a == b
4003 }
4004 let mut table = TableTransaction::new_with_uniqueness_fn(
4005 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "a".to_string())]),
4006 uniqueness_violation,
4007 uniqueness_violation,
4008 )
4009 .unwrap();
4010
4011 assert_ok!(table.insert(2i64.to_le_bytes().to_vec(), "b".to_string(), 0));
4014 assert_ok!(table.insert(3i64.to_le_bytes().to_vec(), "c".to_string(), 0));
4015 assert!(
4016 table
4017 .insert(1i64.to_le_bytes().to_vec(), "c".to_string(), 0)
4018 .is_err()
4019 );
4020 assert!(
4021 table
4022 .insert(4i64.to_le_bytes().to_vec(), "c".to_string(), 0)
4023 .is_err()
4024 );
4025 }
4026
4027 #[mz_ore::test]
4028 fn test_skip_scan_when_unique_key_unchanged() {
4029 fn first_char_same(prev: &String, next: &String) -> bool {
4030 prev.chars().next() == next.chars().next()
4031 }
4032
4033 fn panic_uniqueness_violation(_: &String, _: &String) -> bool {
4035 panic!("uniqueness scan ran for an update that kept the same unique key");
4036 }
4037 let mut table = TableTransaction::new_with_uniqueness_fn(
4038 BTreeMap::from([
4039 (1i64.to_le_bytes().to_vec(), "a1".to_string()),
4040 (2i64.to_le_bytes().to_vec(), "b1".to_string()),
4041 ]),
4042 panic_uniqueness_violation,
4043 first_char_same,
4045 )
4046 .unwrap();
4047 assert!(
4050 table
4051 .update_by_key(1i64.to_le_bytes().to_vec(), "a2".to_string(), 0)
4052 .unwrap()
4053 );
4054
4055 fn real_uniqueness_violation(a: &String, b: &String) -> bool {
4057 a.chars().next() == b.chars().next()
4058 }
4059 let mut table = TableTransaction::new_with_uniqueness_fn(
4060 BTreeMap::from([
4061 (1i64.to_le_bytes().to_vec(), "a1".to_string()),
4062 (2i64.to_le_bytes().to_vec(), "b1".to_string()),
4063 ]),
4064 real_uniqueness_violation,
4065 first_char_same,
4066 )
4067 .unwrap();
4068 assert!(
4071 table
4072 .update_by_key(1i64.to_le_bytes().to_vec(), "b2".to_string(), 0)
4073 .is_err()
4074 );
4075 }
4076
4077 #[mz_ore::test]
4078 fn test_table_transaction() {
4079 fn uniqueness_violation(a: &String, b: &String) -> bool {
4080 a == b
4081 }
4082 let mut table: BTreeMap<Vec<u8>, String> = BTreeMap::new();
4083
4084 fn commit(
4085 table: &mut BTreeMap<Vec<u8>, String>,
4086 mut pending: Vec<(Vec<u8>, String, Diff)>,
4087 ) {
4088 pending.sort_by(|a, b| a.2.cmp(&b.2));
4090 for (k, v, diff) in pending {
4091 if diff == Diff::MINUS_ONE {
4092 let prev = table.remove(&k);
4093 assert_eq!(prev, Some(v));
4094 } else if diff == Diff::ONE {
4095 let prev = table.insert(k, v);
4096 assert_eq!(prev, None);
4097 } else {
4098 panic!("unexpected diff: {diff}");
4099 }
4100 }
4101 }
4102
4103 table.insert(1i64.to_le_bytes().to_vec(), "v1".to_string());
4104 table.insert(2i64.to_le_bytes().to_vec(), "v2".to_string());
4105 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4106 table.clone(),
4107 uniqueness_violation,
4108 uniqueness_violation,
4109 )
4110 .unwrap();
4111 assert_eq!(table_txn.items_cloned(), table);
4112 assert_eq!(table_txn.delete(|_k, _v| false, 0).len(), 0);
4113 assert_eq!(table_txn.delete(|_k, v| v == "v2", 1).len(), 1);
4114 assert_eq!(
4115 table_txn.items_cloned(),
4116 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v1".to_string())])
4117 );
4118 assert_eq!(
4119 table_txn
4120 .update(|_k, _v| Some("v3".to_string()), 2)
4121 .unwrap(),
4122 Diff::ONE
4123 );
4124
4125 table_txn
4127 .insert(3i64.to_le_bytes().to_vec(), "v3".to_string(), 3)
4128 .unwrap_err();
4129
4130 table_txn
4131 .insert(3i64.to_le_bytes().to_vec(), "v4".to_string(), 4)
4132 .unwrap();
4133 assert_eq!(
4134 table_txn.items_cloned(),
4135 BTreeMap::from([
4136 (1i64.to_le_bytes().to_vec(), "v3".to_string()),
4137 (3i64.to_le_bytes().to_vec(), "v4".to_string()),
4138 ])
4139 );
4140 let err = table_txn
4141 .update(|_k, _v| Some("v1".to_string()), 5)
4142 .unwrap_err();
4143 assert!(
4144 matches!(err, DurableCatalogError::UniquenessViolation),
4145 "unexpected err: {err:?}"
4146 );
4147 let pending = table_txn.pending();
4148 assert_eq!(
4149 pending,
4150 vec![
4151 (
4152 1i64.to_le_bytes().to_vec(),
4153 "v1".to_string(),
4154 Diff::MINUS_ONE
4155 ),
4156 (1i64.to_le_bytes().to_vec(), "v3".to_string(), Diff::ONE),
4157 (
4158 2i64.to_le_bytes().to_vec(),
4159 "v2".to_string(),
4160 Diff::MINUS_ONE
4161 ),
4162 (3i64.to_le_bytes().to_vec(), "v4".to_string(), Diff::ONE),
4163 ]
4164 );
4165 commit(&mut table, pending);
4166 assert_eq!(
4167 table,
4168 BTreeMap::from([
4169 (1i64.to_le_bytes().to_vec(), "v3".to_string()),
4170 (3i64.to_le_bytes().to_vec(), "v4".to_string())
4171 ])
4172 );
4173
4174 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4175 table.clone(),
4176 uniqueness_violation,
4177 uniqueness_violation,
4178 )
4179 .unwrap();
4180 assert_eq!(
4182 table_txn.delete(|k, _v| k == &1i64.to_le_bytes(), 0).len(),
4183 1
4184 );
4185 table_txn
4186 .insert(1i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4187 .unwrap();
4188 table_txn
4190 .insert(5i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4191 .unwrap_err();
4192 table_txn
4194 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 0)
4195 .unwrap_err();
4196 assert_eq!(
4197 table_txn.delete(|k, _v| k == &1i64.to_le_bytes(), 0).len(),
4198 1
4199 );
4200 table_txn
4202 .insert(5i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4203 .unwrap();
4204 table_txn
4205 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 0)
4206 .unwrap();
4207 let pending = table_txn.pending();
4208 assert_eq!(
4209 pending,
4210 vec![
4211 (
4212 1i64.to_le_bytes().to_vec(),
4213 "v3".to_string(),
4214 Diff::MINUS_ONE
4215 ),
4216 (1i64.to_le_bytes().to_vec(), "v5".to_string(), Diff::ONE),
4217 (5i64.to_le_bytes().to_vec(), "v3".to_string(), Diff::ONE),
4218 ]
4219 );
4220 commit(&mut table, pending);
4221 assert_eq!(
4222 table,
4223 BTreeMap::from([
4224 (1i64.to_le_bytes().to_vec(), "v5".to_string()),
4225 (3i64.to_le_bytes().to_vec(), "v4".to_string()),
4226 (5i64.to_le_bytes().to_vec(), "v3".to_string()),
4227 ])
4228 );
4229
4230 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4231 table.clone(),
4232 uniqueness_violation,
4233 uniqueness_violation,
4234 )
4235 .unwrap();
4236 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 3);
4237 table_txn
4238 .insert(1i64.to_le_bytes().to_vec(), "v1".to_string(), 0)
4239 .unwrap();
4240
4241 commit(&mut table, table_txn.pending());
4242 assert_eq!(
4243 table,
4244 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v1".to_string()),])
4245 );
4246
4247 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4248 table.clone(),
4249 uniqueness_violation,
4250 uniqueness_violation,
4251 )
4252 .unwrap();
4253 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 1);
4254 table_txn
4255 .insert(1i64.to_le_bytes().to_vec(), "v2".to_string(), 0)
4256 .unwrap();
4257 commit(&mut table, table_txn.pending());
4258 assert_eq!(
4259 table,
4260 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v2".to_string()),])
4261 );
4262
4263 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4265 table.clone(),
4266 uniqueness_violation,
4267 uniqueness_violation,
4268 )
4269 .unwrap();
4270 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 1);
4271 table_txn
4272 .insert(1i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4273 .unwrap();
4274 table_txn
4275 .insert(1i64.to_le_bytes().to_vec(), "v4".to_string(), 1)
4276 .unwrap_err();
4277 assert_eq!(table_txn.delete(|_k, _v| true, 1).len(), 1);
4278 table_txn
4279 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 1)
4280 .unwrap();
4281 commit(&mut table, table_txn.pending());
4282 assert_eq!(
4283 table.clone().into_iter().collect::<Vec<_>>(),
4284 vec![(1i64.to_le_bytes().to_vec(), "v5".to_string())]
4285 );
4286
4287 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4289 table.clone(),
4290 uniqueness_violation,
4291 uniqueness_violation,
4292 )
4293 .unwrap();
4294 table_txn
4296 .set(2i64.to_le_bytes().to_vec(), Some("v5".to_string()), 0)
4297 .unwrap_err();
4298 table_txn
4299 .set(3i64.to_le_bytes().to_vec(), Some("v6".to_string()), 1)
4300 .unwrap();
4301 table_txn.set(2i64.to_le_bytes().to_vec(), None, 2).unwrap();
4302 table_txn.set(1i64.to_le_bytes().to_vec(), None, 2).unwrap();
4303 let pending = table_txn.pending();
4304 assert_eq!(
4305 pending,
4306 vec![
4307 (
4308 1i64.to_le_bytes().to_vec(),
4309 "v5".to_string(),
4310 Diff::MINUS_ONE
4311 ),
4312 (3i64.to_le_bytes().to_vec(), "v6".to_string(), Diff::ONE),
4313 ]
4314 );
4315 commit(&mut table, pending);
4316 assert_eq!(
4317 table,
4318 BTreeMap::from([(3i64.to_le_bytes().to_vec(), "v6".to_string())])
4319 );
4320
4321 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4323 table.clone(),
4324 uniqueness_violation,
4325 uniqueness_violation,
4326 )
4327 .unwrap();
4328 table_txn
4329 .set(3i64.to_le_bytes().to_vec(), Some("v6".to_string()), 0)
4330 .unwrap();
4331 let pending = table_txn.pending::<Vec<u8>, String>();
4332 assert!(pending.is_empty());
4333
4334 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4336 table.clone(),
4337 uniqueness_violation,
4338 uniqueness_violation,
4339 )
4340 .unwrap();
4341 table_txn
4343 .set_many(
4344 BTreeMap::from([
4345 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4346 (42i64.to_le_bytes().to_vec(), Some("v1".to_string())),
4347 ]),
4348 0,
4349 )
4350 .unwrap_err();
4351 table_txn
4352 .set_many(
4353 BTreeMap::from([
4354 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4355 (3i64.to_le_bytes().to_vec(), Some("v1".to_string())),
4356 ]),
4357 1,
4358 )
4359 .unwrap();
4360 table_txn
4361 .set_many(
4362 BTreeMap::from([
4363 (42i64.to_le_bytes().to_vec(), Some("v7".to_string())),
4364 (3i64.to_le_bytes().to_vec(), None),
4365 ]),
4366 2,
4367 )
4368 .unwrap();
4369 let pending = table_txn.pending();
4370 assert_eq!(
4371 pending,
4372 vec![
4373 (1i64.to_le_bytes().to_vec(), "v6".to_string(), Diff::ONE),
4374 (
4375 3i64.to_le_bytes().to_vec(),
4376 "v6".to_string(),
4377 Diff::MINUS_ONE
4378 ),
4379 (42i64.to_le_bytes().to_vec(), "v7".to_string(), Diff::ONE),
4380 ]
4381 );
4382 commit(&mut table, pending);
4383 assert_eq!(
4384 table,
4385 BTreeMap::from([
4386 (1i64.to_le_bytes().to_vec(), "v6".to_string()),
4387 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4388 ])
4389 );
4390
4391 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4393 table.clone(),
4394 uniqueness_violation,
4395 uniqueness_violation,
4396 )
4397 .unwrap();
4398 table_txn
4399 .set_many(
4400 BTreeMap::from([
4401 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4402 (42i64.to_le_bytes().to_vec(), Some("v7".to_string())),
4403 ]),
4404 0,
4405 )
4406 .unwrap();
4407 let pending = table_txn.pending::<Vec<u8>, String>();
4408 assert!(pending.is_empty());
4409 commit(&mut table, pending);
4410 assert_eq!(
4411 table,
4412 BTreeMap::from([
4413 (1i64.to_le_bytes().to_vec(), "v6".to_string()),
4414 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4415 ])
4416 );
4417
4418 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4420 table.clone(),
4421 uniqueness_violation,
4422 uniqueness_violation,
4423 )
4424 .unwrap();
4425 table_txn
4427 .update_by_key(1i64.to_le_bytes().to_vec(), "v7".to_string(), 0)
4428 .unwrap_err();
4429 assert!(
4430 table_txn
4431 .update_by_key(1i64.to_le_bytes().to_vec(), "v8".to_string(), 1)
4432 .unwrap()
4433 );
4434 assert!(
4435 !table_txn
4436 .update_by_key(5i64.to_le_bytes().to_vec(), "v8".to_string(), 2)
4437 .unwrap()
4438 );
4439 let pending = table_txn.pending();
4440 assert_eq!(
4441 pending,
4442 vec![
4443 (
4444 1i64.to_le_bytes().to_vec(),
4445 "v6".to_string(),
4446 Diff::MINUS_ONE
4447 ),
4448 (1i64.to_le_bytes().to_vec(), "v8".to_string(), Diff::ONE),
4449 ]
4450 );
4451 commit(&mut table, pending);
4452 assert_eq!(
4453 table,
4454 BTreeMap::from([
4455 (1i64.to_le_bytes().to_vec(), "v8".to_string()),
4456 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4457 ])
4458 );
4459
4460 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4462 table.clone(),
4463 uniqueness_violation,
4464 uniqueness_violation,
4465 )
4466 .unwrap();
4467 assert!(
4468 table_txn
4469 .update_by_key(1i64.to_le_bytes().to_vec(), "v8".to_string(), 0)
4470 .unwrap()
4471 );
4472 let pending = table_txn.pending::<Vec<u8>, String>();
4473 assert!(pending.is_empty());
4474 commit(&mut table, pending);
4475 assert_eq!(
4476 table,
4477 BTreeMap::from([
4478 (1i64.to_le_bytes().to_vec(), "v8".to_string()),
4479 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4480 ])
4481 );
4482
4483 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4485 table.clone(),
4486 uniqueness_violation,
4487 uniqueness_violation,
4488 )
4489 .unwrap();
4490 table_txn
4492 .update_by_keys(
4493 [
4494 (1i64.to_le_bytes().to_vec(), "v7".to_string()),
4495 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4496 ],
4497 0,
4498 )
4499 .unwrap_err();
4500 let n = table_txn
4501 .update_by_keys(
4502 [
4503 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4504 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4505 ],
4506 1,
4507 )
4508 .unwrap();
4509 assert_eq!(n, Diff::ONE);
4510 let n = table_txn
4511 .update_by_keys(
4512 [
4513 (15i64.to_le_bytes().to_vec(), "v9".to_string()),
4514 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4515 ],
4516 2,
4517 )
4518 .unwrap();
4519 assert_eq!(n, Diff::ZERO);
4520 let pending = table_txn.pending();
4521 assert_eq!(
4522 pending,
4523 vec![
4524 (
4525 1i64.to_le_bytes().to_vec(),
4526 "v8".to_string(),
4527 Diff::MINUS_ONE
4528 ),
4529 (1i64.to_le_bytes().to_vec(), "v9".to_string(), Diff::ONE),
4530 ]
4531 );
4532 commit(&mut table, pending);
4533 assert_eq!(
4534 table,
4535 BTreeMap::from([
4536 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4537 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4538 ])
4539 );
4540
4541 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4543 table.clone(),
4544 uniqueness_violation,
4545 uniqueness_violation,
4546 )
4547 .unwrap();
4548 let n = table_txn
4549 .update_by_keys(
4550 [
4551 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4552 (42i64.to_le_bytes().to_vec(), "v7".to_string()),
4553 ],
4554 0,
4555 )
4556 .unwrap();
4557 assert_eq!(n, Diff::from(2));
4558 let pending = table_txn.pending::<Vec<u8>, String>();
4559 assert!(pending.is_empty());
4560 commit(&mut table, pending);
4561 assert_eq!(
4562 table,
4563 BTreeMap::from([
4564 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4565 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4566 ])
4567 );
4568
4569 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4571 table.clone(),
4572 uniqueness_violation,
4573 uniqueness_violation,
4574 )
4575 .unwrap();
4576 let prev = table_txn.delete_by_key(1i64.to_le_bytes().to_vec(), 0);
4577 assert_eq!(prev, Some("v9".to_string()));
4578 let prev = table_txn.delete_by_key(5i64.to_le_bytes().to_vec(), 1);
4579 assert_none!(prev);
4580 let prev = table_txn.delete_by_key(1i64.to_le_bytes().to_vec(), 2);
4581 assert_none!(prev);
4582 let pending = table_txn.pending();
4583 assert_eq!(
4584 pending,
4585 vec![(
4586 1i64.to_le_bytes().to_vec(),
4587 "v9".to_string(),
4588 Diff::MINUS_ONE
4589 ),]
4590 );
4591 commit(&mut table, pending);
4592 assert_eq!(
4593 table,
4594 BTreeMap::from([(42i64.to_le_bytes().to_vec(), "v7".to_string())])
4595 );
4596
4597 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4599 table.clone(),
4600 uniqueness_violation,
4601 uniqueness_violation,
4602 )
4603 .unwrap();
4604 let prevs = table_txn.delete_by_keys(
4605 [42i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4606 0,
4607 );
4608 assert_eq!(
4609 prevs,
4610 vec![(42i64.to_le_bytes().to_vec(), "v7".to_string())]
4611 );
4612 let prevs = table_txn.delete_by_keys(
4613 [42i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4614 1,
4615 );
4616 assert_eq!(prevs, vec![]);
4617 let prevs = table_txn.delete_by_keys(
4618 [10i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4619 2,
4620 );
4621 assert_eq!(prevs, vec![]);
4622 let pending = table_txn.pending();
4623 assert_eq!(
4624 pending,
4625 vec![(
4626 42i64.to_le_bytes().to_vec(),
4627 "v7".to_string(),
4628 Diff::MINUS_ONE
4629 ),]
4630 );
4631 commit(&mut table, pending);
4632 assert_eq!(table, BTreeMap::new());
4633 }
4634
4635 #[mz_ore::test(tokio::test)]
4636 #[cfg_attr(miri, ignore)] async fn test_savepoint() {
4638 const VERSION: Version = Version::new(26, 0, 0);
4639 let mut persist_cache = PersistClientCache::new_no_metrics();
4640 persist_cache.cfg.build_version = VERSION;
4641 let persist_client = persist_cache
4642 .open(PersistLocation::new_in_mem())
4643 .await
4644 .unwrap();
4645 let state_builder = TestCatalogStateBuilder::new(persist_client)
4646 .with_default_deploy_generation()
4647 .with_version(VERSION);
4648
4649 let _ = state_builder
4651 .clone()
4652 .unwrap_build()
4653 .await
4654 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4655 .await
4656 .unwrap();
4657 let mut savepoint_state = state_builder
4658 .unwrap_build()
4659 .await
4660 .open_savepoint(SYSTEM_TIME().into(), &test_bootstrap_args())
4661 .await
4662 .unwrap();
4663
4664 let initial_snapshot = savepoint_state.sync_to_current_updates().await.unwrap();
4665 assert!(!initial_snapshot.is_empty());
4666
4667 let db_name = "db";
4668 let db_owner = RoleId::User(42);
4669 let db_privileges = Vec::new();
4670 let mut txn = savepoint_state.transaction().await.unwrap();
4671 let (db_id, db_oid) = txn
4672 .insert_user_database(db_name, db_owner, db_privileges.clone(), &HashSet::new())
4673 .unwrap();
4674 let commit_ts = txn.upper();
4675 txn.commit_internal(commit_ts).await.unwrap();
4676 let updates = savepoint_state.sync_to_current_updates().await.unwrap();
4677 let update = updates.into_element();
4678
4679 assert_eq!(update.diff, StateDiff::Addition);
4680
4681 let db = match update.kind {
4682 memory::objects::StateUpdateKind::Database(db) => db,
4683 update => panic!("unexpected update: {update:?}"),
4684 };
4685
4686 assert_eq!(db_id, db.id);
4687 assert_eq!(db_oid, db.oid);
4688 assert_eq!(db_name, db.name);
4689 assert_eq!(db_owner, db.owner_id);
4690 assert_eq!(db_privileges, db.privileges);
4691 }
4692
4693 #[mz_ore::test(tokio::test)]
4694 #[cfg_attr(miri, ignore)] async fn test_dry_run_transaction_rejects_internal_commit() {
4696 const VERSION: Version = Version::new(26, 0, 0);
4697 let mut persist_cache = PersistClientCache::new_no_metrics();
4698 persist_cache.cfg.build_version = VERSION;
4699 let persist_client = persist_cache
4700 .open(PersistLocation::new_in_mem())
4701 .await
4702 .unwrap();
4703 let mut state = TestCatalogStateBuilder::new(persist_client)
4704 .with_default_deploy_generation()
4705 .with_version(VERSION)
4706 .unwrap_build()
4707 .await
4708 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4709 .await
4710 .unwrap();
4711 let _ = state.sync_to_current_updates().await.unwrap();
4712
4713 let initial_id = state.get_next_id(USER_ITEM_ALLOC_KEY).await.unwrap();
4714 let initial_upper = state.current_upper().await;
4715 let snapshot = state.snapshot().await.unwrap();
4716 let mut dry_run = state.transaction_from_snapshot(snapshot).unwrap();
4717 let ids = dry_run
4718 .transaction_mut()
4719 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), 1)
4720 .unwrap();
4721 assert_eq!(ids, vec![initial_id]);
4722
4723 let transaction = dry_run.transaction;
4724 let err = transaction
4725 .commit_internal(initial_upper)
4726 .await
4727 .unwrap_err();
4728 assert!(matches!(
4729 err,
4730 CatalogError::Durable(DurableCatalogError::DryRunTransaction)
4731 ));
4732 assert_eq!(state.current_upper().await, initial_upper);
4733 assert_eq!(
4734 state.get_next_id(USER_ITEM_ALLOC_KEY).await.unwrap(),
4735 initial_id
4736 );
4737 }
4738
4739 #[mz_ore::test(tokio::test)]
4740 #[cfg_attr(miri, ignore)] async fn test_dry_run_transaction_rejects_into_parts_escape() {
4742 const VERSION: Version = Version::new(26, 0, 0);
4743 let mut persist_cache = PersistClientCache::new_no_metrics();
4744 persist_cache.cfg.build_version = VERSION;
4745 let persist_client = persist_cache
4746 .open(PersistLocation::new_in_mem())
4747 .await
4748 .unwrap();
4749 let mut dry_run_state = TestCatalogStateBuilder::new(persist_client.clone())
4750 .with_default_deploy_generation()
4751 .with_version(VERSION)
4752 .unwrap_build()
4753 .await
4754 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4755 .await
4756 .unwrap();
4757 let mut replacement_state = TestCatalogStateBuilder::new(persist_client)
4758 .with_default_deploy_generation()
4759 .with_version(VERSION)
4760 .unwrap_build()
4761 .await
4762 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4763 .await
4764 .unwrap();
4765 let _ = dry_run_state.sync_to_current_updates().await.unwrap();
4766 let _ = replacement_state.sync_to_current_updates().await.unwrap();
4767
4768 let initial_id = dry_run_state
4769 .get_next_id(USER_ITEM_ALLOC_KEY)
4770 .await
4771 .unwrap();
4772 let initial_upper = dry_run_state.current_upper().await;
4773 let snapshot = dry_run_state.snapshot().await.unwrap();
4774 let mut dry_run = dry_run_state.transaction_from_snapshot(snapshot).unwrap();
4775 let ids = dry_run
4776 .transaction_mut()
4777 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), 1)
4778 .unwrap();
4779 assert_eq!(ids, vec![initial_id]);
4780
4781 let replacement = replacement_state.transaction().await.unwrap();
4782 let escaped = std::mem::replace(dry_run.transaction_mut(), replacement);
4783 drop(dry_run);
4784
4785 let err = match escaped.into_parts() {
4786 Ok(_) => panic!("dry-run transaction decomposed into committable parts"),
4787 Err(err) => err,
4788 };
4789 assert!(matches!(
4790 err,
4791 CatalogError::Durable(DurableCatalogError::DryRunTransaction)
4792 ));
4793 assert_eq!(dry_run_state.current_upper().await, initial_upper);
4794 assert_eq!(
4795 dry_run_state
4796 .get_next_id(USER_ITEM_ALLOC_KEY)
4797 .await
4798 .unwrap(),
4799 initial_id
4800 );
4801 }
4802
4803 #[mz_ore::test(tokio::test)]
4807 #[cfg_attr(miri, ignore)] async fn test_insert_replica_with_id_does_not_consume_allocator() {
4809 const VERSION: Version = Version::new(26, 0, 0);
4810 let mut persist_cache = PersistClientCache::new_no_metrics();
4811 persist_cache.cfg.build_version = VERSION;
4812 let persist_client = persist_cache
4813 .open(PersistLocation::new_in_mem())
4814 .await
4815 .unwrap();
4816 let state_builder = TestCatalogStateBuilder::new(persist_client)
4817 .with_default_deploy_generation()
4818 .with_version(VERSION);
4819 let mut state = state_builder
4820 .unwrap_build()
4821 .await
4822 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4823 .await
4824 .unwrap();
4825
4826 let cluster_id = ClusterId::User(1);
4829 let owner_id = RoleId::User(1);
4830 let config = ReplicaConfig {
4831 location: ReplicaLocation::Managed {
4832 size: "1".to_string(),
4833 availability_zones: Vec::new(),
4834 internal: false,
4835 billed_as: None,
4836 pending: false,
4837 },
4838 logging: ReplicaLogging {
4839 log_logging: false,
4840 interval: Some(Duration::from_secs(1)),
4841 },
4842 arrangement_compression: false,
4843 };
4844
4845 let commit_ts = state.current_upper().await;
4847 let a = state
4848 .allocate_user_replica_ids(1, commit_ts)
4849 .await
4850 .unwrap()
4851 .into_element();
4852 assert!(a.is_user());
4853
4854 let initial_updates = state.sync_to_current_updates().await.unwrap();
4855 assert!(!initial_updates.is_empty());
4856
4857 let mut txn = state.transaction().await.unwrap();
4859 txn.insert_cluster_replica_with_id(cluster_id, a, "explicit", config, owner_id)
4860 .unwrap();
4861 let commit_ts = txn.upper();
4862 txn.commit_internal(commit_ts).await.unwrap();
4863 let _ = state.sync_to_current_updates().await.unwrap();
4864
4865 let commit_ts = state.current_upper().await;
4867 let b = state
4868 .allocate_user_replica_ids(1, commit_ts)
4869 .await
4870 .unwrap()
4871 .into_element();
4872
4873 assert_eq!(b.inner_id(), a.inner_id() + 1);
4876
4877 let txn = state.transaction().await.unwrap();
4879 let found = txn
4880 .get_cluster_replicas()
4881 .any(|replica| replica.replica_id == a);
4882 assert!(found, "explicitly inserted replica {a} not found");
4883 }
4884
4885 #[mz_ore::test]
4886 fn test_allocate_introspection_source_index_id() {
4887 let cluster_variant: u8 = 0b0000_0001;
4888 let cluster_id_inner: u64 =
4889 0b0000_0000_1100_0101_1100_0011_1010_1101_0000_1011_1111_1001_0110_1010;
4890 let timely_messages_received_log_variant: u8 = 0b0000_1000;
4891
4892 let cluster_id = ClusterId::System(cluster_id_inner);
4893 let log_variant = LogVariant::Timely(TimelyLog::MessagesReceived);
4894
4895 let introspection_source_index_id: u64 =
4896 0b0000_0001_1100_0101_1100_0011_1010_1101_0000_1011_1111_1001_0110_1010_0000_1000;
4897
4898 {
4900 let mut cluster_variant_mask = 0xFF << 56;
4901 cluster_variant_mask &= introspection_source_index_id;
4902 cluster_variant_mask >>= 56;
4903 assert_eq!(cluster_variant_mask, u64::from(cluster_variant));
4904 }
4905
4906 {
4908 let mut cluster_id_inner_mask = 0xFFFF_FFFF_FFFF << 8;
4909 cluster_id_inner_mask &= introspection_source_index_id;
4910 cluster_id_inner_mask >>= 8;
4911 assert_eq!(cluster_id_inner_mask, cluster_id_inner);
4912 }
4913
4914 {
4916 let mut log_variant_mask = 0xFF;
4917 log_variant_mask &= introspection_source_index_id;
4918 assert_eq!(
4919 log_variant_mask,
4920 u64::from(timely_messages_received_log_variant)
4921 );
4922 }
4923
4924 let (catalog_item_id, global_id) =
4925 Transaction::allocate_introspection_source_index_id(&cluster_id, log_variant);
4926
4927 assert_eq!(
4928 catalog_item_id,
4929 CatalogItemId::IntrospectionSourceIndex(introspection_source_index_id)
4930 );
4931 assert_eq!(
4932 global_id,
4933 GlobalId::IntrospectionSourceIndex(introspection_source_index_id)
4934 );
4935 }
4936}