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_types::{ClusterId, ReplicaId};
21use mz_ore::cast::{u64_to_usize, usize_to_u64};
22use mz_ore::collections::{CollectionExt, HashSet};
23use mz_ore::now::SYSTEM_TIME;
24use mz_ore::vec::VecExt;
25use mz_ore::{soft_assert_no_log, soft_assert_or_log, soft_panic_or_log};
26use mz_persist_types::ShardId;
27use mz_pgrepr::oid::FIRST_USER_OID;
28use mz_proto::{RustType, TryFromProtoError};
29use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem};
30use mz_repr::network_policy_id::NetworkPolicyId;
31use mz_repr::role_id::RoleId;
32use mz_repr::{CatalogItemId, Diff, GlobalId, RelationVersion};
33use mz_sql::catalog::{
34 CatalogError as SqlCatalogError, CatalogItemType, ObjectType, PasswordAction,
35 RoleAttributesRaw, RoleMembership, RoleVars,
36};
37use mz_sql::names::{CommentObjectId, DatabaseId, ResolvedDatabaseSpecifier, SchemaId};
38use mz_sql::plan::NetworkPolicyRule;
39use mz_sql_parser::ast::QualifiedReplica;
40use mz_storage_client::controller::StorageTxn;
41use mz_storage_types::controller::StorageError;
42use tracing::warn;
43use uuid::Uuid;
44
45use crate::builtin::BuiltinLog;
46use crate::durable::initialize::{
47 ENABLE_0DT_DEPLOYMENT_PANIC_AFTER_TIMEOUT, SYSTEM_CONFIG_SYNCED_KEY,
48 WITH_0DT_DEPLOYMENT_DDL_CHECK_INTERVAL, WITH_0DT_DEPLOYMENT_MAX_WAIT,
49};
50use crate::durable::objects::serialization::proto;
51use crate::durable::objects::{
52 AuditLogKey, Cluster, ClusterConfig, ClusterIntrospectionSourceIndexKey,
53 ClusterIntrospectionSourceIndexValue, ClusterKey, ClusterReplica, ClusterReplicaKey,
54 ClusterReplicaValue, ClusterSystemConfiguration, ClusterSystemConfigurationKey,
55 ClusterSystemConfigurationValue, ClusterValue, CommentKey, CommentValue, Config, ConfigKey,
56 ConfigValue, Database, DatabaseKey, DatabaseValue, DefaultPrivilegesKey,
57 DefaultPrivilegesValue, DurableType, GidMappingKey, GidMappingValue, IdAllocKey, IdAllocValue,
58 IntrospectionSourceIndex, Item, ItemKey, ItemValue, NetworkPolicyKey, NetworkPolicyValue,
59 ReplicaConfig, ReplicaSystemConfiguration, ReplicaSystemConfigurationKey,
60 ReplicaSystemConfigurationValue, Role, RoleKey, RoleValue, Schema, SchemaKey, SchemaValue,
61 ServerConfigurationKey, ServerConfigurationValue, SettingKey, SettingValue, SourceReference,
62 SourceReferencesKey, SourceReferencesValue, StorageCollectionMetadataKey,
63 StorageCollectionMetadataValue, SystemObjectDescription, SystemObjectMapping,
64 SystemPrivilegesKey, SystemPrivilegesValue, TxnWalShardValue, UnfinalizedShardKey,
65};
66use crate::durable::{
67 AUDIT_LOG_ID_ALLOC_KEY, BUILTIN_MIGRATION_SHARD_KEY, CATALOG_CONTENT_VERSION_KEY, CatalogError,
68 DATABASE_ID_ALLOC_KEY, DefaultPrivilege, DurableCatalogError, DurableCatalogState,
69 EXPRESSION_CACHE_SHARD_KEY, MOCK_AUTHENTICATION_NONCE_KEY, NetworkPolicy, OID_ALLOC_KEY,
70 SCHEMA_ID_ALLOC_KEY, SYSTEM_CLUSTER_ID_ALLOC_KEY, SYSTEM_ITEM_ALLOC_KEY,
71 SYSTEM_REPLICA_ID_ALLOC_KEY, Snapshot, SystemConfiguration, USER_ITEM_ALLOC_KEY,
72 USER_NETWORK_POLICY_ID_ALLOC_KEY, USER_ROLE_ID_ALLOC_KEY,
73};
74use crate::memory::objects::{StateDiff, StateUpdate, StateUpdateKind};
75
76type Timestamp = u64;
77
78#[derive(Debug, PartialEq)]
79struct CommitCapability;
80
81#[derive(Derivative)]
84#[derivative(Debug)]
85pub struct Transaction<'a> {
86 #[derivative(Debug = "ignore")]
87 #[derivative(PartialEq = "ignore")]
88 durable_catalog: &'a mut dyn DurableCatalogState,
89 databases: TableTransaction<DatabaseKey, DatabaseValue>,
90 schemas: TableTransaction<SchemaKey, SchemaValue>,
91 items: TableTransaction<ItemKey, ItemValue>,
92 comments: TableTransaction<CommentKey, CommentValue>,
93 roles: TableTransaction<RoleKey, RoleValue>,
94 role_auth: TableTransaction<RoleAuthKey, RoleAuthValue>,
95 clusters: TableTransaction<ClusterKey, ClusterValue>,
96 cluster_replicas: TableTransaction<ClusterReplicaKey, ClusterReplicaValue>,
97 introspection_sources:
98 TableTransaction<ClusterIntrospectionSourceIndexKey, ClusterIntrospectionSourceIndexValue>,
99 id_allocator: TableTransaction<IdAllocKey, IdAllocValue>,
100 configs: TableTransaction<ConfigKey, ConfigValue>,
101 settings: TableTransaction<SettingKey, SettingValue>,
102 system_gid_mapping: TableTransaction<GidMappingKey, GidMappingValue>,
103 system_configurations: TableTransaction<ServerConfigurationKey, ServerConfigurationValue>,
104 cluster_system_configurations:
105 TableTransaction<ClusterSystemConfigurationKey, ClusterSystemConfigurationValue>,
106 replica_system_configurations:
107 TableTransaction<ReplicaSystemConfigurationKey, ReplicaSystemConfigurationValue>,
108 default_privileges: TableTransaction<DefaultPrivilegesKey, DefaultPrivilegesValue>,
109 source_references: TableTransaction<SourceReferencesKey, SourceReferencesValue>,
110 system_privileges: TableTransaction<SystemPrivilegesKey, SystemPrivilegesValue>,
111 network_policies: TableTransaction<NetworkPolicyKey, NetworkPolicyValue>,
112 storage_collection_metadata:
113 TableTransaction<StorageCollectionMetadataKey, StorageCollectionMetadataValue>,
114 unfinalized_shards: TableTransaction<UnfinalizedShardKey, ()>,
115 txn_wal_shard: TableTransaction<(), TxnWalShardValue>,
116 audit_log_updates: Vec<(AuditLogKey, Diff, Timestamp)>,
119 upper: mz_repr::Timestamp,
121 op_id: Timestamp,
123 commit_capability: Option<CommitCapability>,
127}
128
129#[derive(Debug)]
131pub struct DryRunTransaction<'a> {
132 transaction: Transaction<'a>,
133}
134
135impl<'a> DryRunTransaction<'a> {
136 pub fn new(mut transaction: Transaction<'a>) -> Self {
138 transaction.commit_capability = None;
139 Self { transaction }
140 }
141
142 pub fn transaction_mut(&mut self) -> &mut Transaction<'a> {
144 &mut self.transaction
145 }
146
147 pub fn current_snapshot(&self) -> Snapshot {
149 self.transaction.current_snapshot()
150 }
151}
152
153impl<'a> Transaction<'a> {
154 pub(super) fn new(
155 durable_catalog: &'a mut dyn DurableCatalogState,
156 Snapshot {
157 databases,
158 schemas,
159 roles,
160 role_auth,
161 items,
162 comments,
163 clusters,
164 network_policies,
165 cluster_replicas,
166 introspection_sources,
167 id_allocator,
168 configs,
169 settings,
170 source_references,
171 system_object_mappings,
172 system_configurations,
173 cluster_system_configurations,
174 replica_system_configurations,
175 default_privileges,
176 system_privileges,
177 storage_collection_metadata,
178 unfinalized_shards,
179 txn_wal_shard,
180 }: Snapshot,
181 upper: mz_repr::Timestamp,
182 ) -> Result<Transaction<'a>, CatalogError> {
183 let database_unique_fn: fn(&DatabaseValue, &DatabaseValue) -> bool =
186 |a, b| a.name == b.name;
187 let schema_unique_fn: fn(&SchemaValue, &SchemaValue) -> bool =
188 |a, b| a.database_id == b.database_id && a.name == b.name;
189 let role_key: fn(&RoleValue, &RoleValue) -> bool = |a, b| a.name == b.name;
190 let cluster_unique_fn: fn(&ClusterValue, &ClusterValue) -> bool = |a, b| a.name == b.name;
191 let network_policy_unique_fn: fn(&NetworkPolicyValue, &NetworkPolicyValue) -> bool =
192 |a, b| a.name == b.name;
193 let cluster_replica_unique_fn: fn(&ClusterReplicaValue, &ClusterReplicaValue) -> bool =
194 |a, b| a.cluster_id == b.cluster_id && a.name == b.name;
195
196 Ok(Transaction {
197 durable_catalog,
198 databases: TableTransaction::new_with_uniqueness_fn(
199 databases,
200 database_unique_fn,
201 database_unique_fn,
202 )?,
203 schemas: TableTransaction::new_with_uniqueness_fn(
204 schemas,
205 schema_unique_fn,
206 schema_unique_fn,
207 )?,
208 items: TableTransaction::new_with_uniqueness_fn(
213 items,
214 |a: &ItemValue, b| {
215 a.schema_id == b.schema_id
216 && a.name == b.name
217 && a.ephemeral_owner_session == b.ephemeral_owner_session
218 && {
219 let a_type = a.item_type();
221 let b_type = b.item_type();
222 (a_type != CatalogItemType::Type && b_type != CatalogItemType::Type)
223 || (a_type == CatalogItemType::Type && b_type.conflicts_with_type())
224 || (b_type == CatalogItemType::Type && a_type.conflicts_with_type())
225 }
226 },
227 |prev: &ItemValue, next| {
228 prev.schema_id == next.schema_id
229 && prev.name == next.name
230 && prev.ephemeral_owner_session == next.ephemeral_owner_session
231 && prev.item_type() == next.item_type()
233 },
234 )?,
235 comments: TableTransaction::new(comments)?,
236 roles: TableTransaction::new_with_uniqueness_fn(roles, role_key, role_key)?,
237 role_auth: TableTransaction::new(role_auth)?,
238 clusters: TableTransaction::new_with_uniqueness_fn(
239 clusters,
240 cluster_unique_fn,
241 cluster_unique_fn,
242 )?,
243 network_policies: TableTransaction::new_with_uniqueness_fn(
244 network_policies,
245 network_policy_unique_fn,
246 network_policy_unique_fn,
247 )?,
248 cluster_replicas: TableTransaction::new_with_uniqueness_fn(
249 cluster_replicas,
250 cluster_replica_unique_fn,
251 cluster_replica_unique_fn,
252 )?,
253 introspection_sources: TableTransaction::new(introspection_sources)?,
254 id_allocator: TableTransaction::new(id_allocator)?,
255 configs: TableTransaction::new(configs)?,
256 settings: TableTransaction::new(settings)?,
257 source_references: TableTransaction::new(source_references)?,
258 system_gid_mapping: TableTransaction::new(system_object_mappings)?,
259 system_configurations: TableTransaction::new(system_configurations)?,
260 cluster_system_configurations: TableTransaction::new(cluster_system_configurations)?,
261 replica_system_configurations: TableTransaction::new(replica_system_configurations)?,
262 default_privileges: TableTransaction::new(default_privileges)?,
263 system_privileges: TableTransaction::new(system_privileges)?,
264 storage_collection_metadata: TableTransaction::new(storage_collection_metadata)?,
265 unfinalized_shards: TableTransaction::new(unfinalized_shards)?,
266 txn_wal_shard: TableTransaction::new(txn_wal_shard)?,
270 audit_log_updates: Vec::new(),
271 upper,
272 op_id: 0,
273 commit_capability: Some(CommitCapability),
274 })
275 }
276
277 pub fn get_item(&self, id: &CatalogItemId) -> Option<Item> {
278 let key = ItemKey { id: *id };
279 self.items
280 .get(&key)
281 .map(|v| DurableType::from_key_value(key, v.clone()))
282 }
283
284 pub fn get_items(&self) -> impl Iterator<Item = Item> + use<> {
285 self.items
286 .items()
287 .into_iter()
288 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
289 .sorted_by_key(|Item { id, .. }| *id)
290 }
291
292 pub fn insert_audit_log_event(&mut self, event: VersionedEvent) {
293 self.insert_audit_log_events([event]);
294 }
295
296 pub fn insert_audit_log_events(&mut self, events: impl IntoIterator<Item = VersionedEvent>) {
297 let events = events
298 .into_iter()
299 .map(|event| (AuditLogKey { event }, Diff::ONE, self.op_id));
300 self.audit_log_updates.extend(events);
301 }
302
303 pub fn insert_user_database(
304 &mut self,
305 database_name: &str,
306 owner_id: RoleId,
307 privileges: Vec<MzAclItem>,
308 temporary_oids: &HashSet<u32>,
309 ) -> Result<(DatabaseId, u32), CatalogError> {
310 let id = self.get_and_increment_id(DATABASE_ID_ALLOC_KEY.to_string())?;
311 let id = DatabaseId::User(id);
312 let oid = self.allocate_oid(temporary_oids)?;
313 self.insert_database(id, database_name, owner_id, privileges, oid)?;
314 Ok((id, oid))
315 }
316
317 pub(crate) fn insert_database(
318 &mut self,
319 id: DatabaseId,
320 database_name: &str,
321 owner_id: RoleId,
322 privileges: Vec<MzAclItem>,
323 oid: u32,
324 ) -> Result<u32, CatalogError> {
325 match self.databases.insert(
326 DatabaseKey { id },
327 DatabaseValue {
328 name: database_name.to_string(),
329 owner_id,
330 privileges,
331 oid,
332 },
333 self.op_id,
334 ) {
335 Ok(_) => Ok(oid),
336 Err(_) => Err(SqlCatalogError::DatabaseAlreadyExists(database_name.to_owned()).into()),
337 }
338 }
339
340 pub fn insert_user_schema(
341 &mut self,
342 database_id: DatabaseId,
343 schema_name: &str,
344 owner_id: RoleId,
345 privileges: Vec<MzAclItem>,
346 temporary_oids: &HashSet<u32>,
347 ) -> Result<(SchemaId, u32), CatalogError> {
348 let id = self.get_and_increment_id(SCHEMA_ID_ALLOC_KEY.to_string())?;
349 let id = SchemaId::User(id);
350 let oid = self.allocate_oid(temporary_oids)?;
351 self.insert_schema(
352 id,
353 Some(database_id),
354 schema_name.to_string(),
355 owner_id,
356 privileges,
357 oid,
358 )?;
359 Ok((id, oid))
360 }
361
362 pub fn insert_system_schema(
363 &mut self,
364 schema_id: u64,
365 schema_name: &str,
366 owner_id: RoleId,
367 privileges: Vec<MzAclItem>,
368 oid: u32,
369 ) -> Result<(), CatalogError> {
370 let id = SchemaId::System(schema_id);
371 self.insert_schema(id, None, schema_name.to_string(), owner_id, privileges, oid)
372 }
373
374 pub(crate) fn insert_schema(
375 &mut self,
376 schema_id: SchemaId,
377 database_id: Option<DatabaseId>,
378 schema_name: String,
379 owner_id: RoleId,
380 privileges: Vec<MzAclItem>,
381 oid: u32,
382 ) -> Result<(), CatalogError> {
383 match self.schemas.insert(
384 SchemaKey { id: schema_id },
385 SchemaValue {
386 database_id,
387 name: schema_name.clone(),
388 owner_id,
389 privileges,
390 oid,
391 },
392 self.op_id,
393 ) {
394 Ok(_) => Ok(()),
395 Err(_) => Err(SqlCatalogError::SchemaAlreadyExists(schema_name).into()),
396 }
397 }
398
399 pub fn insert_builtin_role(
400 &mut self,
401 id: RoleId,
402 name: String,
403 attributes: RoleAttributesRaw,
404 membership: RoleMembership,
405 vars: RoleVars,
406 oid: u32,
407 ) -> Result<RoleId, CatalogError> {
408 soft_assert_or_log!(id.is_builtin(), "ID {id:?} is not builtin");
409 self.insert_role(id, name, attributes, membership, vars, oid)?;
410 Ok(id)
411 }
412
413 pub fn insert_user_role(
414 &mut self,
415 name: String,
416 attributes: RoleAttributesRaw,
417 membership: RoleMembership,
418 vars: RoleVars,
419 temporary_oids: &HashSet<u32>,
420 ) -> Result<(RoleId, u32), CatalogError> {
421 let id = self.get_and_increment_id(USER_ROLE_ID_ALLOC_KEY.to_string())?;
422 let id = RoleId::User(id);
423 let oid = self.allocate_oid(temporary_oids)?;
424 self.insert_role(id, name, attributes, membership, vars, oid)?;
425 Ok((id, oid))
426 }
427
428 fn insert_role(
429 &mut self,
430 id: RoleId,
431 name: String,
432 attributes: RoleAttributesRaw,
433 membership: RoleMembership,
434 vars: RoleVars,
435 oid: u32,
436 ) -> Result<(), CatalogError> {
437 if let Some(ref password) = attributes.password {
438 let hash = mz_auth::hash::scram256_hash(
439 password,
440 &attributes
441 .scram_iterations
442 .or_else(|| {
443 soft_panic_or_log!(
444 "Hash iterations must be set if a password is provided."
445 );
446 None
447 })
448 .unwrap_or_else(|| NonZeroU32::new(600_000).expect("known valid")),
451 )
452 .expect("password hash should be valid");
453 match self.role_auth.insert(
454 RoleAuthKey { role_id: id },
455 RoleAuthValue {
456 password_hash: Some(hash),
457 updated_at: SYSTEM_TIME(),
458 },
459 self.op_id,
460 ) {
461 Ok(_) => {}
462 Err(_) => {
463 return Err(SqlCatalogError::RoleAlreadyExists(name).into());
464 }
465 }
466 }
467
468 match self.roles.insert(
469 RoleKey { id },
470 RoleValue {
471 name: name.clone(),
472 attributes: attributes.into(),
473 membership,
474 vars,
475 oid,
476 },
477 self.op_id,
478 ) {
479 Ok(_) => Ok(()),
480 Err(_) => Err(SqlCatalogError::RoleAlreadyExists(name).into()),
481 }
482 }
483
484 pub fn insert_user_cluster(
486 &mut self,
487 cluster_id: ClusterId,
488 cluster_name: &str,
489 introspection_source_indexes: Vec<(&'static BuiltinLog, CatalogItemId, GlobalId)>,
490 owner_id: RoleId,
491 privileges: Vec<MzAclItem>,
492 config: ClusterConfig,
493 temporary_oids: &HashSet<u32>,
494 ) -> Result<(), CatalogError> {
495 self.insert_cluster(
496 cluster_id,
497 cluster_name,
498 introspection_source_indexes,
499 owner_id,
500 privileges,
501 config,
502 temporary_oids,
503 )
504 }
505
506 pub fn insert_system_cluster(
508 &mut self,
509 cluster_name: &str,
510 introspection_source_indexes: Vec<(&'static BuiltinLog, CatalogItemId, GlobalId)>,
511 privileges: Vec<MzAclItem>,
512 owner_id: RoleId,
513 config: ClusterConfig,
514 temporary_oids: &HashSet<u32>,
515 ) -> Result<ClusterId, CatalogError> {
516 let cluster_id = self.get_and_increment_id(SYSTEM_CLUSTER_ID_ALLOC_KEY.to_string())?;
517 let cluster_id = ClusterId::system(cluster_id).ok_or(SqlCatalogError::IdExhaustion)?;
518 self.insert_cluster(
519 cluster_id,
520 cluster_name,
521 introspection_source_indexes,
522 owner_id,
523 privileges,
524 config,
525 temporary_oids,
526 )?;
527 Ok(cluster_id)
528 }
529
530 fn insert_cluster(
531 &mut self,
532 cluster_id: ClusterId,
533 cluster_name: &str,
534 introspection_source_indexes: Vec<(&'static BuiltinLog, CatalogItemId, GlobalId)>,
535 owner_id: RoleId,
536 privileges: Vec<MzAclItem>,
537 config: ClusterConfig,
538 temporary_oids: &HashSet<u32>,
539 ) -> Result<(), CatalogError> {
540 if let Err(_) = self.clusters.insert(
541 ClusterKey { id: cluster_id },
542 ClusterValue {
543 name: cluster_name.to_string(),
544 owner_id,
545 privileges,
546 config,
547 },
548 self.op_id,
549 ) {
550 return Err(SqlCatalogError::ClusterAlreadyExists(cluster_name.to_owned()).into());
551 };
552
553 let amount = usize_to_u64(introspection_source_indexes.len());
554 let oids = self.allocate_oids(amount, temporary_oids)?;
555 let introspection_source_indexes: Vec<_> = introspection_source_indexes
556 .into_iter()
557 .zip_eq(oids)
558 .map(|((builtin, item_id, index_id), oid)| (builtin, item_id, index_id, oid))
559 .collect();
560 for (builtin, item_id, index_id, oid) in introspection_source_indexes {
561 let introspection_source_index = IntrospectionSourceIndex {
562 cluster_id,
563 name: builtin.name.to_string(),
564 item_id,
565 index_id,
566 oid,
567 };
568 let (key, value) = introspection_source_index.into_key_value();
569 self.introspection_sources
570 .insert(key, value, self.op_id)
571 .expect("no uniqueness violation");
572 }
573
574 Ok(())
575 }
576
577 pub fn rename_cluster(
578 &mut self,
579 cluster_id: ClusterId,
580 cluster_name: &str,
581 cluster_to_name: &str,
582 ) -> Result<(), CatalogError> {
583 let key = ClusterKey { id: cluster_id };
584
585 match self.clusters.update(
586 |k, v| {
587 if *k == key {
588 let mut value = v.clone();
589 value.name = cluster_to_name.to_string();
590 Some(value)
591 } else {
592 None
593 }
594 },
595 self.op_id,
596 )? {
597 Diff::ZERO => Err(SqlCatalogError::UnknownCluster(cluster_name.to_string()).into()),
598 Diff::ONE => Ok(()),
599 n => panic!(
600 "Expected to update single cluster {cluster_name} ({cluster_id}), updated {n}"
601 ),
602 }
603 }
604
605 pub fn rename_cluster_replica(
606 &mut self,
607 replica_id: ReplicaId,
608 replica_name: &QualifiedReplica,
609 replica_to_name: &str,
610 ) -> Result<(), CatalogError> {
611 let key = ClusterReplicaKey { id: replica_id };
612
613 match self.cluster_replicas.update(
614 |k, v| {
615 if *k == key {
616 let mut value = v.clone();
617 value.name = replica_to_name.to_string();
618 Some(value)
619 } else {
620 None
621 }
622 },
623 self.op_id,
624 )? {
625 Diff::ZERO => {
626 Err(SqlCatalogError::UnknownClusterReplica(replica_name.to_string()).into())
627 }
628 Diff::ONE => Ok(()),
629 n => panic!(
630 "Expected to update single cluster replica {replica_name} ({replica_id}), updated {n}"
631 ),
632 }
633 }
634
635 pub fn insert_cluster_replica_with_id(
636 &mut self,
637 cluster_id: ClusterId,
638 replica_id: ReplicaId,
639 replica_name: &str,
640 config: ReplicaConfig,
641 owner_id: RoleId,
642 ) -> Result<(), CatalogError> {
643 if let Err(_) = self.cluster_replicas.insert(
644 ClusterReplicaKey { id: replica_id },
645 ClusterReplicaValue {
646 cluster_id,
647 name: replica_name.into(),
648 config,
649 owner_id,
650 },
651 self.op_id,
652 ) {
653 let cluster = self
654 .clusters
655 .get(&ClusterKey { id: cluster_id })
656 .expect("cluster exists");
657 return Err(SqlCatalogError::DuplicateReplica(
658 replica_name.to_string(),
659 cluster.name.to_string(),
660 )
661 .into());
662 };
663 Ok(())
664 }
665
666 pub fn insert_user_network_policy(
667 &mut self,
668 name: String,
669 rules: Vec<NetworkPolicyRule>,
670 privileges: Vec<MzAclItem>,
671 owner_id: RoleId,
672 temporary_oids: &HashSet<u32>,
673 ) -> Result<NetworkPolicyId, CatalogError> {
674 let oid = self.allocate_oid(temporary_oids)?;
675 let id = self.get_and_increment_id(USER_NETWORK_POLICY_ID_ALLOC_KEY.to_string())?;
676 let id = NetworkPolicyId::User(id);
677 self.insert_network_policy(id, name, rules, privileges, owner_id, oid)
678 }
679
680 pub fn insert_network_policy(
681 &mut self,
682 id: NetworkPolicyId,
683 name: String,
684 rules: Vec<NetworkPolicyRule>,
685 privileges: Vec<MzAclItem>,
686 owner_id: RoleId,
687 oid: u32,
688 ) -> Result<NetworkPolicyId, CatalogError> {
689 match self.network_policies.insert(
690 NetworkPolicyKey { id },
691 NetworkPolicyValue {
692 name: name.clone(),
693 rules,
694 privileges,
695 owner_id,
696 oid,
697 },
698 self.op_id,
699 ) {
700 Ok(_) => Ok(id),
701 Err(_) => Err(SqlCatalogError::NetworkPolicyAlreadyExists(name).into()),
702 }
703 }
704
705 pub fn update_introspection_source_index_gids(
710 &mut self,
711 mappings: impl Iterator<
712 Item = (
713 ClusterId,
714 impl Iterator<Item = (String, CatalogItemId, GlobalId, u32)>,
715 ),
716 >,
717 ) -> Result<(), CatalogError> {
718 for (cluster_id, updates) in mappings {
719 for (name, item_id, index_id, oid) in updates {
720 let introspection_source_index = IntrospectionSourceIndex {
721 cluster_id,
722 name,
723 item_id,
724 index_id,
725 oid,
726 };
727 let (key, value) = introspection_source_index.into_key_value();
728
729 let prev = self
730 .introspection_sources
731 .set(key, Some(value), self.op_id)?;
732 if prev.is_none() {
733 return Err(SqlCatalogError::FailedBuiltinSchemaMigration(format!(
734 "{index_id}"
735 ))
736 .into());
737 }
738 }
739 }
740 Ok(())
741 }
742
743 pub fn insert_user_item(
744 &mut self,
745 id: CatalogItemId,
746 global_id: GlobalId,
747 schema_id: SchemaId,
748 item_name: &str,
749 create_sql: String,
750 owner_id: RoleId,
751 privileges: Vec<MzAclItem>,
752 temporary_oids: &HashSet<u32>,
753 versions: BTreeMap<RelationVersion, GlobalId>,
754 ephemeral_owner_session: Option<Uuid>,
755 ) -> Result<u32, CatalogError> {
756 let oid = self.allocate_oid(temporary_oids)?;
757 self.insert_item(
758 id,
759 oid,
760 global_id,
761 schema_id,
762 item_name,
763 create_sql,
764 owner_id,
765 privileges,
766 versions,
767 ephemeral_owner_session,
768 )?;
769 Ok(oid)
770 }
771
772 pub fn insert_item(
773 &mut self,
774 id: CatalogItemId,
775 oid: u32,
776 global_id: GlobalId,
777 schema_id: SchemaId,
778 item_name: &str,
779 create_sql: String,
780 owner_id: RoleId,
781 privileges: Vec<MzAclItem>,
782 extra_versions: BTreeMap<RelationVersion, GlobalId>,
783 ephemeral_owner_session: Option<Uuid>,
784 ) -> Result<(), CatalogError> {
785 match self.items.insert(
786 ItemKey { id },
787 ItemValue {
788 schema_id,
789 name: item_name.to_string(),
790 create_sql,
791 owner_id,
792 privileges,
793 oid,
794 global_id,
795 extra_versions,
796 ephemeral_owner_session,
797 },
798 self.op_id,
799 ) {
800 Ok(_) => Ok(()),
801 Err(_) => Err(SqlCatalogError::ItemAlreadyExists(id, item_name.to_owned()).into()),
802 }
803 }
804
805 pub fn remove_ephemeral_items(&mut self) {
820 let mut keys = Vec::new();
821 let mut item_ids = BTreeSet::new();
822 let mut global_ids = BTreeSet::new();
823 for (key, value) in self.items.items() {
824 if value.ephemeral_owner_session.is_none() {
825 continue;
826 }
827 item_ids.insert(key.id);
828 global_ids.insert(value.global_id);
829 global_ids.extend(value.extra_versions.values().copied());
830 keys.push(key.clone());
831 }
832 self.items.delete_by_keys(keys, self.op_id);
833
834 let dropped_mappings = self.delete_collection_metadata(global_ids);
842 let mut dropped_shards: BTreeSet<_> = dropped_mappings
843 .into_iter()
844 .map(|(_, shard)| shard)
845 .collect();
846 let live_shards: BTreeSet<_> = self.get_collection_metadata().into_values().collect();
847 dropped_shards.retain(|shard| {
848 let live = live_shards.contains(shard);
849 if live {
850 soft_panic_or_log!(
851 "shard {shard} of a reclaimed ephemeral item is still referenced by a \
852 live collection, not finalizing it"
853 );
854 }
855 !live
856 });
857 self.insert_unfinalized_shards(dropped_shards).expect(
858 "inserting unfinalized shards only fails on duplicate values, which it ignores",
859 );
860
861 self.comments.delete(
864 |key, _value| match key.object_id {
865 CommentObjectId::Table(item_id)
866 | CommentObjectId::View(item_id)
867 | CommentObjectId::MaterializedView(item_id)
868 | CommentObjectId::Source(item_id)
869 | CommentObjectId::Sink(item_id)
870 | CommentObjectId::MetricSink(item_id)
871 | CommentObjectId::Index(item_id)
872 | CommentObjectId::Func(item_id)
873 | CommentObjectId::Connection(item_id)
874 | CommentObjectId::Type(item_id)
875 | CommentObjectId::Secret(item_id) => item_ids.contains(&item_id),
876 CommentObjectId::Role(_)
877 | CommentObjectId::Database(_)
878 | CommentObjectId::Schema(_)
879 | CommentObjectId::Cluster(_)
880 | CommentObjectId::ClusterReplica(_)
881 | CommentObjectId::NetworkPolicy(_) => false,
882 },
883 self.op_id,
884 );
885
886 self.source_references
889 .delete(|key, _value| item_ids.contains(&key.source_id), self.op_id);
890 }
891
892 pub fn get_and_increment_id(&mut self, key: String) -> Result<u64, CatalogError> {
893 Ok(self.get_and_increment_id_by(key, 1)?.into_element())
894 }
895
896 pub fn get_and_increment_id_by(
897 &mut self,
898 key: String,
899 amount: u64,
900 ) -> Result<Vec<u64>, CatalogError> {
901 assert!(
902 key != SYSTEM_ITEM_ALLOC_KEY || !self.durable_catalog.is_bootstrap_complete(),
903 "system item IDs cannot be allocated outside of bootstrap"
904 );
905
906 let current_id = self
907 .id_allocator
908 .items()
909 .get(&IdAllocKey { name: key.clone() })
910 .unwrap_or_else(|| panic!("{key} id allocator missing"))
911 .next_id;
912 let next_id = current_id
913 .checked_add(amount)
914 .ok_or(SqlCatalogError::IdExhaustion)?;
915 let prev = self.id_allocator.set(
916 IdAllocKey { name: key },
917 Some(IdAllocValue { next_id }),
918 self.op_id,
919 )?;
920 assert_eq!(
921 prev,
922 Some(IdAllocValue {
923 next_id: current_id
924 })
925 );
926 Ok((current_id..next_id).collect())
927 }
928
929 pub fn allocate_system_item_ids(
930 &mut self,
931 amount: u64,
932 ) -> Result<Vec<(CatalogItemId, GlobalId)>, CatalogError> {
933 assert!(
934 !self.durable_catalog.is_bootstrap_complete(),
935 "we can only allocate system item IDs during bootstrap"
936 );
937 Ok(self
938 .get_and_increment_id_by(SYSTEM_ITEM_ALLOC_KEY.to_string(), amount)?
939 .into_iter()
940 .map(|x| (CatalogItemId::System(x), GlobalId::System(x)))
942 .collect())
943 }
944
945 pub fn allocate_introspection_source_index_id(
977 cluster_id: &ClusterId,
978 log_variant: LogVariant,
979 ) -> (CatalogItemId, GlobalId) {
980 let cluster_variant: u8 = match cluster_id {
981 ClusterId::System(_) => 1,
982 ClusterId::User(_) => 2,
983 };
984 let cluster_id: u64 = cluster_id.inner_id();
985 const CLUSTER_ID_MASK: u64 = 0xFFFF << 48;
986 assert_eq!(
987 CLUSTER_ID_MASK & cluster_id,
988 0,
989 "invalid cluster ID: {cluster_id}"
990 );
991 let log_variant: u8 = match log_variant {
992 LogVariant::Timely(TimelyLog::Operates) => 1,
993 LogVariant::Timely(TimelyLog::Channels) => 2,
994 LogVariant::Timely(TimelyLog::Elapsed) => 3,
995 LogVariant::Timely(TimelyLog::Histogram) => 4,
996 LogVariant::Timely(TimelyLog::Addresses) => 5,
997 LogVariant::Timely(TimelyLog::Parks) => 6,
998 LogVariant::Timely(TimelyLog::MessagesSent) => 7,
999 LogVariant::Timely(TimelyLog::MessagesReceived) => 8,
1000 LogVariant::Timely(TimelyLog::Reachability) => 9,
1001 LogVariant::Timely(TimelyLog::BatchesSent) => 10,
1002 LogVariant::Timely(TimelyLog::BatchesReceived) => 11,
1003 LogVariant::Differential(DifferentialLog::ArrangementBatches) => 12,
1004 LogVariant::Differential(DifferentialLog::ArrangementRecords) => 13,
1005 LogVariant::Differential(DifferentialLog::Sharing) => 14,
1006 LogVariant::Differential(DifferentialLog::BatcherRecords) => 15,
1007 LogVariant::Differential(DifferentialLog::BatcherSize) => 16,
1008 LogVariant::Differential(DifferentialLog::BatcherCapacity) => 17,
1009 LogVariant::Differential(DifferentialLog::BatcherAllocations) => 18,
1010 LogVariant::Compute(ComputeLog::DataflowCurrent) => 19,
1011 LogVariant::Compute(ComputeLog::FrontierCurrent) => 20,
1012 LogVariant::Compute(ComputeLog::PeekCurrent) => 21,
1013 LogVariant::Compute(ComputeLog::PeekDuration) => 22,
1014 LogVariant::Compute(ComputeLog::ImportFrontierCurrent) => 23,
1015 LogVariant::Compute(ComputeLog::ArrangementHeapSize) => 24,
1016 LogVariant::Compute(ComputeLog::ArrangementHeapCapacity) => 25,
1017 LogVariant::Compute(ComputeLog::ArrangementHeapAllocations) => 26,
1018 LogVariant::Compute(ComputeLog::ErrorCount) => 28,
1019 LogVariant::Compute(ComputeLog::HydrationTime) => 29,
1020 LogVariant::Compute(ComputeLog::LirMapping) => 30,
1021 LogVariant::Compute(ComputeLog::DataflowGlobal) => 31,
1022 LogVariant::Compute(ComputeLog::OperatorHydrationStatus) => 32,
1023 LogVariant::Compute(ComputeLog::PrometheusMetrics) => 33,
1024 };
1025
1026 let mut id: u64 = u64::from(cluster_variant) << 56;
1027 id |= cluster_id << 8;
1028 id |= u64::from(log_variant);
1029
1030 (
1031 CatalogItemId::IntrospectionSourceIndex(id),
1032 GlobalId::IntrospectionSourceIndex(id),
1033 )
1034 }
1035
1036 pub fn allocate_user_item_ids(
1037 &mut self,
1038 amount: u64,
1039 ) -> Result<Vec<(CatalogItemId, GlobalId)>, CatalogError> {
1040 Ok(self
1041 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), amount)?
1042 .into_iter()
1043 .map(|x| (CatalogItemId::User(x), GlobalId::User(x)))
1045 .collect())
1046 }
1047
1048 pub fn allocate_system_replica_id(&mut self) -> Result<ReplicaId, CatalogError> {
1049 let id = self.get_and_increment_id(SYSTEM_REPLICA_ID_ALLOC_KEY.to_string())?;
1050 Ok(ReplicaId::System(id))
1051 }
1052
1053 pub fn allocate_audit_log_id(&mut self) -> Result<u64, CatalogError> {
1054 self.get_and_increment_id(AUDIT_LOG_ID_ALLOC_KEY.to_string())
1055 }
1056
1057 #[mz_ore::instrument]
1060 fn allocate_oids(
1061 &mut self,
1062 amount: u64,
1063 temporary_oids: &HashSet<u32>,
1064 ) -> Result<Vec<u32>, CatalogError> {
1065 struct UserOid(u32);
1068
1069 impl UserOid {
1070 fn new(oid: u32) -> Result<UserOid, anyhow::Error> {
1071 if oid < FIRST_USER_OID {
1072 Err(anyhow!("invalid user OID {oid}"))
1073 } else {
1074 Ok(UserOid(oid))
1075 }
1076 }
1077 }
1078
1079 impl std::ops::AddAssign<u32> for UserOid {
1080 fn add_assign(&mut self, rhs: u32) {
1081 let (res, overflow) = self.0.overflowing_add(rhs);
1082 self.0 = if overflow { FIRST_USER_OID + res } else { res };
1083 }
1084 }
1085
1086 if amount > u32::MAX.into() {
1087 return Err(CatalogError::Catalog(SqlCatalogError::OidExhaustion));
1088 }
1089
1090 let mut allocated_oids = HashSet::with_capacity(
1096 self.databases.len()
1097 + self.schemas.len()
1098 + self.roles.len()
1099 + self.items.len()
1100 + self.introspection_sources.len()
1101 + temporary_oids.len(),
1102 );
1103 self.databases.for_values(|_, value| {
1104 allocated_oids.insert(value.oid);
1105 });
1106 self.schemas.for_values(|_, value| {
1107 allocated_oids.insert(value.oid);
1108 });
1109 self.roles.for_values(|_, value| {
1110 allocated_oids.insert(value.oid);
1111 });
1112 self.items.for_values(|_, value| {
1113 allocated_oids.insert(value.oid);
1114 });
1115 self.introspection_sources.for_values(|_, value| {
1116 allocated_oids.insert(value.oid);
1117 });
1118
1119 let is_allocated = |oid| allocated_oids.contains(&oid) || temporary_oids.contains(&oid);
1120
1121 let start_oid: u32 = self
1122 .id_allocator
1123 .items()
1124 .get(&IdAllocKey {
1125 name: OID_ALLOC_KEY.to_string(),
1126 })
1127 .unwrap_or_else(|| panic!("{OID_ALLOC_KEY} id allocator missing"))
1128 .next_id
1129 .try_into()
1130 .expect("we should never persist an oid outside of the u32 range");
1131 let mut current_oid = UserOid::new(start_oid)
1132 .expect("we should never persist an oid outside of user OID range");
1133 let mut oids = Vec::new();
1134 while oids.len() < u64_to_usize(amount) {
1135 if !is_allocated(current_oid.0) {
1136 oids.push(current_oid.0);
1137 }
1138 current_oid += 1;
1139
1140 if current_oid.0 == start_oid && oids.len() < u64_to_usize(amount) {
1141 return Err(CatalogError::Catalog(SqlCatalogError::OidExhaustion));
1143 }
1144 }
1145
1146 let next_id = current_oid.0;
1147 let prev = self.id_allocator.set(
1148 IdAllocKey {
1149 name: OID_ALLOC_KEY.to_string(),
1150 },
1151 Some(IdAllocValue {
1152 next_id: next_id.into(),
1153 }),
1154 self.op_id,
1155 )?;
1156 assert_eq!(
1157 prev,
1158 Some(IdAllocValue {
1159 next_id: start_oid.into(),
1160 })
1161 );
1162
1163 Ok(oids)
1164 }
1165
1166 pub fn allocate_oid(&mut self, temporary_oids: &HashSet<u32>) -> Result<u32, CatalogError> {
1169 self.allocate_oids(1, temporary_oids)
1170 .map(|oids| oids.into_element())
1171 }
1172
1173 pub fn current_snapshot(&self) -> Snapshot {
1181 Snapshot {
1182 databases: self.databases.current_items_proto(),
1183 schemas: self.schemas.current_items_proto(),
1184 roles: self.roles.current_items_proto(),
1185 role_auth: self.role_auth.current_items_proto(),
1186 items: self.items.current_items_proto(),
1187 comments: self.comments.current_items_proto(),
1188 clusters: self.clusters.current_items_proto(),
1189 network_policies: self.network_policies.current_items_proto(),
1190 cluster_replicas: self.cluster_replicas.current_items_proto(),
1191 introspection_sources: self.introspection_sources.current_items_proto(),
1192 id_allocator: self.id_allocator.current_items_proto(),
1193 configs: self.configs.current_items_proto(),
1194 settings: self.settings.current_items_proto(),
1195 system_object_mappings: self.system_gid_mapping.current_items_proto(),
1196 system_configurations: self.system_configurations.current_items_proto(),
1197 cluster_system_configurations: self.cluster_system_configurations.current_items_proto(),
1198 replica_system_configurations: self.replica_system_configurations.current_items_proto(),
1199 default_privileges: self.default_privileges.current_items_proto(),
1200 source_references: self.source_references.current_items_proto(),
1201 system_privileges: self.system_privileges.current_items_proto(),
1202 storage_collection_metadata: self.storage_collection_metadata.current_items_proto(),
1203 unfinalized_shards: self.unfinalized_shards.current_items_proto(),
1204 txn_wal_shard: self.txn_wal_shard.current_items_proto(),
1205 }
1206 }
1207
1208 pub(crate) fn insert_id_allocator(
1209 &mut self,
1210 name: String,
1211 next_id: u64,
1212 ) -> Result<(), CatalogError> {
1213 match self.id_allocator.insert(
1214 IdAllocKey { name: name.clone() },
1215 IdAllocValue { next_id },
1216 self.op_id,
1217 ) {
1218 Ok(_) => Ok(()),
1219 Err(_) => Err(SqlCatalogError::IdAllocatorAlreadyExists(name).into()),
1220 }
1221 }
1222
1223 pub fn remove_database(&mut self, id: &DatabaseId) -> Result<(), CatalogError> {
1230 let prev = self
1231 .databases
1232 .set(DatabaseKey { id: *id }, None, self.op_id)?;
1233 if prev.is_some() {
1234 Ok(())
1235 } else {
1236 Err(SqlCatalogError::UnknownDatabase(id.to_string()).into())
1237 }
1238 }
1239
1240 pub fn remove_databases(
1247 &mut self,
1248 databases: &BTreeSet<DatabaseId>,
1249 ) -> Result<(), CatalogError> {
1250 if databases.is_empty() {
1251 return Ok(());
1252 }
1253
1254 let to_remove = databases
1255 .iter()
1256 .map(|id| (DatabaseKey { id: *id }, None))
1257 .collect();
1258 let mut prev = self.databases.set_many(to_remove, self.op_id)?;
1259 prev.retain(|_k, val| val.is_none());
1260
1261 if !prev.is_empty() {
1262 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1263 return Err(SqlCatalogError::UnknownDatabase(err).into());
1264 }
1265
1266 Ok(())
1267 }
1268
1269 pub fn remove_schema(
1276 &mut self,
1277 database_id: &Option<DatabaseId>,
1278 schema_id: &SchemaId,
1279 ) -> Result<(), CatalogError> {
1280 let prev = self
1281 .schemas
1282 .set(SchemaKey { id: *schema_id }, None, self.op_id)?;
1283 if prev.is_some() {
1284 Ok(())
1285 } else {
1286 let database_name = match database_id {
1287 Some(id) => format!("{id}."),
1288 None => "".to_string(),
1289 };
1290 Err(SqlCatalogError::UnknownSchema(format!("{}.{}", database_name, schema_id)).into())
1291 }
1292 }
1293
1294 pub fn remove_schemas(
1301 &mut self,
1302 schemas: &BTreeMap<SchemaId, ResolvedDatabaseSpecifier>,
1303 ) -> Result<(), CatalogError> {
1304 if schemas.is_empty() {
1305 return Ok(());
1306 }
1307
1308 let to_remove = schemas
1309 .iter()
1310 .map(|(schema_id, _)| (SchemaKey { id: *schema_id }, None))
1311 .collect();
1312 let mut prev = self.schemas.set_many(to_remove, self.op_id)?;
1313 prev.retain(|_k, v| v.is_none());
1314
1315 if !prev.is_empty() {
1316 let err = prev
1317 .keys()
1318 .map(|k| {
1319 let db_spec = schemas.get(&k.id).expect("should_exist");
1320 let db_name = match db_spec {
1321 ResolvedDatabaseSpecifier::Id(id) => format!("{id}."),
1322 ResolvedDatabaseSpecifier::Ambient => "".to_string(),
1323 };
1324 format!("{}.{}", db_name, k.id)
1325 })
1326 .join(", ");
1327
1328 return Err(SqlCatalogError::UnknownSchema(err).into());
1329 }
1330
1331 Ok(())
1332 }
1333
1334 pub fn remove_source_references(
1335 &mut self,
1336 source_id: CatalogItemId,
1337 ) -> Result<(), CatalogError> {
1338 let deleted = self
1339 .source_references
1340 .delete_by_key(SourceReferencesKey { source_id }, self.op_id)
1341 .is_some();
1342 if deleted {
1343 Ok(())
1344 } else {
1345 Err(SqlCatalogError::UnknownItem(source_id.to_string()).into())
1346 }
1347 }
1348
1349 pub fn remove_user_roles(&mut self, roles: &BTreeSet<RoleId>) -> Result<(), CatalogError> {
1356 assert!(
1357 roles.iter().all(|id| id.is_user()),
1358 "cannot delete non-user roles"
1359 );
1360 self.remove_roles(roles)
1361 }
1362
1363 pub fn remove_roles(&mut self, roles: &BTreeSet<RoleId>) -> Result<(), CatalogError> {
1370 if roles.is_empty() {
1371 return Ok(());
1372 }
1373
1374 let to_remove_keys = roles
1375 .iter()
1376 .map(|role_id| RoleKey { id: *role_id })
1377 .collect::<Vec<_>>();
1378
1379 let to_remove_roles = to_remove_keys
1380 .iter()
1381 .map(|role_key| (role_key.clone(), None))
1382 .collect();
1383
1384 let mut prev = self.roles.set_many(to_remove_roles, self.op_id)?;
1385
1386 let to_remove_role_auth = to_remove_keys
1387 .iter()
1388 .map(|role_key| {
1389 (
1390 RoleAuthKey {
1391 role_id: role_key.id,
1392 },
1393 None,
1394 )
1395 })
1396 .collect();
1397
1398 let mut role_auth_prev = self.role_auth.set_many(to_remove_role_auth, self.op_id)?;
1399
1400 prev.retain(|_k, v| v.is_none());
1401 if !prev.is_empty() {
1402 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1403 return Err(SqlCatalogError::UnknownRole(err).into());
1404 }
1405
1406 role_auth_prev.retain(|_k, v| v.is_none());
1407 Ok(())
1411 }
1412
1413 pub fn remove_clusters(&mut self, clusters: &BTreeSet<ClusterId>) -> Result<(), CatalogError> {
1420 if clusters.is_empty() {
1421 return Ok(());
1422 }
1423
1424 let to_remove = clusters
1425 .iter()
1426 .map(|cluster_id| (ClusterKey { id: *cluster_id }, None))
1427 .collect();
1428 let mut prev = self.clusters.set_many(to_remove, self.op_id)?;
1429
1430 prev.retain(|_k, v| v.is_none());
1431 if !prev.is_empty() {
1432 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1433 return Err(SqlCatalogError::UnknownCluster(err).into());
1434 }
1435
1436 self.cluster_replicas
1442 .delete(|_k, v| clusters.contains(&v.cluster_id), self.op_id);
1443 self.introspection_sources
1444 .delete(|k, _v| clusters.contains(&k.cluster_id), self.op_id);
1445
1446 Ok(())
1447 }
1448
1449 pub fn remove_cluster_replica(&mut self, id: ReplicaId) -> Result<(), CatalogError> {
1456 let deleted = self
1457 .cluster_replicas
1458 .delete_by_key(ClusterReplicaKey { id }, self.op_id)
1459 .is_some();
1460 if deleted {
1461 Ok(())
1462 } else {
1463 Err(SqlCatalogError::UnknownClusterReplica(id.to_string()).into())
1464 }
1465 }
1466
1467 pub fn remove_cluster_replicas(
1474 &mut self,
1475 replicas: &BTreeSet<ReplicaId>,
1476 ) -> Result<(), CatalogError> {
1477 if replicas.is_empty() {
1478 return Ok(());
1479 }
1480
1481 let to_remove = replicas
1482 .iter()
1483 .map(|replica_id| (ClusterReplicaKey { id: *replica_id }, None))
1484 .collect();
1485 let mut prev = self.cluster_replicas.set_many(to_remove, self.op_id)?;
1486
1487 prev.retain(|_k, v| v.is_none());
1488 if !prev.is_empty() {
1489 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1490 return Err(SqlCatalogError::UnknownClusterReplica(err).into());
1491 }
1492
1493 Ok(())
1494 }
1495
1496 pub fn remove_item(&mut self, id: CatalogItemId) -> Result<(), CatalogError> {
1503 let prev = self.items.set(ItemKey { id }, None, self.op_id)?;
1504 if prev.is_some() {
1505 Ok(())
1506 } else {
1507 Err(SqlCatalogError::UnknownItem(id.to_string()).into())
1508 }
1509 }
1510
1511 pub fn remove_items(&mut self, ids: &BTreeSet<CatalogItemId>) -> Result<(), CatalogError> {
1518 if ids.is_empty() {
1519 return Ok(());
1520 }
1521
1522 let ks: Vec<_> = ids.clone().into_iter().map(|id| ItemKey { id }).collect();
1523 let n = self.items.delete_by_keys(ks, self.op_id).len();
1524 if n == ids.len() {
1525 Ok(())
1526 } else {
1527 let item_ids = self.items.items().keys().map(|k| k.id).collect();
1528 let mut unknown = ids.difference(&item_ids);
1529 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1530 }
1531 }
1532
1533 pub fn remove_system_object_mappings(
1540 &mut self,
1541 descriptions: BTreeSet<SystemObjectDescription>,
1542 ) -> Result<(), CatalogError> {
1543 if descriptions.is_empty() {
1544 return Ok(());
1545 }
1546
1547 let ks: Vec<_> = descriptions
1548 .clone()
1549 .into_iter()
1550 .map(|desc| GidMappingKey {
1551 schema_name: desc.schema_name,
1552 object_type: desc.object_type,
1553 object_name: desc.object_name,
1554 })
1555 .collect();
1556 let n = self.system_gid_mapping.delete_by_keys(ks, self.op_id).len();
1557
1558 if n == descriptions.len() {
1559 Ok(())
1560 } else {
1561 let item_descriptions = self
1562 .system_gid_mapping
1563 .items()
1564 .keys()
1565 .map(|k| SystemObjectDescription {
1566 schema_name: k.schema_name.clone(),
1567 object_type: k.object_type.clone(),
1568 object_name: k.object_name.clone(),
1569 })
1570 .collect();
1571 let mut unknown = descriptions.difference(&item_descriptions).map(|desc| {
1572 format!(
1573 "{} {}.{}",
1574 desc.object_type, desc.schema_name, desc.object_name
1575 )
1576 });
1577 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1578 }
1579 }
1580
1581 pub fn remove_introspection_source_indexes(
1588 &mut self,
1589 introspection_source_indexes: BTreeSet<(ClusterId, String)>,
1590 ) -> Result<(), CatalogError> {
1591 if introspection_source_indexes.is_empty() {
1592 return Ok(());
1593 }
1594
1595 let ks: Vec<_> = introspection_source_indexes
1596 .clone()
1597 .into_iter()
1598 .map(|(cluster_id, name)| ClusterIntrospectionSourceIndexKey { cluster_id, name })
1599 .collect();
1600 let n = self
1601 .introspection_sources
1602 .delete_by_keys(ks, self.op_id)
1603 .len();
1604 if n == introspection_source_indexes.len() {
1605 Ok(())
1606 } else {
1607 let txn_indexes = self
1608 .introspection_sources
1609 .items()
1610 .keys()
1611 .map(|k| (k.cluster_id, k.name.clone()))
1612 .collect();
1613 let mut unknown = introspection_source_indexes
1614 .difference(&txn_indexes)
1615 .map(|(cluster_id, name)| format!("{cluster_id} {name}"));
1616 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1617 }
1618 }
1619
1620 pub fn update_item(&mut self, id: CatalogItemId, item: Item) -> Result<(), CatalogError> {
1627 let updated =
1628 self.items
1629 .update_by_key(ItemKey { id }, item.into_key_value().1, self.op_id)?;
1630 if updated {
1631 Ok(())
1632 } else {
1633 Err(SqlCatalogError::UnknownItem(id.to_string()).into())
1634 }
1635 }
1636
1637 pub fn update_items(
1645 &mut self,
1646 items: BTreeMap<CatalogItemId, Item>,
1647 ) -> Result<(), CatalogError> {
1648 if items.is_empty() {
1649 return Ok(());
1650 }
1651
1652 let update_ids: BTreeSet<_> = items.keys().cloned().collect();
1653 let kvs: Vec<_> = items
1654 .clone()
1655 .into_iter()
1656 .map(|(id, item)| (ItemKey { id }, item.into_key_value().1))
1657 .collect();
1658 let n = self.items.update_by_keys(kvs, self.op_id)?;
1659 let n = usize::try_from(n.into_inner()).expect("Must be positive and fit in usize");
1660 if n == update_ids.len() {
1661 Ok(())
1662 } else {
1663 let item_ids: BTreeSet<_> = self.items.items().keys().map(|k| k.id).collect();
1664 let mut unknown = update_ids.difference(&item_ids);
1665 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1666 }
1667 }
1668
1669 pub fn update_role(
1677 &mut self,
1678 id: RoleId,
1679 role: Role,
1680 password: PasswordAction,
1681 ) -> Result<(), CatalogError> {
1682 let key = RoleKey { id };
1683 if self.roles.get(&key).is_some() {
1684 let auth_key = RoleAuthKey { role_id: id };
1685
1686 match password {
1687 PasswordAction::Set(new_password) => {
1688 let hash = mz_auth::hash::scram256_hash(
1689 &new_password.password,
1690 &new_password.scram_iterations,
1691 )
1692 .expect("password hash should be valid");
1693 let value = RoleAuthValue {
1694 password_hash: Some(hash),
1695 updated_at: SYSTEM_TIME(),
1696 };
1697
1698 if self.role_auth.get(&auth_key).is_some() {
1699 self.role_auth
1700 .update_by_key(auth_key.clone(), value, self.op_id)?;
1701 } else {
1702 self.role_auth.insert(auth_key.clone(), value, self.op_id)?;
1703 }
1704 }
1705 PasswordAction::Clear => {
1706 let value = RoleAuthValue {
1707 password_hash: None,
1708 updated_at: SYSTEM_TIME(),
1709 };
1710 if self.role_auth.get(&auth_key).is_some() {
1711 self.role_auth
1712 .update_by_key(auth_key.clone(), value, self.op_id)?;
1713 }
1714 }
1715 PasswordAction::NoChange => {}
1716 }
1717
1718 self.roles
1719 .update_by_key(key, role.into_key_value().1, self.op_id)?;
1720
1721 Ok(())
1722 } else {
1723 Err(SqlCatalogError::UnknownRole(id.to_string()).into())
1724 }
1725 }
1726
1727 pub fn update_roles_without_auth(
1738 &mut self,
1739 roles: BTreeMap<RoleId, Role>,
1740 ) -> Result<(), CatalogError> {
1741 if roles.is_empty() {
1742 return Ok(());
1743 }
1744
1745 let update_role_ids: BTreeSet<_> = roles.keys().cloned().collect();
1746 let kvs: Vec<_> = roles
1747 .into_iter()
1748 .map(|(id, role)| (RoleKey { id }, role.into_key_value().1))
1749 .collect();
1750 let n = self.roles.update_by_keys(kvs, self.op_id)?;
1751 let n = usize::try_from(n.into_inner()).expect("Must be positive and fit in usize");
1752
1753 if n == update_role_ids.len() {
1754 Ok(())
1755 } else {
1756 let role_ids: BTreeSet<_> = self.roles.items().keys().map(|k| k.id).collect();
1757 let mut unknown = update_role_ids.difference(&role_ids);
1758 Err(SqlCatalogError::UnknownRole(unknown.join(", ")).into())
1759 }
1760 }
1761
1762 pub fn update_system_object_mappings(
1767 &mut self,
1768 mappings: BTreeMap<CatalogItemId, SystemObjectMapping>,
1769 ) -> Result<(), CatalogError> {
1770 if mappings.is_empty() {
1771 return Ok(());
1772 }
1773
1774 let n = self.system_gid_mapping.update(
1775 |_k, v| {
1776 if let Some(mapping) = mappings.get(&CatalogItemId::from(v.catalog_id)) {
1777 let (_, new_value) = mapping.clone().into_key_value();
1778 Some(new_value)
1779 } else {
1780 None
1781 }
1782 },
1783 self.op_id,
1784 )?;
1785
1786 if usize::try_from(n.into_inner()).expect("update diff should fit into usize")
1787 != mappings.len()
1788 {
1789 let id_str = mappings.keys().map(|id| id.to_string()).join(",");
1790 return Err(SqlCatalogError::FailedBuiltinSchemaMigration(id_str).into());
1791 }
1792
1793 Ok(())
1794 }
1795
1796 pub fn update_cluster(&mut self, id: ClusterId, cluster: Cluster) -> Result<(), CatalogError> {
1803 let updated = self.clusters.update_by_key(
1804 ClusterKey { id },
1805 cluster.into_key_value().1,
1806 self.op_id,
1807 )?;
1808 if updated {
1809 Ok(())
1810 } else {
1811 Err(SqlCatalogError::UnknownCluster(id.to_string()).into())
1812 }
1813 }
1814
1815 pub fn update_cluster_replica(
1822 &mut self,
1823 replica_id: ReplicaId,
1824 replica: ClusterReplica,
1825 ) -> Result<(), CatalogError> {
1826 let updated = self.cluster_replicas.update_by_key(
1827 ClusterReplicaKey { id: replica_id },
1828 replica.into_key_value().1,
1829 self.op_id,
1830 )?;
1831 if updated {
1832 Ok(())
1833 } else {
1834 Err(SqlCatalogError::UnknownClusterReplica(replica_id.to_string()).into())
1835 }
1836 }
1837
1838 pub fn update_database(
1845 &mut self,
1846 id: DatabaseId,
1847 database: Database,
1848 ) -> Result<(), CatalogError> {
1849 let updated = self.databases.update_by_key(
1850 DatabaseKey { id },
1851 database.into_key_value().1,
1852 self.op_id,
1853 )?;
1854 if updated {
1855 Ok(())
1856 } else {
1857 Err(SqlCatalogError::UnknownDatabase(id.to_string()).into())
1858 }
1859 }
1860
1861 pub fn update_schema(
1868 &mut self,
1869 schema_id: SchemaId,
1870 schema: Schema,
1871 ) -> Result<(), CatalogError> {
1872 let updated = self.schemas.update_by_key(
1873 SchemaKey { id: schema_id },
1874 schema.into_key_value().1,
1875 self.op_id,
1876 )?;
1877 if updated {
1878 Ok(())
1879 } else {
1880 Err(SqlCatalogError::UnknownSchema(schema_id.to_string()).into())
1881 }
1882 }
1883
1884 pub fn update_network_policy(
1891 &mut self,
1892 id: NetworkPolicyId,
1893 network_policy: NetworkPolicy,
1894 ) -> Result<(), CatalogError> {
1895 let updated = self.network_policies.update_by_key(
1896 NetworkPolicyKey { id },
1897 network_policy.into_key_value().1,
1898 self.op_id,
1899 )?;
1900 if updated {
1901 Ok(())
1902 } else {
1903 Err(SqlCatalogError::UnknownNetworkPolicy(id.to_string()).into())
1904 }
1905 }
1906 pub fn remove_network_policies(
1913 &mut self,
1914 network_policies: &BTreeSet<NetworkPolicyId>,
1915 ) -> Result<(), CatalogError> {
1916 if network_policies.is_empty() {
1917 return Ok(());
1918 }
1919
1920 let to_remove = network_policies
1921 .iter()
1922 .map(|policy_id| (NetworkPolicyKey { id: *policy_id }, None))
1923 .collect();
1924 let mut prev = self.network_policies.set_many(to_remove, self.op_id)?;
1925 assert!(
1926 prev.iter().all(|(k, _)| k.id.is_user()),
1927 "cannot delete non-user network policy"
1928 );
1929
1930 prev.retain(|_k, v| v.is_none());
1931 if !prev.is_empty() {
1932 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1933 return Err(SqlCatalogError::UnknownNetworkPolicy(err).into());
1934 }
1935
1936 Ok(())
1937 }
1938 pub fn set_default_privilege(
1942 &mut self,
1943 role_id: RoleId,
1944 database_id: Option<DatabaseId>,
1945 schema_id: Option<SchemaId>,
1946 object_type: ObjectType,
1947 grantee: RoleId,
1948 privileges: Option<AclMode>,
1949 ) -> Result<(), CatalogError> {
1950 self.default_privileges.set(
1951 DefaultPrivilegesKey {
1952 role_id,
1953 database_id,
1954 schema_id,
1955 object_type,
1956 grantee,
1957 },
1958 privileges.map(|privileges| DefaultPrivilegesValue { privileges }),
1959 self.op_id,
1960 )?;
1961 Ok(())
1962 }
1963
1964 pub fn set_default_privileges(
1966 &mut self,
1967 default_privileges: Vec<DefaultPrivilege>,
1968 ) -> Result<(), CatalogError> {
1969 if default_privileges.is_empty() {
1970 return Ok(());
1971 }
1972
1973 let default_privileges = default_privileges
1974 .into_iter()
1975 .map(DurableType::into_key_value)
1976 .map(|(k, v)| (k, Some(v)))
1977 .collect();
1978 self.default_privileges
1979 .set_many(default_privileges, self.op_id)?;
1980 Ok(())
1981 }
1982
1983 pub fn set_system_privilege(
1987 &mut self,
1988 grantee: RoleId,
1989 grantor: RoleId,
1990 acl_mode: Option<AclMode>,
1991 ) -> Result<(), CatalogError> {
1992 self.system_privileges.set(
1993 SystemPrivilegesKey { grantee, grantor },
1994 acl_mode.map(|acl_mode| SystemPrivilegesValue { acl_mode }),
1995 self.op_id,
1996 )?;
1997 Ok(())
1998 }
1999
2000 pub fn set_system_privileges(
2002 &mut self,
2003 system_privileges: Vec<MzAclItem>,
2004 ) -> Result<(), CatalogError> {
2005 if system_privileges.is_empty() {
2006 return Ok(());
2007 }
2008
2009 let system_privileges = system_privileges
2010 .into_iter()
2011 .map(DurableType::into_key_value)
2012 .map(|(k, v)| (k, Some(v)))
2013 .collect();
2014 self.system_privileges
2015 .set_many(system_privileges, self.op_id)?;
2016 Ok(())
2017 }
2018
2019 pub fn set_setting(&mut self, name: String, value: Option<String>) -> Result<(), CatalogError> {
2021 self.settings.set(
2022 SettingKey { name },
2023 value.map(|value| SettingValue { value }),
2024 self.op_id,
2025 )?;
2026 Ok(())
2027 }
2028
2029 pub fn set_catalog_content_version(&mut self, version: String) -> Result<(), CatalogError> {
2030 self.set_setting(CATALOG_CONTENT_VERSION_KEY.to_string(), Some(version))
2031 }
2032
2033 pub fn insert_introspection_source_indexes(
2035 &mut self,
2036 introspection_source_indexes: Vec<(ClusterId, String, CatalogItemId, GlobalId)>,
2037 temporary_oids: &HashSet<u32>,
2038 ) -> Result<(), CatalogError> {
2039 if introspection_source_indexes.is_empty() {
2040 return Ok(());
2041 }
2042
2043 let amount = usize_to_u64(introspection_source_indexes.len());
2044 let oids = self.allocate_oids(amount, temporary_oids)?;
2045 let introspection_source_indexes: Vec<_> = introspection_source_indexes
2046 .into_iter()
2047 .zip_eq(oids)
2048 .map(
2049 |((cluster_id, name, item_id, index_id), oid)| IntrospectionSourceIndex {
2050 cluster_id,
2051 name,
2052 item_id,
2053 index_id,
2054 oid,
2055 },
2056 )
2057 .collect();
2058
2059 for introspection_source_index in introspection_source_indexes {
2060 let (key, value) = introspection_source_index.into_key_value();
2061 self.introspection_sources.insert(key, value, self.op_id)?;
2062 }
2063
2064 Ok(())
2065 }
2066
2067 pub fn set_system_object_mappings(
2069 &mut self,
2070 mappings: Vec<SystemObjectMapping>,
2071 ) -> Result<(), CatalogError> {
2072 if mappings.is_empty() {
2073 return Ok(());
2074 }
2075
2076 let mappings = mappings
2077 .into_iter()
2078 .map(DurableType::into_key_value)
2079 .map(|(k, v)| (k, Some(v)))
2080 .collect();
2081 self.system_gid_mapping.set_many(mappings, self.op_id)?;
2082 Ok(())
2083 }
2084
2085 pub fn set_replicas(&mut self, replicas: Vec<ClusterReplica>) -> Result<(), CatalogError> {
2087 if replicas.is_empty() {
2088 return Ok(());
2089 }
2090
2091 let replicas = replicas
2092 .into_iter()
2093 .map(DurableType::into_key_value)
2094 .map(|(k, v)| (k, Some(v)))
2095 .collect();
2096 self.cluster_replicas.set_many(replicas, self.op_id)?;
2097 Ok(())
2098 }
2099
2100 pub fn set_config(&mut self, key: String, value: Option<u64>) -> Result<(), CatalogError> {
2102 match value {
2103 Some(value) => {
2104 let config = Config { key, value };
2105 let (key, value) = config.into_key_value();
2106 self.configs.set(key, Some(value), self.op_id)?;
2107 }
2108 None => {
2109 self.configs.set(ConfigKey { key }, None, self.op_id)?;
2110 }
2111 }
2112 Ok(())
2113 }
2114
2115 pub fn get_config(&self, key: String) -> Option<u64> {
2117 self.configs
2118 .get(&ConfigKey { key })
2119 .map(|entry| entry.value)
2120 }
2121
2122 pub fn get_setting(&self, name: String) -> Option<&str> {
2124 self.settings
2125 .get(&SettingKey { name })
2126 .map(|entry| &*entry.value)
2127 }
2128
2129 pub fn get_builtin_migration_shard(&self) -> Option<ShardId> {
2130 self.get_setting(BUILTIN_MIGRATION_SHARD_KEY.to_string())
2131 .map(|shard_id| shard_id.parse().expect("valid ShardId"))
2132 }
2133
2134 pub fn set_builtin_migration_shard(&mut self, shard_id: ShardId) -> Result<(), CatalogError> {
2135 self.set_setting(
2136 BUILTIN_MIGRATION_SHARD_KEY.to_string(),
2137 Some(shard_id.to_string()),
2138 )
2139 }
2140
2141 pub fn get_expression_cache_shard(&self) -> Option<ShardId> {
2142 self.get_setting(EXPRESSION_CACHE_SHARD_KEY.to_string())
2143 .map(|shard_id| shard_id.parse().expect("valid ShardId"))
2144 }
2145
2146 pub fn set_expression_cache_shard(&mut self, shard_id: ShardId) -> Result<(), CatalogError> {
2147 self.set_setting(
2148 EXPRESSION_CACHE_SHARD_KEY.to_string(),
2149 Some(shard_id.to_string()),
2150 )
2151 }
2152
2153 pub fn set_0dt_deployment_max_wait(&mut self, value: Duration) -> Result<(), CatalogError> {
2159 self.set_config(
2160 WITH_0DT_DEPLOYMENT_MAX_WAIT.into(),
2161 Some(
2162 value
2163 .as_millis()
2164 .try_into()
2165 .expect("max wait fits into u64"),
2166 ),
2167 )
2168 }
2169
2170 pub fn set_0dt_deployment_ddl_check_interval(
2177 &mut self,
2178 value: Duration,
2179 ) -> Result<(), CatalogError> {
2180 self.set_config(
2181 WITH_0DT_DEPLOYMENT_DDL_CHECK_INTERVAL.into(),
2182 Some(
2183 value
2184 .as_millis()
2185 .try_into()
2186 .expect("ddl check interval fits into u64"),
2187 ),
2188 )
2189 }
2190
2191 pub fn set_enable_0dt_deployment_panic_after_timeout(
2197 &mut self,
2198 value: bool,
2199 ) -> Result<(), CatalogError> {
2200 self.set_config(
2201 ENABLE_0DT_DEPLOYMENT_PANIC_AFTER_TIMEOUT.into(),
2202 Some(u64::from(value)),
2203 )
2204 }
2205
2206 pub fn reset_0dt_deployment_max_wait(&mut self) -> Result<(), CatalogError> {
2212 self.set_config(WITH_0DT_DEPLOYMENT_MAX_WAIT.into(), None)
2213 }
2214
2215 pub fn reset_0dt_deployment_ddl_check_interval(&mut self) -> Result<(), CatalogError> {
2222 self.set_config(WITH_0DT_DEPLOYMENT_DDL_CHECK_INTERVAL.into(), None)
2223 }
2224
2225 pub fn reset_enable_0dt_deployment_panic_after_timeout(&mut self) -> Result<(), CatalogError> {
2232 self.set_config(ENABLE_0DT_DEPLOYMENT_PANIC_AFTER_TIMEOUT.into(), None)
2233 }
2234
2235 pub fn set_system_config_synced_once(&mut self) -> Result<(), CatalogError> {
2237 self.set_config(SYSTEM_CONFIG_SYNCED_KEY.into(), Some(1))
2238 }
2239
2240 pub fn update_comment(
2241 &mut self,
2242 object_id: CommentObjectId,
2243 sub_component: Option<usize>,
2244 comment: Option<String>,
2245 ) -> Result<(), CatalogError> {
2246 let key = CommentKey {
2247 object_id,
2248 sub_component,
2249 };
2250 let value = comment.map(|c| CommentValue { comment: c });
2251 self.comments.set(key, value, self.op_id)?;
2252
2253 Ok(())
2254 }
2255
2256 pub fn drop_comments(
2257 &mut self,
2258 object_ids: &BTreeSet<CommentObjectId>,
2259 ) -> Result<(), CatalogError> {
2260 if object_ids.is_empty() {
2261 return Ok(());
2262 }
2263
2264 self.comments
2265 .delete(|k, _v| object_ids.contains(&k.object_id), self.op_id);
2266 Ok(())
2267 }
2268
2269 pub fn update_source_references(
2270 &mut self,
2271 source_id: CatalogItemId,
2272 references: Vec<SourceReference>,
2273 updated_at: u64,
2274 ) -> Result<(), CatalogError> {
2275 let key = SourceReferencesKey { source_id };
2276 let value = SourceReferencesValue {
2277 references,
2278 updated_at,
2279 };
2280 self.source_references.set(key, Some(value), self.op_id)?;
2281 Ok(())
2282 }
2283
2284 pub fn upsert_system_config(&mut self, name: &str, value: String) -> Result<(), CatalogError> {
2286 let key = ServerConfigurationKey {
2287 name: name.to_string(),
2288 };
2289 let value = ServerConfigurationValue { value };
2290 self.system_configurations
2291 .set(key, Some(value), self.op_id)?;
2292 Ok(())
2293 }
2294
2295 pub fn remove_system_config(&mut self, name: &str) {
2297 let key = ServerConfigurationKey {
2298 name: name.to_string(),
2299 };
2300 self.system_configurations
2301 .set(key, None, self.op_id)
2302 .expect("cannot have uniqueness violation");
2303 }
2304
2305 pub fn clear_system_configs(&mut self) {
2307 self.system_configurations.delete(|_k, _v| true, self.op_id);
2308 }
2309
2310 pub fn get_cluster_system_configurations(
2312 &self,
2313 ) -> impl Iterator<Item = ClusterSystemConfiguration> + use<'_> {
2314 self.cluster_system_configurations
2315 .items()
2316 .into_iter()
2317 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2318 }
2319
2320 pub fn upsert_cluster_system_config(
2323 &mut self,
2324 cluster_id: ClusterId,
2325 name: &str,
2326 value: String,
2327 ) -> Result<(), CatalogError> {
2328 let key = ClusterSystemConfigurationKey {
2329 cluster_id,
2330 name: name.to_string(),
2331 };
2332 let value = ClusterSystemConfigurationValue { value };
2333 self.cluster_system_configurations
2334 .set(key, Some(value), self.op_id)?;
2335 Ok(())
2336 }
2337
2338 pub fn remove_cluster_system_config(&mut self, cluster_id: ClusterId, name: &str) {
2341 let key = ClusterSystemConfigurationKey {
2342 cluster_id,
2343 name: name.to_string(),
2344 };
2345 self.cluster_system_configurations
2346 .set(key, None, self.op_id)
2347 .expect("cannot have uniqueness violation");
2348 }
2349
2350 pub fn get_replica_system_configurations(
2352 &self,
2353 ) -> impl Iterator<Item = ReplicaSystemConfiguration> + use<'_> {
2354 self.replica_system_configurations
2355 .items()
2356 .into_iter()
2357 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2358 }
2359
2360 pub fn upsert_replica_system_config(
2363 &mut self,
2364 replica_id: ReplicaId,
2365 name: &str,
2366 value: String,
2367 ) -> Result<(), CatalogError> {
2368 let key = ReplicaSystemConfigurationKey {
2369 replica_id,
2370 name: name.to_string(),
2371 };
2372 let value = ReplicaSystemConfigurationValue { value };
2373 self.replica_system_configurations
2374 .set(key, Some(value), self.op_id)?;
2375 Ok(())
2376 }
2377
2378 pub fn remove_replica_system_config(&mut self, replica_id: ReplicaId, name: &str) {
2381 let key = ReplicaSystemConfigurationKey {
2382 replica_id,
2383 name: name.to_string(),
2384 };
2385 self.replica_system_configurations
2386 .set(key, None, self.op_id)
2387 .expect("cannot have uniqueness violation");
2388 }
2389
2390 pub(crate) fn insert_config(&mut self, key: String, value: u64) -> Result<(), CatalogError> {
2391 match self.configs.insert(
2392 ConfigKey { key: key.clone() },
2393 ConfigValue { value },
2394 self.op_id,
2395 ) {
2396 Ok(_) => Ok(()),
2397 Err(_) => Err(SqlCatalogError::ConfigAlreadyExists(key).into()),
2398 }
2399 }
2400
2401 pub fn get_clusters(&self) -> impl Iterator<Item = Cluster> + use<'_> {
2402 self.clusters
2403 .items()
2404 .into_iter()
2405 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2406 }
2407
2408 pub fn get_cluster_replicas(&self) -> impl Iterator<Item = ClusterReplica> + use<'_> {
2409 self.cluster_replicas
2410 .items()
2411 .into_iter()
2412 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2413 }
2414
2415 pub fn get_databases(&self) -> impl Iterator<Item = Database> + use<'_> {
2416 self.databases
2417 .items()
2418 .into_iter()
2419 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2420 }
2421
2422 pub fn get_roles(&self) -> impl Iterator<Item = Role> + use<'_> {
2423 self.roles
2424 .items()
2425 .into_iter()
2426 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2427 }
2428
2429 pub fn get_network_policies(&self) -> impl Iterator<Item = NetworkPolicy> + use<'_> {
2430 self.network_policies
2431 .items()
2432 .into_iter()
2433 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2434 }
2435
2436 pub fn get_system_object_mappings(
2437 &self,
2438 ) -> impl Iterator<Item = SystemObjectMapping> + use<'_> {
2439 self.system_gid_mapping
2440 .items()
2441 .into_iter()
2442 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2443 }
2444
2445 pub fn get_schemas(&self) -> impl Iterator<Item = Schema> + use<'_> {
2446 self.schemas
2447 .items()
2448 .into_iter()
2449 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2450 }
2451
2452 pub fn get_system_configurations(&self) -> impl Iterator<Item = SystemConfiguration> + use<'_> {
2453 self.system_configurations
2454 .items()
2455 .into_iter()
2456 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2457 }
2458
2459 pub fn get_schema(&self, id: &SchemaId) -> Option<Schema> {
2460 let key = SchemaKey { id: *id };
2461 self.schemas
2462 .get(&key)
2463 .map(|v| DurableType::from_key_value(key, v.clone()))
2464 }
2465
2466 pub fn get_introspection_source_indexes(
2467 &self,
2468 cluster_id: ClusterId,
2469 ) -> BTreeMap<&str, (GlobalId, u32)> {
2470 self.introspection_sources
2471 .items()
2472 .into_iter()
2473 .filter(|(k, _v)| k.cluster_id == cluster_id)
2474 .map(|(k, v)| (k.name.as_str(), (v.global_id.into(), v.oid)))
2475 .collect()
2476 }
2477
2478 pub fn get_catalog_content_version(&self) -> Option<&str> {
2479 self.settings
2480 .get(&SettingKey {
2481 name: CATALOG_CONTENT_VERSION_KEY.to_string(),
2482 })
2483 .map(|value| &*value.value)
2484 }
2485
2486 pub fn get_authentication_mock_nonce(&self) -> Option<String> {
2487 self.settings
2488 .get(&SettingKey {
2489 name: MOCK_AUTHENTICATION_NONCE_KEY.to_string(),
2490 })
2491 .map(|value| value.value.clone())
2492 }
2493
2494 #[must_use]
2500 pub fn get_and_commit_op_updates(&mut self) -> Vec<StateUpdate> {
2501 let updates = self.get_op_updates();
2502 self.commit_op();
2503 updates
2504 }
2505
2506 fn get_op_updates(&self) -> Vec<StateUpdate> {
2507 fn get_collection_op_updates<'a, T>(
2508 table_txn: &'a TableTransaction<T::Key, T::Value>,
2509 kind_fn: impl Fn(T) -> StateUpdateKind + 'a,
2510 op: Timestamp,
2511 ) -> impl Iterator<Item = (StateUpdateKind, StateDiff)> + 'a
2512 where
2513 T::Key: Ord + Eq + Clone + Debug,
2514 T::Value: Ord + Clone + Debug,
2515 T: DurableType,
2516 {
2517 table_txn
2518 .pending
2519 .iter()
2520 .flat_map(|(k, vs)| vs.into_iter().map(move |v| (k, v)))
2521 .filter_map(move |(k, v)| {
2522 if v.ts == op {
2523 let key = k.clone();
2524 let value = v.value.clone();
2525 let diff = v.diff.clone().try_into().expect("invalid diff");
2526 let update = DurableType::from_key_value(key, value);
2527 let kind = kind_fn(update);
2528 Some((kind, diff))
2529 } else {
2530 None
2531 }
2532 })
2533 }
2534
2535 fn get_large_collection_op_updates<'a, T>(
2536 collection: &'a Vec<(T::Key, Diff, Timestamp)>,
2537 kind_fn: impl Fn(T) -> StateUpdateKind + 'a,
2538 op: Timestamp,
2539 ) -> impl Iterator<Item = (StateUpdateKind, StateDiff)> + 'a
2540 where
2541 T::Key: Ord + Eq + Clone + Debug,
2542 T: DurableType<Value = ()>,
2543 {
2544 collection.iter().filter_map(move |(k, diff, ts)| {
2545 if *ts == op {
2546 let key = k.clone();
2547 let diff = diff.clone().try_into().expect("invalid diff");
2548 let update = DurableType::from_key_value(key, ());
2549 let kind = kind_fn(update);
2550 Some((kind, diff))
2551 } else {
2552 None
2553 }
2554 })
2555 }
2556
2557 let Transaction {
2558 durable_catalog: _,
2559 databases,
2560 schemas,
2561 items,
2562 comments,
2563 roles,
2564 role_auth,
2565 clusters,
2566 network_policies,
2567 cluster_replicas,
2568 introspection_sources,
2569 system_gid_mapping,
2570 system_configurations,
2571 cluster_system_configurations,
2572 replica_system_configurations,
2573 default_privileges,
2574 source_references,
2575 system_privileges,
2576 audit_log_updates,
2577 storage_collection_metadata,
2578 unfinalized_shards,
2579 id_allocator: _,
2581 configs: _,
2582 settings: _,
2583 txn_wal_shard: _,
2584 upper,
2585 op_id: _,
2586 commit_capability: _,
2587 } = &self;
2588
2589 let updates = std::iter::empty()
2590 .chain(get_collection_op_updates(
2591 roles,
2592 StateUpdateKind::Role,
2593 self.op_id,
2594 ))
2595 .chain(get_collection_op_updates(
2596 role_auth,
2597 StateUpdateKind::RoleAuth,
2598 self.op_id,
2599 ))
2600 .chain(get_collection_op_updates(
2601 databases,
2602 StateUpdateKind::Database,
2603 self.op_id,
2604 ))
2605 .chain(get_collection_op_updates(
2606 schemas,
2607 StateUpdateKind::Schema,
2608 self.op_id,
2609 ))
2610 .chain(get_collection_op_updates(
2611 default_privileges,
2612 StateUpdateKind::DefaultPrivilege,
2613 self.op_id,
2614 ))
2615 .chain(get_collection_op_updates(
2616 system_privileges,
2617 StateUpdateKind::SystemPrivilege,
2618 self.op_id,
2619 ))
2620 .chain(get_collection_op_updates(
2621 system_configurations,
2622 StateUpdateKind::SystemConfiguration,
2623 self.op_id,
2624 ))
2625 .chain(get_collection_op_updates(
2626 cluster_system_configurations,
2627 StateUpdateKind::ClusterSystemConfiguration,
2628 self.op_id,
2629 ))
2630 .chain(get_collection_op_updates(
2631 replica_system_configurations,
2632 StateUpdateKind::ReplicaSystemConfiguration,
2633 self.op_id,
2634 ))
2635 .chain(get_collection_op_updates(
2636 clusters,
2637 StateUpdateKind::Cluster,
2638 self.op_id,
2639 ))
2640 .chain(get_collection_op_updates(
2641 network_policies,
2642 StateUpdateKind::NetworkPolicy,
2643 self.op_id,
2644 ))
2645 .chain(get_collection_op_updates(
2646 introspection_sources,
2647 StateUpdateKind::IntrospectionSourceIndex,
2648 self.op_id,
2649 ))
2650 .chain(get_collection_op_updates(
2651 cluster_replicas,
2652 StateUpdateKind::ClusterReplica,
2653 self.op_id,
2654 ))
2655 .chain(get_collection_op_updates(
2656 system_gid_mapping,
2657 StateUpdateKind::SystemObjectMapping,
2658 self.op_id,
2659 ))
2660 .chain(get_collection_op_updates(
2661 items,
2662 StateUpdateKind::Item,
2663 self.op_id,
2664 ))
2665 .chain(get_collection_op_updates(
2666 comments,
2667 StateUpdateKind::Comment,
2668 self.op_id,
2669 ))
2670 .chain(get_collection_op_updates(
2671 source_references,
2672 StateUpdateKind::SourceReferences,
2673 self.op_id,
2674 ))
2675 .chain(get_collection_op_updates(
2676 storage_collection_metadata,
2677 StateUpdateKind::StorageCollectionMetadata,
2678 self.op_id,
2679 ))
2680 .chain(get_collection_op_updates(
2681 unfinalized_shards,
2682 StateUpdateKind::UnfinalizedShard,
2683 self.op_id,
2684 ))
2685 .chain(get_large_collection_op_updates(
2686 audit_log_updates,
2687 StateUpdateKind::AuditLog,
2688 self.op_id,
2689 ))
2690 .map(|(kind, diff)| StateUpdate {
2691 kind,
2692 ts: upper.clone(),
2693 diff,
2694 })
2695 .collect();
2696
2697 updates
2698 }
2699
2700 pub fn is_savepoint(&self) -> bool {
2701 self.durable_catalog.is_savepoint()
2702 }
2703
2704 fn commit_op(&mut self) {
2705 self.op_id += 1;
2706 }
2707
2708 pub fn op_id(&self) -> Timestamp {
2709 self.op_id
2710 }
2711
2712 pub fn upper(&self) -> mz_repr::Timestamp {
2713 self.upper
2714 }
2715
2716 fn ensure_committable(&self) -> Result<(), CatalogError> {
2717 match self.commit_capability {
2718 Some(_) => Ok(()),
2719 None => Err(DurableCatalogError::DryRunTransaction.into()),
2720 }
2721 }
2722
2723 pub(super) async fn ensure_not_out_of_sync(&mut self) -> Result<(), CatalogError> {
2725 self.durable_catalog
2726 .ensure_not_out_of_sync(self.upper)
2727 .await
2728 }
2729
2730 pub(crate) fn into_parts(
2731 self,
2732 ) -> Result<(TransactionBatch, &'a mut dyn DurableCatalogState), CatalogError> {
2733 let commit_capability = self
2734 .commit_capability
2735 .ok_or(DurableCatalogError::DryRunTransaction)?;
2736 let audit_log_updates = self
2737 .audit_log_updates
2738 .into_iter()
2739 .map(|(k, diff, _op)| (k.into_proto(), (), diff))
2740 .collect();
2741
2742 let txn_batch = TransactionBatch {
2743 databases: self.databases.pending(),
2744 schemas: self.schemas.pending(),
2745 items: self.items.pending(),
2746 comments: self.comments.pending(),
2747 roles: self.roles.pending(),
2748 role_auth: self.role_auth.pending(),
2749 clusters: self.clusters.pending(),
2750 cluster_replicas: self.cluster_replicas.pending(),
2751 network_policies: self.network_policies.pending(),
2752 introspection_sources: self.introspection_sources.pending(),
2753 id_allocator: self.id_allocator.pending(),
2754 configs: self.configs.pending(),
2755 source_references: self.source_references.pending(),
2756 settings: self.settings.pending(),
2757 system_gid_mapping: self.system_gid_mapping.pending(),
2758 system_configurations: self.system_configurations.pending(),
2759 cluster_system_configurations: self.cluster_system_configurations.pending(),
2760 replica_system_configurations: self.replica_system_configurations.pending(),
2761 default_privileges: self.default_privileges.pending(),
2762 system_privileges: self.system_privileges.pending(),
2763 storage_collection_metadata: self.storage_collection_metadata.pending(),
2764 unfinalized_shards: self.unfinalized_shards.pending(),
2765 txn_wal_shard: self.txn_wal_shard.pending(),
2766 audit_log_updates,
2767 upper: self.upper,
2768 _commit_capability: commit_capability,
2769 };
2770 Ok((txn_batch, self.durable_catalog))
2771 }
2772
2773 #[mz_ore::instrument(level = "debug")]
2789 pub(crate) async fn commit_internal(
2790 self,
2791 commit_ts: mz_repr::Timestamp,
2792 ) -> Result<(&'a mut dyn DurableCatalogState, mz_repr::Timestamp), CatalogError> {
2793 self.ensure_committable()?;
2794 let (mut txn_batch, durable_catalog) = self.into_parts()?;
2795 let TransactionBatch {
2796 databases,
2797 schemas,
2798 items,
2799 comments,
2800 roles,
2801 role_auth,
2802 clusters,
2803 cluster_replicas,
2804 network_policies,
2805 introspection_sources,
2806 id_allocator,
2807 configs,
2808 source_references,
2809 settings,
2810 system_gid_mapping,
2811 system_configurations,
2812 cluster_system_configurations,
2813 replica_system_configurations,
2814 default_privileges,
2815 system_privileges,
2816 storage_collection_metadata,
2817 unfinalized_shards,
2818 txn_wal_shard,
2819 audit_log_updates,
2820 upper: _,
2821 _commit_capability: _,
2822 } = &mut txn_batch;
2823 differential_dataflow::consolidation::consolidate_updates(databases);
2826 differential_dataflow::consolidation::consolidate_updates(schemas);
2827 differential_dataflow::consolidation::consolidate_updates(items);
2828 differential_dataflow::consolidation::consolidate_updates(comments);
2829 differential_dataflow::consolidation::consolidate_updates(roles);
2830 differential_dataflow::consolidation::consolidate_updates(role_auth);
2831 differential_dataflow::consolidation::consolidate_updates(clusters);
2832 differential_dataflow::consolidation::consolidate_updates(cluster_replicas);
2833 differential_dataflow::consolidation::consolidate_updates(network_policies);
2834 differential_dataflow::consolidation::consolidate_updates(introspection_sources);
2835 differential_dataflow::consolidation::consolidate_updates(id_allocator);
2836 differential_dataflow::consolidation::consolidate_updates(configs);
2837 differential_dataflow::consolidation::consolidate_updates(settings);
2838 differential_dataflow::consolidation::consolidate_updates(source_references);
2839 differential_dataflow::consolidation::consolidate_updates(system_gid_mapping);
2840 differential_dataflow::consolidation::consolidate_updates(system_configurations);
2841 differential_dataflow::consolidation::consolidate_updates(cluster_system_configurations);
2842 differential_dataflow::consolidation::consolidate_updates(replica_system_configurations);
2843 differential_dataflow::consolidation::consolidate_updates(default_privileges);
2844 differential_dataflow::consolidation::consolidate_updates(system_privileges);
2845 differential_dataflow::consolidation::consolidate_updates(storage_collection_metadata);
2846 differential_dataflow::consolidation::consolidate_updates(unfinalized_shards);
2847 differential_dataflow::consolidation::consolidate_updates(txn_wal_shard);
2848 differential_dataflow::consolidation::consolidate_updates(audit_log_updates);
2849
2850 let upper = durable_catalog
2851 .commit_transaction(txn_batch, commit_ts)
2852 .await?;
2853 Ok((durable_catalog, upper))
2854 }
2855
2856 #[mz_ore::instrument(level = "debug")]
2877 pub async fn commit(self, commit_ts: mz_repr::Timestamp) -> Result<(), CatalogError> {
2878 self.ensure_committable()?;
2879 let op_updates = self.get_op_updates();
2880 assert!(
2881 op_updates.is_empty(),
2882 "unconsumed transaction updates: {op_updates:?}"
2883 );
2884
2885 let (durable_storage, upper) = self.commit_internal(commit_ts).await?;
2886 let updates = durable_storage.sync_updates(upper).await?;
2888 soft_assert_no_log!(
2897 durable_storage.is_read_only()
2898 || updates
2899 .iter()
2900 .all(|update| update.ts >= commit_ts && update.ts < upper),
2901 "unconsumed updates existed before transaction commit: commit_ts={commit_ts:?}, upper={upper:?}, updates:{updates:?}"
2902 );
2903 Ok(())
2904 }
2905}
2906
2907use crate::durable::async_trait;
2908
2909use super::objects::{RoleAuthKey, RoleAuthValue};
2910
2911#[async_trait]
2912impl StorageTxn for Transaction<'_> {
2913 fn get_collection_metadata(&self) -> BTreeMap<GlobalId, ShardId> {
2914 self.storage_collection_metadata
2915 .items()
2916 .into_iter()
2917 .map(
2918 |(
2919 StorageCollectionMetadataKey { id },
2920 StorageCollectionMetadataValue { shard },
2921 )| { (*id, shard.clone()) },
2922 )
2923 .collect()
2924 }
2925
2926 fn insert_collection_metadata(
2927 &mut self,
2928 metadata: BTreeMap<GlobalId, ShardId>,
2929 ) -> Result<(), StorageError> {
2930 for (id, shard) in metadata {
2931 self.storage_collection_metadata
2932 .insert(
2933 StorageCollectionMetadataKey { id },
2934 StorageCollectionMetadataValue {
2935 shard: shard.clone(),
2936 },
2937 self.op_id,
2938 )
2939 .map_err(|err| match err {
2940 DurableCatalogError::DuplicateKey => {
2941 StorageError::CollectionMetadataAlreadyExists(id)
2942 }
2943 DurableCatalogError::UniquenessViolation => {
2944 StorageError::PersistShardAlreadyInUse(shard)
2945 }
2946 err => StorageError::Generic(anyhow::anyhow!(err)),
2947 })?;
2948 }
2949 Ok(())
2950 }
2951
2952 fn delete_collection_metadata(&mut self, ids: BTreeSet<GlobalId>) -> Vec<(GlobalId, ShardId)> {
2953 let ks: Vec<_> = ids
2954 .into_iter()
2955 .map(|id| StorageCollectionMetadataKey { id })
2956 .collect();
2957 self.storage_collection_metadata
2958 .delete_by_keys(ks, self.op_id)
2959 .into_iter()
2960 .map(
2961 |(
2962 StorageCollectionMetadataKey { id },
2963 StorageCollectionMetadataValue { shard },
2964 )| (id, shard),
2965 )
2966 .collect()
2967 }
2968
2969 fn get_unfinalized_shards(&self) -> BTreeSet<ShardId> {
2970 self.unfinalized_shards
2971 .items()
2972 .into_iter()
2973 .map(|(UnfinalizedShardKey { shard }, ())| *shard)
2974 .collect()
2975 }
2976
2977 fn insert_unfinalized_shards(&mut self, s: BTreeSet<ShardId>) -> Result<(), StorageError> {
2978 for shard in s {
2979 match self
2980 .unfinalized_shards
2981 .insert(UnfinalizedShardKey { shard }, (), self.op_id)
2982 {
2983 Ok(()) | Err(DurableCatalogError::DuplicateKey) => {}
2985 Err(e) => Err(StorageError::Generic(anyhow::anyhow!(e)))?,
2986 };
2987 }
2988 Ok(())
2989 }
2990
2991 fn remove_unfinalized_shards(&mut self, shards: BTreeSet<ShardId>) {
2992 let ks: Vec<_> = shards
2993 .into_iter()
2994 .map(|shard| UnfinalizedShardKey { shard })
2995 .collect();
2996 let _ = self.unfinalized_shards.delete_by_keys(ks, self.op_id);
2997 }
2998
2999 fn get_txn_wal_shard(&self) -> Option<ShardId> {
3000 self.txn_wal_shard
3001 .values()
3002 .iter()
3003 .next()
3004 .map(|TxnWalShardValue { shard }| *shard)
3005 }
3006
3007 fn write_txn_wal_shard(&mut self, shard: ShardId) -> Result<(), StorageError> {
3008 self.txn_wal_shard
3009 .insert((), TxnWalShardValue { shard }, self.op_id)
3010 .map_err(|err| match err {
3011 DurableCatalogError::DuplicateKey => StorageError::TxnWalShardAlreadyExists,
3012 err => StorageError::Generic(anyhow::anyhow!(err)),
3013 })
3014 }
3015}
3016
3017#[derive(Debug, PartialEq)]
3019pub struct TransactionBatch {
3020 pub(crate) databases: Vec<(proto::DatabaseKey, proto::DatabaseValue, Diff)>,
3021 pub(crate) schemas: Vec<(proto::SchemaKey, proto::SchemaValue, Diff)>,
3022 pub(crate) items: Vec<(proto::ItemKey, proto::ItemValue, Diff)>,
3023 pub(crate) comments: Vec<(proto::CommentKey, proto::CommentValue, Diff)>,
3024 pub(crate) roles: Vec<(proto::RoleKey, proto::RoleValue, Diff)>,
3025 pub(crate) role_auth: Vec<(proto::RoleAuthKey, proto::RoleAuthValue, Diff)>,
3026 pub(crate) clusters: Vec<(proto::ClusterKey, proto::ClusterValue, Diff)>,
3027 pub(crate) cluster_replicas: Vec<(proto::ClusterReplicaKey, proto::ClusterReplicaValue, Diff)>,
3028 pub(crate) network_policies: Vec<(proto::NetworkPolicyKey, proto::NetworkPolicyValue, Diff)>,
3029 pub(crate) introspection_sources: Vec<(
3030 proto::ClusterIntrospectionSourceIndexKey,
3031 proto::ClusterIntrospectionSourceIndexValue,
3032 Diff,
3033 )>,
3034 pub(crate) id_allocator: Vec<(proto::IdAllocKey, proto::IdAllocValue, Diff)>,
3035 pub(crate) configs: Vec<(proto::ConfigKey, proto::ConfigValue, Diff)>,
3036 pub(crate) settings: Vec<(proto::SettingKey, proto::SettingValue, Diff)>,
3037 pub(crate) system_gid_mapping: Vec<(proto::GidMappingKey, proto::GidMappingValue, Diff)>,
3038 pub(crate) system_configurations: Vec<(
3039 proto::ServerConfigurationKey,
3040 proto::ServerConfigurationValue,
3041 Diff,
3042 )>,
3043 pub(crate) cluster_system_configurations: Vec<(
3044 proto::ClusterSystemConfigurationKey,
3045 proto::ClusterSystemConfigurationValue,
3046 Diff,
3047 )>,
3048 pub(crate) replica_system_configurations: Vec<(
3049 proto::ReplicaSystemConfigurationKey,
3050 proto::ReplicaSystemConfigurationValue,
3051 Diff,
3052 )>,
3053 pub(crate) default_privileges: Vec<(
3054 proto::DefaultPrivilegesKey,
3055 proto::DefaultPrivilegesValue,
3056 Diff,
3057 )>,
3058 pub(crate) source_references: Vec<(
3059 proto::SourceReferencesKey,
3060 proto::SourceReferencesValue,
3061 Diff,
3062 )>,
3063 pub(crate) system_privileges: Vec<(
3064 proto::SystemPrivilegesKey,
3065 proto::SystemPrivilegesValue,
3066 Diff,
3067 )>,
3068 pub(crate) storage_collection_metadata: Vec<(
3069 proto::StorageCollectionMetadataKey,
3070 proto::StorageCollectionMetadataValue,
3071 Diff,
3072 )>,
3073 pub(crate) unfinalized_shards: Vec<(proto::UnfinalizedShardKey, (), Diff)>,
3074 pub(crate) txn_wal_shard: Vec<((), proto::TxnWalShardValue, Diff)>,
3075 pub(crate) audit_log_updates: Vec<(proto::AuditLogKey, (), Diff)>,
3076 pub(crate) upper: mz_repr::Timestamp,
3078 _commit_capability: CommitCapability,
3081}
3082
3083impl TransactionBatch {
3084 pub fn is_empty(&self) -> bool {
3085 let TransactionBatch {
3086 databases,
3087 schemas,
3088 items,
3089 comments,
3090 roles,
3091 role_auth,
3092 clusters,
3093 cluster_replicas,
3094 network_policies,
3095 introspection_sources,
3096 id_allocator,
3097 configs,
3098 settings,
3099 source_references,
3100 system_gid_mapping,
3101 system_configurations,
3102 cluster_system_configurations,
3103 replica_system_configurations,
3104 default_privileges,
3105 system_privileges,
3106 storage_collection_metadata,
3107 unfinalized_shards,
3108 txn_wal_shard,
3109 audit_log_updates,
3110 upper: _,
3111 _commit_capability: _,
3112 } = self;
3113 databases.is_empty()
3114 && schemas.is_empty()
3115 && items.is_empty()
3116 && comments.is_empty()
3117 && roles.is_empty()
3118 && role_auth.is_empty()
3119 && clusters.is_empty()
3120 && cluster_replicas.is_empty()
3121 && network_policies.is_empty()
3122 && introspection_sources.is_empty()
3123 && id_allocator.is_empty()
3124 && configs.is_empty()
3125 && settings.is_empty()
3126 && source_references.is_empty()
3127 && system_gid_mapping.is_empty()
3128 && system_configurations.is_empty()
3129 && cluster_system_configurations.is_empty()
3130 && replica_system_configurations.is_empty()
3131 && default_privileges.is_empty()
3132 && system_privileges.is_empty()
3133 && storage_collection_metadata.is_empty()
3134 && unfinalized_shards.is_empty()
3135 && txn_wal_shard.is_empty()
3136 && audit_log_updates.is_empty()
3137 }
3138}
3139
3140#[derive(Debug, Clone, PartialEq, Eq)]
3141struct TransactionUpdate<V> {
3142 value: V,
3143 ts: Timestamp,
3144 diff: Diff,
3145}
3146
3147trait UniqueName {
3149 const HAS_UNIQUE_NAME: bool;
3152 fn unique_name(&self) -> &str;
3154}
3155
3156mod unique_name {
3157 use crate::durable::objects::*;
3158
3159 macro_rules! impl_unique_name {
3160 ($($t:ty),* $(,)?) => {
3161 $(
3162 impl crate::durable::transaction::UniqueName for $t {
3163 const HAS_UNIQUE_NAME: bool = true;
3164 fn unique_name(&self) -> &str {
3165 &self.name
3166 }
3167 }
3168 )*
3169 };
3170 }
3171
3172 macro_rules! impl_no_unique_name {
3173 ($($t:ty),* $(,)?) => {
3174 $(
3175 impl crate::durable::transaction::UniqueName for $t {
3176 const HAS_UNIQUE_NAME: bool = false;
3177 fn unique_name(&self) -> &str {
3178 ""
3179 }
3180 }
3181 )*
3182 };
3183 }
3184
3185 impl_unique_name! {
3186 ClusterReplicaValue,
3187 ClusterValue,
3188 DatabaseValue,
3189 ItemValue,
3190 NetworkPolicyValue,
3191 RoleValue,
3192 SchemaValue,
3193 }
3194
3195 impl_no_unique_name!(
3196 (),
3197 ClusterIntrospectionSourceIndexValue,
3198 ClusterSystemConfigurationValue,
3199 CommentValue,
3200 ConfigValue,
3201 DefaultPrivilegesValue,
3202 GidMappingValue,
3203 IdAllocValue,
3204 ReplicaSystemConfigurationValue,
3205 ServerConfigurationValue,
3206 SettingValue,
3207 SourceReferencesValue,
3208 StorageCollectionMetadataValue,
3209 SystemPrivilegesValue,
3210 TxnWalShardValue,
3211 RoleAuthValue,
3212 );
3213
3214 #[cfg(test)]
3215 mod test {
3216 impl_no_unique_name!(String,);
3217 }
3218}
3219
3220#[derive(Debug)]
3228struct UniquenessCheck<V> {
3229 violation: fn(a: &V, b: &V) -> bool,
3230 is_unique_key_unchanged_after_update: fn(prev: &V, next: &V) -> bool,
3231}
3232
3233#[derive(Debug)]
3243struct TableTransaction<K, V> {
3244 initial: BTreeMap<K, V>,
3245 pending: BTreeMap<K, Vec<TransactionUpdate<V>>>,
3248 uniqueness_check: Option<UniquenessCheck<V>>,
3250}
3251
3252impl<K, V> TableTransaction<K, V>
3253where
3254 K: Ord + Eq + Clone + Debug,
3255 V: Ord + Clone + Debug + UniqueName,
3256{
3257 fn new<KP, VP>(initial: BTreeMap<KP, VP>) -> Result<Self, TryFromProtoError>
3264 where
3265 K: RustType<KP>,
3266 V: RustType<VP>,
3267 {
3268 let initial = initial
3269 .into_iter()
3270 .map(RustType::from_proto)
3271 .collect::<Result<_, _>>()?;
3272
3273 Ok(Self {
3274 initial,
3275 pending: BTreeMap::new(),
3276 uniqueness_check: None,
3277 })
3278 }
3279
3280 fn new_with_uniqueness_fn<KP, VP>(
3283 initial: BTreeMap<KP, VP>,
3284 uniqueness_violation: fn(a: &V, b: &V) -> bool,
3285 is_unique_key_unchanged_after_update: fn(prev: &V, next: &V) -> bool,
3286 ) -> Result<Self, TryFromProtoError>
3287 where
3288 K: RustType<KP>,
3289 V: RustType<VP>,
3290 {
3291 let initial = initial
3292 .into_iter()
3293 .map(RustType::from_proto)
3294 .collect::<Result<_, _>>()?;
3295
3296 Ok(Self {
3297 initial,
3298 pending: BTreeMap::new(),
3299 uniqueness_check: Some(UniquenessCheck {
3300 violation: uniqueness_violation,
3301 is_unique_key_unchanged_after_update,
3302 }),
3303 })
3304 }
3305
3306 fn pending<KP, VP>(self) -> Vec<(KP, VP, Diff)>
3309 where
3310 K: RustType<KP>,
3311 V: RustType<VP>,
3312 {
3313 soft_assert_no_log!(self.verify().is_ok());
3314 self.pending
3317 .into_iter()
3318 .flat_map(|(k, v)| {
3319 let mut v: Vec<_> = v
3320 .into_iter()
3321 .map(|TransactionUpdate { value, ts: _, diff }| (value, diff))
3322 .collect();
3323 differential_dataflow::consolidation::consolidate(&mut v);
3324 v.into_iter().map(move |(v, diff)| (k.clone(), v, diff))
3325 })
3326 .map(|(key, val, diff)| (key.into_proto(), val.into_proto(), diff))
3327 .collect()
3328 }
3329
3330 fn verify(&self) -> Result<(), DurableCatalogError> {
3335 if let Some(check) = &self.uniqueness_check {
3336 let items = self.values();
3338 if V::HAS_UNIQUE_NAME {
3339 let by_name: BTreeMap<_, _> = items
3340 .iter()
3341 .enumerate()
3342 .map(|(v, vi)| (vi.unique_name(), (v, vi)))
3343 .collect();
3344 for (i, vi) in items.iter().enumerate() {
3345 if let Some((j, vj)) = by_name.get(vi.unique_name()) {
3346 if i != *j && (check.violation)(vi, *vj) {
3347 return Err(DurableCatalogError::UniquenessViolation);
3348 }
3349 }
3350 }
3351 } else {
3352 for (i, vi) in items.iter().enumerate() {
3353 for (j, vj) in items.iter().enumerate() {
3354 if i != j && (check.violation)(vi, vj) {
3355 return Err(DurableCatalogError::UniquenessViolation);
3356 }
3357 }
3358 }
3359 }
3360 }
3361 soft_assert_no_log!(
3362 self.pending
3363 .values()
3364 .all(|pending| { pending.is_sorted_by(|a, b| a.ts <= b.ts) }),
3365 "pending should be sorted by timestamp: {:?}",
3366 self.pending
3367 );
3368 Ok(())
3369 }
3370
3371 fn verify_keys<'a>(
3376 &self,
3377 keys: impl IntoIterator<Item = &'a K>,
3378 ) -> Result<(), DurableCatalogError>
3379 where
3380 K: 'a,
3381 {
3382 if let Some(check) = &self.uniqueness_check {
3383 let entries: Vec<_> = keys
3384 .into_iter()
3385 .filter_map(|key| self.get(key).map(|value| (key, value)))
3386 .collect();
3387 for (ki, vi) in self.items() {
3389 for (kj, vj) in &entries {
3390 if ki != *kj && (check.violation)(vi, vj) {
3391 return Err(DurableCatalogError::UniquenessViolation);
3392 }
3393 }
3394 }
3395 }
3396 soft_assert_no_log!(self.verify().is_ok());
3397 Ok(())
3398 }
3399
3400 fn for_values<'a, F: FnMut(&'a K, &'a V)>(&'a self, mut f: F) {
3403 let mut seen = BTreeSet::new();
3404 for k in self.pending.keys() {
3405 seen.insert(k);
3406 let v = self.get(k);
3407 if let Some(v) = v {
3410 f(k, v);
3411 }
3412 }
3413 for (k, v) in self.initial.iter() {
3414 if !seen.contains(k) {
3416 f(k, v);
3417 }
3418 }
3419 }
3420
3421 fn get(&self, k: &K) -> Option<&V> {
3423 let pending = self.pending.get(k).map(Vec::as_slice).unwrap_or_default();
3424 let mut updates = Vec::with_capacity(pending.len() + 1);
3425 if let Some(initial) = self.initial.get(k) {
3426 updates.push((initial, Diff::ONE));
3427 }
3428 updates.extend(
3429 pending
3430 .into_iter()
3431 .map(|TransactionUpdate { value, ts: _, diff }| (value, *diff)),
3432 );
3433
3434 differential_dataflow::consolidation::consolidate(&mut updates);
3435 assert!(updates.len() <= 1);
3436 updates.into_iter().next().map(|(v, _)| v)
3437 }
3438
3439 #[cfg(test)]
3444 fn items_cloned(&self) -> BTreeMap<K, V> {
3445 let mut items = BTreeMap::new();
3446 self.for_values(|k, v| {
3447 items.insert(k.clone(), v.clone());
3448 });
3449 items
3450 }
3451
3452 fn current_items_proto<KP, VP>(&self) -> BTreeMap<KP, VP>
3456 where
3457 K: RustType<KP>,
3458 V: RustType<VP>,
3459 KP: Ord,
3460 {
3461 let mut items = BTreeMap::new();
3462 self.for_values(|k, v| {
3463 items.insert(k.into_proto(), v.into_proto());
3464 });
3465 items
3466 }
3467
3468 fn items(&self) -> BTreeMap<&K, &V> {
3471 let mut items = BTreeMap::new();
3472 self.for_values(|k, v| {
3473 items.insert(k, v);
3474 });
3475 items
3476 }
3477
3478 fn values(&self) -> BTreeSet<&V> {
3480 let mut items = BTreeSet::new();
3481 self.for_values(|_, v| {
3482 items.insert(v);
3483 });
3484 items
3485 }
3486
3487 fn len(&self) -> usize {
3489 let mut count = 0;
3490 self.for_values(|_, _| {
3491 count += 1;
3492 });
3493 count
3494 }
3495
3496 fn for_values_mut<F: FnMut(&mut BTreeMap<K, Vec<TransactionUpdate<V>>>, &K, &V)>(
3500 &mut self,
3501 mut f: F,
3502 ) {
3503 let mut pending = BTreeMap::new();
3504 self.for_values(|k, v| f(&mut pending, k, v));
3505 for (k, updates) in pending {
3506 self.pending.entry(k).or_default().extend(updates);
3507 }
3508 }
3509
3510 fn insert(&mut self, k: K, v: V, ts: Timestamp) -> Result<(), DurableCatalogError> {
3514 let mut violation = None;
3515 let uniqueness_violation = self.uniqueness_check.as_ref().map(|check| check.violation);
3516 self.for_values(|for_k, for_v| {
3517 if &k == for_k {
3518 violation = Some(DurableCatalogError::DuplicateKey);
3519 }
3520 if let Some(uniqueness_violation) = uniqueness_violation {
3521 if uniqueness_violation(for_v, &v) {
3522 violation = Some(DurableCatalogError::UniquenessViolation);
3523 }
3524 }
3525 });
3526 if let Some(violation) = violation {
3527 return Err(violation);
3528 }
3529 self.pending.entry(k).or_default().push(TransactionUpdate {
3530 value: v,
3531 ts,
3532 diff: Diff::ONE,
3533 });
3534 soft_assert_no_log!(self.verify().is_ok());
3535 Ok(())
3536 }
3537
3538 fn update<F: Fn(&K, &V) -> Option<V>>(
3547 &mut self,
3548 f: F,
3549 ts: Timestamp,
3550 ) -> Result<Diff, DurableCatalogError> {
3551 let mut changed = Diff::ZERO;
3552 let mut keys = BTreeSet::new();
3553 let pending = self.pending.clone();
3555 self.for_values_mut(|p, k, v| {
3556 if let Some(next) = f(k, v) {
3557 changed += Diff::ONE;
3558 keys.insert(k.clone());
3559 let updates = p.entry(k.clone()).or_default();
3560 updates.push(TransactionUpdate {
3561 value: v.clone(),
3562 ts,
3563 diff: Diff::MINUS_ONE,
3564 });
3565 updates.push(TransactionUpdate {
3566 value: next,
3567 ts,
3568 diff: Diff::ONE,
3569 });
3570 }
3571 });
3572 if let Err(err) = self.verify_keys(&keys) {
3574 self.pending = pending;
3575 Err(err)
3576 } else {
3577 Ok(changed)
3578 }
3579 }
3580
3581 fn update_by_key(&mut self, k: K, v: V, ts: Timestamp) -> Result<bool, DurableCatalogError> {
3586 if let Some(cur_v) = self.get(&k) {
3587 if v != *cur_v {
3588 self.set(k, Some(v), ts)?;
3589 }
3590 Ok(true)
3591 } else {
3592 Ok(false)
3593 }
3594 }
3595
3596 fn update_by_keys(
3601 &mut self,
3602 kvs: impl IntoIterator<Item = (K, V)>,
3603 ts: Timestamp,
3604 ) -> Result<Diff, DurableCatalogError> {
3605 let kvs: Vec<_> = kvs
3606 .into_iter()
3607 .filter_map(|(k, v)| match self.get(&k) {
3608 Some(cur_v) => Some((*cur_v == v, k, v)),
3610 None => None,
3611 })
3612 .collect();
3613 let changed = kvs.len();
3614 let changed =
3615 Diff::try_from(changed).map_err(|e| DurableCatalogError::Internal(e.to_string()))?;
3616 let kvs = kvs
3617 .into_iter()
3618 .filter(|(no_op, _, _)| !no_op)
3620 .map(|(_, k, v)| (k, Some(v)))
3621 .collect();
3622 self.set_many(kvs, ts)?;
3623 Ok(changed)
3624 }
3625
3626 fn update_needs_uniqueness_check(&self, prev: Option<&V>, next: Option<&V>) -> bool {
3628 match (&self.uniqueness_check, prev, next) {
3629 (None, _, _) | (_, _, None) => false,
3631 (Some(check), Some(prev), Some(next)) => {
3633 !(check.is_unique_key_unchanged_after_update)(prev, next)
3634 }
3635 (Some(_), None, Some(_)) => true,
3637 }
3638 }
3639
3640 fn set(&mut self, k: K, v: Option<V>, ts: Timestamp) -> Result<Option<V>, DurableCatalogError> {
3647 let prev = self.get(&k).cloned();
3648 let needs_uniqueness_check = self.update_needs_uniqueness_check(prev.as_ref(), v.as_ref());
3649 let entry = self.pending.entry(k.clone()).or_default();
3650 let restore_len = entry.len();
3651
3652 match (v, prev.clone()) {
3653 (Some(v), Some(prev)) => {
3654 entry.push(TransactionUpdate {
3655 value: prev,
3656 ts,
3657 diff: Diff::MINUS_ONE,
3658 });
3659 entry.push(TransactionUpdate {
3660 value: v,
3661 ts,
3662 diff: Diff::ONE,
3663 });
3664 }
3665 (Some(v), None) => {
3666 entry.push(TransactionUpdate {
3667 value: v,
3668 ts,
3669 diff: Diff::ONE,
3670 });
3671 }
3672 (None, Some(prev)) => {
3673 entry.push(TransactionUpdate {
3674 value: prev,
3675 ts,
3676 diff: Diff::MINUS_ONE,
3677 });
3678 }
3679 (None, None) => {}
3680 }
3681
3682 if needs_uniqueness_check {
3684 if let Err(err) = self.verify_keys([&k]) {
3685 let pending = self.pending.get_mut(&k).expect("inserted above");
3688 pending.truncate(restore_len);
3689 return Err(err);
3690 }
3691 }
3692 Ok(prev)
3693 }
3694
3695 fn set_many(
3700 &mut self,
3701 kvs: BTreeMap<K, Option<V>>,
3702 ts: Timestamp,
3703 ) -> Result<BTreeMap<K, Option<V>>, DurableCatalogError> {
3704 if kvs.is_empty() {
3705 return Ok(BTreeMap::new());
3706 }
3707
3708 let mut prevs = BTreeMap::new();
3709 let mut restores = BTreeMap::new();
3710 let mut keys_to_verify_uniqueness = Vec::new();
3712
3713 for (k, v) in kvs {
3714 let prev = self.get(&k).cloned();
3715 if self.update_needs_uniqueness_check(prev.as_ref(), v.as_ref()) {
3716 keys_to_verify_uniqueness.push(k.clone());
3717 }
3718 let entry = self.pending.entry(k.clone()).or_default();
3719 restores.insert(k.clone(), entry.len());
3720
3721 match (v, prev.clone()) {
3722 (Some(v), Some(prev)) => {
3723 entry.push(TransactionUpdate {
3724 value: prev,
3725 ts,
3726 diff: Diff::MINUS_ONE,
3727 });
3728 entry.push(TransactionUpdate {
3729 value: v,
3730 ts,
3731 diff: Diff::ONE,
3732 });
3733 }
3734 (Some(v), None) => {
3735 entry.push(TransactionUpdate {
3736 value: v,
3737 ts,
3738 diff: Diff::ONE,
3739 });
3740 }
3741 (None, Some(prev)) => {
3742 entry.push(TransactionUpdate {
3743 value: prev,
3744 ts,
3745 diff: Diff::MINUS_ONE,
3746 });
3747 }
3748 (None, None) => {}
3749 }
3750
3751 prevs.insert(k, prev);
3752 }
3753
3754 if let Err(err) = self.verify_keys(keys_to_verify_uniqueness.iter()) {
3756 for (k, restore_len) in restores {
3757 let pending = self.pending.get_mut(&k).expect("inserted above");
3760 pending.truncate(restore_len);
3761 }
3762 Err(err)
3763 } else {
3764 Ok(prevs)
3765 }
3766 }
3767
3768 fn delete<F: Fn(&K, &V) -> bool>(&mut self, f: F, ts: Timestamp) -> Vec<(K, V)> {
3774 let mut deleted = Vec::new();
3775 self.for_values_mut(|p, k, v| {
3776 if f(k, v) {
3777 deleted.push((k.clone(), v.clone()));
3778 p.entry(k.clone()).or_default().push(TransactionUpdate {
3779 value: v.clone(),
3780 ts,
3781 diff: Diff::MINUS_ONE,
3782 });
3783 }
3784 });
3785 soft_assert_no_log!(self.verify().is_ok());
3786 deleted
3787 }
3788
3789 fn delete_by_key(&mut self, k: K, ts: Timestamp) -> Option<V> {
3793 self.set(k, None, ts)
3794 .expect("deleting an entry cannot violate uniqueness")
3795 }
3796
3797 fn delete_by_keys(&mut self, ks: impl IntoIterator<Item = K>, ts: Timestamp) -> Vec<(K, V)> {
3801 let kvs = ks.into_iter().map(|k| (k, None)).collect();
3802 let prevs = self
3803 .set_many(kvs, ts)
3804 .expect("deleting entries cannot violate uniqueness");
3805 prevs
3806 .into_iter()
3807 .filter_map(|(k, v)| v.map(|v| (k, v)))
3808 .collect()
3809 }
3810}
3811
3812#[cfg(test)]
3813#[allow(clippy::unwrap_used)]
3814mod tests {
3815 use super::*;
3816
3817 use mz_controller::clusters::ReplicaLogging;
3818 use mz_ore::now::SYSTEM_TIME;
3819 use mz_ore::{assert_none, assert_ok};
3820 use mz_persist_client::cache::PersistClientCache;
3821 use mz_persist_types::PersistLocation;
3822 use semver::Version;
3823
3824 use crate::durable::{
3825 ReplicaConfig, ReplicaLocation, TestCatalogStateBuilder, test_bootstrap_args,
3826 };
3827 use crate::memory;
3828
3829 #[mz_ore::test]
3830 fn test_table_transaction_simple() {
3831 fn uniqueness_violation(a: &String, b: &String) -> bool {
3832 a == b
3833 }
3834 let mut table = TableTransaction::new_with_uniqueness_fn(
3835 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "a".to_string())]),
3836 uniqueness_violation,
3837 uniqueness_violation,
3838 )
3839 .unwrap();
3840
3841 assert_ok!(table.insert(2i64.to_le_bytes().to_vec(), "b".to_string(), 0));
3844 assert_ok!(table.insert(3i64.to_le_bytes().to_vec(), "c".to_string(), 0));
3845 assert!(
3846 table
3847 .insert(1i64.to_le_bytes().to_vec(), "c".to_string(), 0)
3848 .is_err()
3849 );
3850 assert!(
3851 table
3852 .insert(4i64.to_le_bytes().to_vec(), "c".to_string(), 0)
3853 .is_err()
3854 );
3855 }
3856
3857 #[mz_ore::test]
3858 fn test_skip_scan_when_unique_key_unchanged() {
3859 fn first_char_same(prev: &String, next: &String) -> bool {
3860 prev.chars().next() == next.chars().next()
3861 }
3862
3863 fn panic_uniqueness_violation(_: &String, _: &String) -> bool {
3865 panic!("uniqueness scan ran for an update that kept the same unique key");
3866 }
3867 let mut table = TableTransaction::new_with_uniqueness_fn(
3868 BTreeMap::from([
3869 (1i64.to_le_bytes().to_vec(), "a1".to_string()),
3870 (2i64.to_le_bytes().to_vec(), "b1".to_string()),
3871 ]),
3872 panic_uniqueness_violation,
3873 first_char_same,
3875 )
3876 .unwrap();
3877 assert!(
3880 table
3881 .update_by_key(1i64.to_le_bytes().to_vec(), "a2".to_string(), 0)
3882 .unwrap()
3883 );
3884
3885 fn real_uniqueness_violation(a: &String, b: &String) -> bool {
3887 a.chars().next() == b.chars().next()
3888 }
3889 let mut table = TableTransaction::new_with_uniqueness_fn(
3890 BTreeMap::from([
3891 (1i64.to_le_bytes().to_vec(), "a1".to_string()),
3892 (2i64.to_le_bytes().to_vec(), "b1".to_string()),
3893 ]),
3894 real_uniqueness_violation,
3895 first_char_same,
3896 )
3897 .unwrap();
3898 assert!(
3901 table
3902 .update_by_key(1i64.to_le_bytes().to_vec(), "b2".to_string(), 0)
3903 .is_err()
3904 );
3905 }
3906
3907 #[mz_ore::test]
3908 fn test_table_transaction() {
3909 fn uniqueness_violation(a: &String, b: &String) -> bool {
3910 a == b
3911 }
3912 let mut table: BTreeMap<Vec<u8>, String> = BTreeMap::new();
3913
3914 fn commit(
3915 table: &mut BTreeMap<Vec<u8>, String>,
3916 mut pending: Vec<(Vec<u8>, String, Diff)>,
3917 ) {
3918 pending.sort_by(|a, b| a.2.cmp(&b.2));
3920 for (k, v, diff) in pending {
3921 if diff == Diff::MINUS_ONE {
3922 let prev = table.remove(&k);
3923 assert_eq!(prev, Some(v));
3924 } else if diff == Diff::ONE {
3925 let prev = table.insert(k, v);
3926 assert_eq!(prev, None);
3927 } else {
3928 panic!("unexpected diff: {diff}");
3929 }
3930 }
3931 }
3932
3933 table.insert(1i64.to_le_bytes().to_vec(), "v1".to_string());
3934 table.insert(2i64.to_le_bytes().to_vec(), "v2".to_string());
3935 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
3936 table.clone(),
3937 uniqueness_violation,
3938 uniqueness_violation,
3939 )
3940 .unwrap();
3941 assert_eq!(table_txn.items_cloned(), table);
3942 assert_eq!(table_txn.delete(|_k, _v| false, 0).len(), 0);
3943 assert_eq!(table_txn.delete(|_k, v| v == "v2", 1).len(), 1);
3944 assert_eq!(
3945 table_txn.items_cloned(),
3946 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v1".to_string())])
3947 );
3948 assert_eq!(
3949 table_txn
3950 .update(|_k, _v| Some("v3".to_string()), 2)
3951 .unwrap(),
3952 Diff::ONE
3953 );
3954
3955 table_txn
3957 .insert(3i64.to_le_bytes().to_vec(), "v3".to_string(), 3)
3958 .unwrap_err();
3959
3960 table_txn
3961 .insert(3i64.to_le_bytes().to_vec(), "v4".to_string(), 4)
3962 .unwrap();
3963 assert_eq!(
3964 table_txn.items_cloned(),
3965 BTreeMap::from([
3966 (1i64.to_le_bytes().to_vec(), "v3".to_string()),
3967 (3i64.to_le_bytes().to_vec(), "v4".to_string()),
3968 ])
3969 );
3970 let err = table_txn
3971 .update(|_k, _v| Some("v1".to_string()), 5)
3972 .unwrap_err();
3973 assert!(
3974 matches!(err, DurableCatalogError::UniquenessViolation),
3975 "unexpected err: {err:?}"
3976 );
3977 let pending = table_txn.pending();
3978 assert_eq!(
3979 pending,
3980 vec![
3981 (
3982 1i64.to_le_bytes().to_vec(),
3983 "v1".to_string(),
3984 Diff::MINUS_ONE
3985 ),
3986 (1i64.to_le_bytes().to_vec(), "v3".to_string(), Diff::ONE),
3987 (
3988 2i64.to_le_bytes().to_vec(),
3989 "v2".to_string(),
3990 Diff::MINUS_ONE
3991 ),
3992 (3i64.to_le_bytes().to_vec(), "v4".to_string(), Diff::ONE),
3993 ]
3994 );
3995 commit(&mut table, pending);
3996 assert_eq!(
3997 table,
3998 BTreeMap::from([
3999 (1i64.to_le_bytes().to_vec(), "v3".to_string()),
4000 (3i64.to_le_bytes().to_vec(), "v4".to_string())
4001 ])
4002 );
4003
4004 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4005 table.clone(),
4006 uniqueness_violation,
4007 uniqueness_violation,
4008 )
4009 .unwrap();
4010 assert_eq!(
4012 table_txn.delete(|k, _v| k == &1i64.to_le_bytes(), 0).len(),
4013 1
4014 );
4015 table_txn
4016 .insert(1i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4017 .unwrap();
4018 table_txn
4020 .insert(5i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4021 .unwrap_err();
4022 table_txn
4024 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 0)
4025 .unwrap_err();
4026 assert_eq!(
4027 table_txn.delete(|k, _v| k == &1i64.to_le_bytes(), 0).len(),
4028 1
4029 );
4030 table_txn
4032 .insert(5i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4033 .unwrap();
4034 table_txn
4035 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 0)
4036 .unwrap();
4037 let pending = table_txn.pending();
4038 assert_eq!(
4039 pending,
4040 vec![
4041 (
4042 1i64.to_le_bytes().to_vec(),
4043 "v3".to_string(),
4044 Diff::MINUS_ONE
4045 ),
4046 (1i64.to_le_bytes().to_vec(), "v5".to_string(), Diff::ONE),
4047 (5i64.to_le_bytes().to_vec(), "v3".to_string(), Diff::ONE),
4048 ]
4049 );
4050 commit(&mut table, pending);
4051 assert_eq!(
4052 table,
4053 BTreeMap::from([
4054 (1i64.to_le_bytes().to_vec(), "v5".to_string()),
4055 (3i64.to_le_bytes().to_vec(), "v4".to_string()),
4056 (5i64.to_le_bytes().to_vec(), "v3".to_string()),
4057 ])
4058 );
4059
4060 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4061 table.clone(),
4062 uniqueness_violation,
4063 uniqueness_violation,
4064 )
4065 .unwrap();
4066 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 3);
4067 table_txn
4068 .insert(1i64.to_le_bytes().to_vec(), "v1".to_string(), 0)
4069 .unwrap();
4070
4071 commit(&mut table, table_txn.pending());
4072 assert_eq!(
4073 table,
4074 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v1".to_string()),])
4075 );
4076
4077 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4078 table.clone(),
4079 uniqueness_violation,
4080 uniqueness_violation,
4081 )
4082 .unwrap();
4083 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 1);
4084 table_txn
4085 .insert(1i64.to_le_bytes().to_vec(), "v2".to_string(), 0)
4086 .unwrap();
4087 commit(&mut table, table_txn.pending());
4088 assert_eq!(
4089 table,
4090 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v2".to_string()),])
4091 );
4092
4093 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4095 table.clone(),
4096 uniqueness_violation,
4097 uniqueness_violation,
4098 )
4099 .unwrap();
4100 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 1);
4101 table_txn
4102 .insert(1i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4103 .unwrap();
4104 table_txn
4105 .insert(1i64.to_le_bytes().to_vec(), "v4".to_string(), 1)
4106 .unwrap_err();
4107 assert_eq!(table_txn.delete(|_k, _v| true, 1).len(), 1);
4108 table_txn
4109 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 1)
4110 .unwrap();
4111 commit(&mut table, table_txn.pending());
4112 assert_eq!(
4113 table.clone().into_iter().collect::<Vec<_>>(),
4114 vec![(1i64.to_le_bytes().to_vec(), "v5".to_string())]
4115 );
4116
4117 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4119 table.clone(),
4120 uniqueness_violation,
4121 uniqueness_violation,
4122 )
4123 .unwrap();
4124 table_txn
4126 .set(2i64.to_le_bytes().to_vec(), Some("v5".to_string()), 0)
4127 .unwrap_err();
4128 table_txn
4129 .set(3i64.to_le_bytes().to_vec(), Some("v6".to_string()), 1)
4130 .unwrap();
4131 table_txn.set(2i64.to_le_bytes().to_vec(), None, 2).unwrap();
4132 table_txn.set(1i64.to_le_bytes().to_vec(), None, 2).unwrap();
4133 let pending = table_txn.pending();
4134 assert_eq!(
4135 pending,
4136 vec![
4137 (
4138 1i64.to_le_bytes().to_vec(),
4139 "v5".to_string(),
4140 Diff::MINUS_ONE
4141 ),
4142 (3i64.to_le_bytes().to_vec(), "v6".to_string(), Diff::ONE),
4143 ]
4144 );
4145 commit(&mut table, pending);
4146 assert_eq!(
4147 table,
4148 BTreeMap::from([(3i64.to_le_bytes().to_vec(), "v6".to_string())])
4149 );
4150
4151 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4153 table.clone(),
4154 uniqueness_violation,
4155 uniqueness_violation,
4156 )
4157 .unwrap();
4158 table_txn
4159 .set(3i64.to_le_bytes().to_vec(), Some("v6".to_string()), 0)
4160 .unwrap();
4161 let pending = table_txn.pending::<Vec<u8>, String>();
4162 assert!(pending.is_empty());
4163
4164 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4166 table.clone(),
4167 uniqueness_violation,
4168 uniqueness_violation,
4169 )
4170 .unwrap();
4171 table_txn
4173 .set_many(
4174 BTreeMap::from([
4175 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4176 (42i64.to_le_bytes().to_vec(), Some("v1".to_string())),
4177 ]),
4178 0,
4179 )
4180 .unwrap_err();
4181 table_txn
4182 .set_many(
4183 BTreeMap::from([
4184 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4185 (3i64.to_le_bytes().to_vec(), Some("v1".to_string())),
4186 ]),
4187 1,
4188 )
4189 .unwrap();
4190 table_txn
4191 .set_many(
4192 BTreeMap::from([
4193 (42i64.to_le_bytes().to_vec(), Some("v7".to_string())),
4194 (3i64.to_le_bytes().to_vec(), None),
4195 ]),
4196 2,
4197 )
4198 .unwrap();
4199 let pending = table_txn.pending();
4200 assert_eq!(
4201 pending,
4202 vec![
4203 (1i64.to_le_bytes().to_vec(), "v6".to_string(), Diff::ONE),
4204 (
4205 3i64.to_le_bytes().to_vec(),
4206 "v6".to_string(),
4207 Diff::MINUS_ONE
4208 ),
4209 (42i64.to_le_bytes().to_vec(), "v7".to_string(), Diff::ONE),
4210 ]
4211 );
4212 commit(&mut table, pending);
4213 assert_eq!(
4214 table,
4215 BTreeMap::from([
4216 (1i64.to_le_bytes().to_vec(), "v6".to_string()),
4217 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4218 ])
4219 );
4220
4221 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4223 table.clone(),
4224 uniqueness_violation,
4225 uniqueness_violation,
4226 )
4227 .unwrap();
4228 table_txn
4229 .set_many(
4230 BTreeMap::from([
4231 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4232 (42i64.to_le_bytes().to_vec(), Some("v7".to_string())),
4233 ]),
4234 0,
4235 )
4236 .unwrap();
4237 let pending = table_txn.pending::<Vec<u8>, String>();
4238 assert!(pending.is_empty());
4239 commit(&mut table, pending);
4240 assert_eq!(
4241 table,
4242 BTreeMap::from([
4243 (1i64.to_le_bytes().to_vec(), "v6".to_string()),
4244 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4245 ])
4246 );
4247
4248 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4250 table.clone(),
4251 uniqueness_violation,
4252 uniqueness_violation,
4253 )
4254 .unwrap();
4255 table_txn
4257 .update_by_key(1i64.to_le_bytes().to_vec(), "v7".to_string(), 0)
4258 .unwrap_err();
4259 assert!(
4260 table_txn
4261 .update_by_key(1i64.to_le_bytes().to_vec(), "v8".to_string(), 1)
4262 .unwrap()
4263 );
4264 assert!(
4265 !table_txn
4266 .update_by_key(5i64.to_le_bytes().to_vec(), "v8".to_string(), 2)
4267 .unwrap()
4268 );
4269 let pending = table_txn.pending();
4270 assert_eq!(
4271 pending,
4272 vec![
4273 (
4274 1i64.to_le_bytes().to_vec(),
4275 "v6".to_string(),
4276 Diff::MINUS_ONE
4277 ),
4278 (1i64.to_le_bytes().to_vec(), "v8".to_string(), Diff::ONE),
4279 ]
4280 );
4281 commit(&mut table, pending);
4282 assert_eq!(
4283 table,
4284 BTreeMap::from([
4285 (1i64.to_le_bytes().to_vec(), "v8".to_string()),
4286 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4287 ])
4288 );
4289
4290 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4292 table.clone(),
4293 uniqueness_violation,
4294 uniqueness_violation,
4295 )
4296 .unwrap();
4297 assert!(
4298 table_txn
4299 .update_by_key(1i64.to_le_bytes().to_vec(), "v8".to_string(), 0)
4300 .unwrap()
4301 );
4302 let pending = table_txn.pending::<Vec<u8>, String>();
4303 assert!(pending.is_empty());
4304 commit(&mut table, pending);
4305 assert_eq!(
4306 table,
4307 BTreeMap::from([
4308 (1i64.to_le_bytes().to_vec(), "v8".to_string()),
4309 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4310 ])
4311 );
4312
4313 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4315 table.clone(),
4316 uniqueness_violation,
4317 uniqueness_violation,
4318 )
4319 .unwrap();
4320 table_txn
4322 .update_by_keys(
4323 [
4324 (1i64.to_le_bytes().to_vec(), "v7".to_string()),
4325 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4326 ],
4327 0,
4328 )
4329 .unwrap_err();
4330 let n = table_txn
4331 .update_by_keys(
4332 [
4333 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4334 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4335 ],
4336 1,
4337 )
4338 .unwrap();
4339 assert_eq!(n, Diff::ONE);
4340 let n = table_txn
4341 .update_by_keys(
4342 [
4343 (15i64.to_le_bytes().to_vec(), "v9".to_string()),
4344 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4345 ],
4346 2,
4347 )
4348 .unwrap();
4349 assert_eq!(n, Diff::ZERO);
4350 let pending = table_txn.pending();
4351 assert_eq!(
4352 pending,
4353 vec![
4354 (
4355 1i64.to_le_bytes().to_vec(),
4356 "v8".to_string(),
4357 Diff::MINUS_ONE
4358 ),
4359 (1i64.to_le_bytes().to_vec(), "v9".to_string(), Diff::ONE),
4360 ]
4361 );
4362 commit(&mut table, pending);
4363 assert_eq!(
4364 table,
4365 BTreeMap::from([
4366 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4367 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4368 ])
4369 );
4370
4371 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4373 table.clone(),
4374 uniqueness_violation,
4375 uniqueness_violation,
4376 )
4377 .unwrap();
4378 let n = table_txn
4379 .update_by_keys(
4380 [
4381 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4382 (42i64.to_le_bytes().to_vec(), "v7".to_string()),
4383 ],
4384 0,
4385 )
4386 .unwrap();
4387 assert_eq!(n, Diff::from(2));
4388 let pending = table_txn.pending::<Vec<u8>, String>();
4389 assert!(pending.is_empty());
4390 commit(&mut table, pending);
4391 assert_eq!(
4392 table,
4393 BTreeMap::from([
4394 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4395 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4396 ])
4397 );
4398
4399 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4401 table.clone(),
4402 uniqueness_violation,
4403 uniqueness_violation,
4404 )
4405 .unwrap();
4406 let prev = table_txn.delete_by_key(1i64.to_le_bytes().to_vec(), 0);
4407 assert_eq!(prev, Some("v9".to_string()));
4408 let prev = table_txn.delete_by_key(5i64.to_le_bytes().to_vec(), 1);
4409 assert_none!(prev);
4410 let prev = table_txn.delete_by_key(1i64.to_le_bytes().to_vec(), 2);
4411 assert_none!(prev);
4412 let pending = table_txn.pending();
4413 assert_eq!(
4414 pending,
4415 vec![(
4416 1i64.to_le_bytes().to_vec(),
4417 "v9".to_string(),
4418 Diff::MINUS_ONE
4419 ),]
4420 );
4421 commit(&mut table, pending);
4422 assert_eq!(
4423 table,
4424 BTreeMap::from([(42i64.to_le_bytes().to_vec(), "v7".to_string())])
4425 );
4426
4427 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4429 table.clone(),
4430 uniqueness_violation,
4431 uniqueness_violation,
4432 )
4433 .unwrap();
4434 let prevs = table_txn.delete_by_keys(
4435 [42i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4436 0,
4437 );
4438 assert_eq!(
4439 prevs,
4440 vec![(42i64.to_le_bytes().to_vec(), "v7".to_string())]
4441 );
4442 let prevs = table_txn.delete_by_keys(
4443 [42i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4444 1,
4445 );
4446 assert_eq!(prevs, vec![]);
4447 let prevs = table_txn.delete_by_keys(
4448 [10i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4449 2,
4450 );
4451 assert_eq!(prevs, vec![]);
4452 let pending = table_txn.pending();
4453 assert_eq!(
4454 pending,
4455 vec![(
4456 42i64.to_le_bytes().to_vec(),
4457 "v7".to_string(),
4458 Diff::MINUS_ONE
4459 ),]
4460 );
4461 commit(&mut table, pending);
4462 assert_eq!(table, BTreeMap::new());
4463 }
4464
4465 #[mz_ore::test(tokio::test)]
4466 #[cfg_attr(miri, ignore)] async fn test_savepoint() {
4468 const VERSION: Version = Version::new(26, 0, 0);
4469 let mut persist_cache = PersistClientCache::new_no_metrics();
4470 persist_cache.cfg.build_version = VERSION;
4471 let persist_client = persist_cache
4472 .open(PersistLocation::new_in_mem())
4473 .await
4474 .unwrap();
4475 let state_builder = TestCatalogStateBuilder::new(persist_client)
4476 .with_default_deploy_generation()
4477 .with_version(VERSION);
4478
4479 let _ = state_builder
4481 .clone()
4482 .unwrap_build()
4483 .await
4484 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4485 .await
4486 .unwrap();
4487 let mut savepoint_state = state_builder
4488 .unwrap_build()
4489 .await
4490 .open_savepoint(SYSTEM_TIME().into(), &test_bootstrap_args())
4491 .await
4492 .unwrap();
4493
4494 let initial_snapshot = savepoint_state.sync_to_current_updates().await.unwrap();
4495 assert!(!initial_snapshot.is_empty());
4496
4497 let db_name = "db";
4498 let db_owner = RoleId::User(42);
4499 let db_privileges = Vec::new();
4500 let mut txn = savepoint_state.transaction().await.unwrap();
4501 let (db_id, db_oid) = txn
4502 .insert_user_database(db_name, db_owner, db_privileges.clone(), &HashSet::new())
4503 .unwrap();
4504 let commit_ts = txn.upper();
4505 txn.commit_internal(commit_ts).await.unwrap();
4506 let updates = savepoint_state.sync_to_current_updates().await.unwrap();
4507 let update = updates.into_element();
4508
4509 assert_eq!(update.diff, StateDiff::Addition);
4510
4511 let db = match update.kind {
4512 memory::objects::StateUpdateKind::Database(db) => db,
4513 update => panic!("unexpected update: {update:?}"),
4514 };
4515
4516 assert_eq!(db_id, db.id);
4517 assert_eq!(db_oid, db.oid);
4518 assert_eq!(db_name, db.name);
4519 assert_eq!(db_owner, db.owner_id);
4520 assert_eq!(db_privileges, db.privileges);
4521 }
4522
4523 #[mz_ore::test(tokio::test)]
4524 #[cfg_attr(miri, ignore)] async fn test_dry_run_transaction_rejects_internal_commit() {
4526 const VERSION: Version = Version::new(26, 0, 0);
4527 let mut persist_cache = PersistClientCache::new_no_metrics();
4528 persist_cache.cfg.build_version = VERSION;
4529 let persist_client = persist_cache
4530 .open(PersistLocation::new_in_mem())
4531 .await
4532 .unwrap();
4533 let mut state = TestCatalogStateBuilder::new(persist_client)
4534 .with_default_deploy_generation()
4535 .with_version(VERSION)
4536 .unwrap_build()
4537 .await
4538 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4539 .await
4540 .unwrap();
4541 let _ = state.sync_to_current_updates().await.unwrap();
4542
4543 let initial_id = state.get_next_id(USER_ITEM_ALLOC_KEY).await.unwrap();
4544 let initial_upper = state.current_upper().await;
4545 let snapshot = state.snapshot().await.unwrap();
4546 let mut dry_run = state.transaction_from_snapshot(snapshot).unwrap();
4547 let ids = dry_run
4548 .transaction_mut()
4549 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), 1)
4550 .unwrap();
4551 assert_eq!(ids, vec![initial_id]);
4552
4553 let transaction = dry_run.transaction;
4554 let err = transaction
4555 .commit_internal(initial_upper)
4556 .await
4557 .unwrap_err();
4558 assert!(matches!(
4559 err,
4560 CatalogError::Durable(DurableCatalogError::DryRunTransaction)
4561 ));
4562 assert_eq!(state.current_upper().await, initial_upper);
4563 assert_eq!(
4564 state.get_next_id(USER_ITEM_ALLOC_KEY).await.unwrap(),
4565 initial_id
4566 );
4567 }
4568
4569 #[mz_ore::test(tokio::test)]
4570 #[cfg_attr(miri, ignore)] async fn test_dry_run_transaction_rejects_into_parts_escape() {
4572 const VERSION: Version = Version::new(26, 0, 0);
4573 let mut persist_cache = PersistClientCache::new_no_metrics();
4574 persist_cache.cfg.build_version = VERSION;
4575 let persist_client = persist_cache
4576 .open(PersistLocation::new_in_mem())
4577 .await
4578 .unwrap();
4579 let mut dry_run_state = TestCatalogStateBuilder::new(persist_client.clone())
4580 .with_default_deploy_generation()
4581 .with_version(VERSION)
4582 .unwrap_build()
4583 .await
4584 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4585 .await
4586 .unwrap();
4587 let mut replacement_state = TestCatalogStateBuilder::new(persist_client)
4588 .with_default_deploy_generation()
4589 .with_version(VERSION)
4590 .unwrap_build()
4591 .await
4592 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4593 .await
4594 .unwrap();
4595 let _ = dry_run_state.sync_to_current_updates().await.unwrap();
4596 let _ = replacement_state.sync_to_current_updates().await.unwrap();
4597
4598 let initial_id = dry_run_state
4599 .get_next_id(USER_ITEM_ALLOC_KEY)
4600 .await
4601 .unwrap();
4602 let initial_upper = dry_run_state.current_upper().await;
4603 let snapshot = dry_run_state.snapshot().await.unwrap();
4604 let mut dry_run = dry_run_state.transaction_from_snapshot(snapshot).unwrap();
4605 let ids = dry_run
4606 .transaction_mut()
4607 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), 1)
4608 .unwrap();
4609 assert_eq!(ids, vec![initial_id]);
4610
4611 let replacement = replacement_state.transaction().await.unwrap();
4612 let escaped = std::mem::replace(dry_run.transaction_mut(), replacement);
4613 drop(dry_run);
4614
4615 let err = match escaped.into_parts() {
4616 Ok(_) => panic!("dry-run transaction decomposed into committable parts"),
4617 Err(err) => err,
4618 };
4619 assert!(matches!(
4620 err,
4621 CatalogError::Durable(DurableCatalogError::DryRunTransaction)
4622 ));
4623 assert_eq!(dry_run_state.current_upper().await, initial_upper);
4624 assert_eq!(
4625 dry_run_state
4626 .get_next_id(USER_ITEM_ALLOC_KEY)
4627 .await
4628 .unwrap(),
4629 initial_id
4630 );
4631 }
4632
4633 #[mz_ore::test(tokio::test)]
4637 #[cfg_attr(miri, ignore)] async fn test_insert_replica_with_id_does_not_consume_allocator() {
4639 const VERSION: Version = Version::new(26, 0, 0);
4640 let mut persist_cache = PersistClientCache::new_no_metrics();
4641 persist_cache.cfg.build_version = VERSION;
4642 let persist_client = persist_cache
4643 .open(PersistLocation::new_in_mem())
4644 .await
4645 .unwrap();
4646 let state_builder = TestCatalogStateBuilder::new(persist_client)
4647 .with_default_deploy_generation()
4648 .with_version(VERSION);
4649 let mut state = state_builder
4650 .unwrap_build()
4651 .await
4652 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4653 .await
4654 .unwrap();
4655
4656 let cluster_id = ClusterId::User(1);
4659 let owner_id = RoleId::User(1);
4660 let config = ReplicaConfig {
4661 location: ReplicaLocation::Managed {
4662 size: "1".to_string(),
4663 availability_zones: Vec::new(),
4664 internal: false,
4665 billed_as: None,
4666 pending: false,
4667 },
4668 logging: ReplicaLogging {
4669 log_logging: false,
4670 interval: Some(Duration::from_secs(1)),
4671 },
4672 arrangement_compression: false,
4673 };
4674
4675 let commit_ts = state.current_upper().await;
4677 let a = state
4678 .allocate_user_replica_ids(1, commit_ts)
4679 .await
4680 .unwrap()
4681 .into_element();
4682 assert!(a.is_user());
4683
4684 let initial_updates = state.sync_to_current_updates().await.unwrap();
4685 assert!(!initial_updates.is_empty());
4686
4687 let mut txn = state.transaction().await.unwrap();
4689 txn.insert_cluster_replica_with_id(cluster_id, a, "explicit", config, owner_id)
4690 .unwrap();
4691 let commit_ts = txn.upper();
4692 txn.commit_internal(commit_ts).await.unwrap();
4693 let _ = state.sync_to_current_updates().await.unwrap();
4694
4695 let commit_ts = state.current_upper().await;
4697 let b = state
4698 .allocate_user_replica_ids(1, commit_ts)
4699 .await
4700 .unwrap()
4701 .into_element();
4702
4703 assert_eq!(b.inner_id(), a.inner_id() + 1);
4706
4707 let txn = state.transaction().await.unwrap();
4709 let found = txn
4710 .get_cluster_replicas()
4711 .any(|replica| replica.replica_id == a);
4712 assert!(found, "explicitly inserted replica {a} not found");
4713 }
4714
4715 #[mz_ore::test]
4716 fn test_allocate_introspection_source_index_id() {
4717 let cluster_variant: u8 = 0b0000_0001;
4718 let cluster_id_inner: u64 =
4719 0b0000_0000_1100_0101_1100_0011_1010_1101_0000_1011_1111_1001_0110_1010;
4720 let timely_messages_received_log_variant: u8 = 0b0000_1000;
4721
4722 let cluster_id = ClusterId::System(cluster_id_inner);
4723 let log_variant = LogVariant::Timely(TimelyLog::MessagesReceived);
4724
4725 let introspection_source_index_id: u64 =
4726 0b0000_0001_1100_0101_1100_0011_1010_1101_0000_1011_1111_1001_0110_1010_0000_1000;
4727
4728 {
4730 let mut cluster_variant_mask = 0xFF << 56;
4731 cluster_variant_mask &= introspection_source_index_id;
4732 cluster_variant_mask >>= 56;
4733 assert_eq!(cluster_variant_mask, u64::from(cluster_variant));
4734 }
4735
4736 {
4738 let mut cluster_id_inner_mask = 0xFFFF_FFFF_FFFF << 8;
4739 cluster_id_inner_mask &= introspection_source_index_id;
4740 cluster_id_inner_mask >>= 8;
4741 assert_eq!(cluster_id_inner_mask, cluster_id_inner);
4742 }
4743
4744 {
4746 let mut log_variant_mask = 0xFF;
4747 log_variant_mask &= introspection_source_index_id;
4748 assert_eq!(
4749 log_variant_mask,
4750 u64::from(timely_messages_received_log_variant)
4751 );
4752 }
4753
4754 let (catalog_item_id, global_id) =
4755 Transaction::allocate_introspection_source_index_id(&cluster_id, log_variant);
4756
4757 assert_eq!(
4758 catalog_item_id,
4759 CatalogItemId::IntrospectionSourceIndex(introspection_source_index_id)
4760 );
4761 assert_eq!(
4762 global_id,
4763 GlobalId::IntrospectionSourceIndex(introspection_source_index_id)
4764 );
4765 }
4766}