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 LogVariant::Compute(ComputeLog::ResourceUsage) => 34,
1025 };
1026
1027 let mut id: u64 = u64::from(cluster_variant) << 56;
1028 id |= cluster_id << 8;
1029 id |= u64::from(log_variant);
1030
1031 (
1032 CatalogItemId::IntrospectionSourceIndex(id),
1033 GlobalId::IntrospectionSourceIndex(id),
1034 )
1035 }
1036
1037 pub fn allocate_user_item_ids(
1038 &mut self,
1039 amount: u64,
1040 ) -> Result<Vec<(CatalogItemId, GlobalId)>, CatalogError> {
1041 Ok(self
1042 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), amount)?
1043 .into_iter()
1044 .map(|x| (CatalogItemId::User(x), GlobalId::User(x)))
1046 .collect())
1047 }
1048
1049 pub fn allocate_system_replica_id(&mut self) -> Result<ReplicaId, CatalogError> {
1050 let id = self.get_and_increment_id(SYSTEM_REPLICA_ID_ALLOC_KEY.to_string())?;
1051 Ok(ReplicaId::System(id))
1052 }
1053
1054 pub fn allocate_audit_log_id(&mut self) -> Result<u64, CatalogError> {
1055 self.get_and_increment_id(AUDIT_LOG_ID_ALLOC_KEY.to_string())
1056 }
1057
1058 #[mz_ore::instrument]
1061 fn allocate_oids(
1062 &mut self,
1063 amount: u64,
1064 temporary_oids: &HashSet<u32>,
1065 ) -> Result<Vec<u32>, CatalogError> {
1066 struct UserOid(u32);
1069
1070 impl UserOid {
1071 fn new(oid: u32) -> Result<UserOid, anyhow::Error> {
1072 if oid < FIRST_USER_OID {
1073 Err(anyhow!("invalid user OID {oid}"))
1074 } else {
1075 Ok(UserOid(oid))
1076 }
1077 }
1078 }
1079
1080 impl std::ops::AddAssign<u32> for UserOid {
1081 fn add_assign(&mut self, rhs: u32) {
1082 let (res, overflow) = self.0.overflowing_add(rhs);
1083 self.0 = if overflow { FIRST_USER_OID + res } else { res };
1084 }
1085 }
1086
1087 if amount > u32::MAX.into() {
1088 return Err(CatalogError::Catalog(SqlCatalogError::OidExhaustion));
1089 }
1090
1091 let mut allocated_oids = HashSet::with_capacity(
1097 self.databases.len()
1098 + self.schemas.len()
1099 + self.roles.len()
1100 + self.items.len()
1101 + self.introspection_sources.len()
1102 + temporary_oids.len(),
1103 );
1104 self.databases.for_values(|_, value| {
1105 allocated_oids.insert(value.oid);
1106 });
1107 self.schemas.for_values(|_, value| {
1108 allocated_oids.insert(value.oid);
1109 });
1110 self.roles.for_values(|_, value| {
1111 allocated_oids.insert(value.oid);
1112 });
1113 self.items.for_values(|_, value| {
1114 allocated_oids.insert(value.oid);
1115 });
1116 self.introspection_sources.for_values(|_, value| {
1117 allocated_oids.insert(value.oid);
1118 });
1119
1120 let is_allocated = |oid| allocated_oids.contains(&oid) || temporary_oids.contains(&oid);
1121
1122 let start_oid: u32 = self
1123 .id_allocator
1124 .items()
1125 .get(&IdAllocKey {
1126 name: OID_ALLOC_KEY.to_string(),
1127 })
1128 .unwrap_or_else(|| panic!("{OID_ALLOC_KEY} id allocator missing"))
1129 .next_id
1130 .try_into()
1131 .expect("we should never persist an oid outside of the u32 range");
1132 let mut current_oid = UserOid::new(start_oid)
1133 .expect("we should never persist an oid outside of user OID range");
1134 let mut oids = Vec::new();
1135 while oids.len() < u64_to_usize(amount) {
1136 if !is_allocated(current_oid.0) {
1137 oids.push(current_oid.0);
1138 }
1139 current_oid += 1;
1140
1141 if current_oid.0 == start_oid && oids.len() < u64_to_usize(amount) {
1142 return Err(CatalogError::Catalog(SqlCatalogError::OidExhaustion));
1144 }
1145 }
1146
1147 let next_id = current_oid.0;
1148 let prev = self.id_allocator.set(
1149 IdAllocKey {
1150 name: OID_ALLOC_KEY.to_string(),
1151 },
1152 Some(IdAllocValue {
1153 next_id: next_id.into(),
1154 }),
1155 self.op_id,
1156 )?;
1157 assert_eq!(
1158 prev,
1159 Some(IdAllocValue {
1160 next_id: start_oid.into(),
1161 })
1162 );
1163
1164 Ok(oids)
1165 }
1166
1167 pub fn allocate_oid(&mut self, temporary_oids: &HashSet<u32>) -> Result<u32, CatalogError> {
1170 self.allocate_oids(1, temporary_oids)
1171 .map(|oids| oids.into_element())
1172 }
1173
1174 pub fn current_snapshot(&self) -> Snapshot {
1182 Snapshot {
1183 databases: self.databases.current_items_proto(),
1184 schemas: self.schemas.current_items_proto(),
1185 roles: self.roles.current_items_proto(),
1186 role_auth: self.role_auth.current_items_proto(),
1187 items: self.items.current_items_proto(),
1188 comments: self.comments.current_items_proto(),
1189 clusters: self.clusters.current_items_proto(),
1190 network_policies: self.network_policies.current_items_proto(),
1191 cluster_replicas: self.cluster_replicas.current_items_proto(),
1192 introspection_sources: self.introspection_sources.current_items_proto(),
1193 id_allocator: self.id_allocator.current_items_proto(),
1194 configs: self.configs.current_items_proto(),
1195 settings: self.settings.current_items_proto(),
1196 system_object_mappings: self.system_gid_mapping.current_items_proto(),
1197 system_configurations: self.system_configurations.current_items_proto(),
1198 cluster_system_configurations: self.cluster_system_configurations.current_items_proto(),
1199 replica_system_configurations: self.replica_system_configurations.current_items_proto(),
1200 default_privileges: self.default_privileges.current_items_proto(),
1201 source_references: self.source_references.current_items_proto(),
1202 system_privileges: self.system_privileges.current_items_proto(),
1203 storage_collection_metadata: self.storage_collection_metadata.current_items_proto(),
1204 unfinalized_shards: self.unfinalized_shards.current_items_proto(),
1205 txn_wal_shard: self.txn_wal_shard.current_items_proto(),
1206 }
1207 }
1208
1209 pub(crate) fn insert_id_allocator(
1210 &mut self,
1211 name: String,
1212 next_id: u64,
1213 ) -> Result<(), CatalogError> {
1214 match self.id_allocator.insert(
1215 IdAllocKey { name: name.clone() },
1216 IdAllocValue { next_id },
1217 self.op_id,
1218 ) {
1219 Ok(_) => Ok(()),
1220 Err(_) => Err(SqlCatalogError::IdAllocatorAlreadyExists(name).into()),
1221 }
1222 }
1223
1224 pub fn remove_database(&mut self, id: &DatabaseId) -> Result<(), CatalogError> {
1231 let prev = self
1232 .databases
1233 .set(DatabaseKey { id: *id }, None, self.op_id)?;
1234 if prev.is_some() {
1235 Ok(())
1236 } else {
1237 Err(SqlCatalogError::UnknownDatabase(id.to_string()).into())
1238 }
1239 }
1240
1241 pub fn remove_databases(
1248 &mut self,
1249 databases: &BTreeSet<DatabaseId>,
1250 ) -> Result<(), CatalogError> {
1251 if databases.is_empty() {
1252 return Ok(());
1253 }
1254
1255 let to_remove = databases
1256 .iter()
1257 .map(|id| (DatabaseKey { id: *id }, None))
1258 .collect();
1259 let mut prev = self.databases.set_many(to_remove, self.op_id)?;
1260 prev.retain(|_k, val| val.is_none());
1261
1262 if !prev.is_empty() {
1263 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1264 return Err(SqlCatalogError::UnknownDatabase(err).into());
1265 }
1266
1267 Ok(())
1268 }
1269
1270 pub fn remove_schema(
1277 &mut self,
1278 database_id: &Option<DatabaseId>,
1279 schema_id: &SchemaId,
1280 ) -> Result<(), CatalogError> {
1281 let prev = self
1282 .schemas
1283 .set(SchemaKey { id: *schema_id }, None, self.op_id)?;
1284 if prev.is_some() {
1285 Ok(())
1286 } else {
1287 let database_name = match database_id {
1288 Some(id) => format!("{id}."),
1289 None => "".to_string(),
1290 };
1291 Err(SqlCatalogError::UnknownSchema(format!("{}.{}", database_name, schema_id)).into())
1292 }
1293 }
1294
1295 pub fn remove_schemas(
1302 &mut self,
1303 schemas: &BTreeMap<SchemaId, ResolvedDatabaseSpecifier>,
1304 ) -> Result<(), CatalogError> {
1305 if schemas.is_empty() {
1306 return Ok(());
1307 }
1308
1309 let to_remove = schemas
1310 .iter()
1311 .map(|(schema_id, _)| (SchemaKey { id: *schema_id }, None))
1312 .collect();
1313 let mut prev = self.schemas.set_many(to_remove, self.op_id)?;
1314 prev.retain(|_k, v| v.is_none());
1315
1316 if !prev.is_empty() {
1317 let err = prev
1318 .keys()
1319 .map(|k| {
1320 let db_spec = schemas.get(&k.id).expect("should_exist");
1321 let db_name = match db_spec {
1322 ResolvedDatabaseSpecifier::Id(id) => format!("{id}."),
1323 ResolvedDatabaseSpecifier::Ambient => "".to_string(),
1324 };
1325 format!("{}.{}", db_name, k.id)
1326 })
1327 .join(", ");
1328
1329 return Err(SqlCatalogError::UnknownSchema(err).into());
1330 }
1331
1332 Ok(())
1333 }
1334
1335 pub fn remove_source_references(
1336 &mut self,
1337 source_id: CatalogItemId,
1338 ) -> Result<(), CatalogError> {
1339 let deleted = self
1340 .source_references
1341 .delete_by_key(SourceReferencesKey { source_id }, self.op_id)
1342 .is_some();
1343 if deleted {
1344 Ok(())
1345 } else {
1346 Err(SqlCatalogError::UnknownItem(source_id.to_string()).into())
1347 }
1348 }
1349
1350 pub fn remove_user_roles(&mut self, roles: &BTreeSet<RoleId>) -> Result<(), CatalogError> {
1357 assert!(
1358 roles.iter().all(|id| id.is_user()),
1359 "cannot delete non-user roles"
1360 );
1361 self.remove_roles(roles)
1362 }
1363
1364 pub fn remove_roles(&mut self, roles: &BTreeSet<RoleId>) -> Result<(), CatalogError> {
1371 if roles.is_empty() {
1372 return Ok(());
1373 }
1374
1375 let to_remove_keys = roles
1376 .iter()
1377 .map(|role_id| RoleKey { id: *role_id })
1378 .collect::<Vec<_>>();
1379
1380 let to_remove_roles = to_remove_keys
1381 .iter()
1382 .map(|role_key| (role_key.clone(), None))
1383 .collect();
1384
1385 let mut prev = self.roles.set_many(to_remove_roles, self.op_id)?;
1386
1387 let to_remove_role_auth = to_remove_keys
1388 .iter()
1389 .map(|role_key| {
1390 (
1391 RoleAuthKey {
1392 role_id: role_key.id,
1393 },
1394 None,
1395 )
1396 })
1397 .collect();
1398
1399 let mut role_auth_prev = self.role_auth.set_many(to_remove_role_auth, self.op_id)?;
1400
1401 prev.retain(|_k, v| v.is_none());
1402 if !prev.is_empty() {
1403 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1404 return Err(SqlCatalogError::UnknownRole(err).into());
1405 }
1406
1407 role_auth_prev.retain(|_k, v| v.is_none());
1408 Ok(())
1412 }
1413
1414 pub fn remove_clusters(&mut self, clusters: &BTreeSet<ClusterId>) -> Result<(), CatalogError> {
1421 if clusters.is_empty() {
1422 return Ok(());
1423 }
1424
1425 let to_remove = clusters
1426 .iter()
1427 .map(|cluster_id| (ClusterKey { id: *cluster_id }, None))
1428 .collect();
1429 let mut prev = self.clusters.set_many(to_remove, self.op_id)?;
1430
1431 prev.retain(|_k, v| v.is_none());
1432 if !prev.is_empty() {
1433 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1434 return Err(SqlCatalogError::UnknownCluster(err).into());
1435 }
1436
1437 self.cluster_replicas
1443 .delete(|_k, v| clusters.contains(&v.cluster_id), self.op_id);
1444 self.introspection_sources
1445 .delete(|k, _v| clusters.contains(&k.cluster_id), self.op_id);
1446
1447 Ok(())
1448 }
1449
1450 pub fn remove_cluster_replica(&mut self, id: ReplicaId) -> Result<(), CatalogError> {
1457 let deleted = self
1458 .cluster_replicas
1459 .delete_by_key(ClusterReplicaKey { id }, self.op_id)
1460 .is_some();
1461 if deleted {
1462 Ok(())
1463 } else {
1464 Err(SqlCatalogError::UnknownClusterReplica(id.to_string()).into())
1465 }
1466 }
1467
1468 pub fn remove_cluster_replicas(
1475 &mut self,
1476 replicas: &BTreeSet<ReplicaId>,
1477 ) -> Result<(), CatalogError> {
1478 if replicas.is_empty() {
1479 return Ok(());
1480 }
1481
1482 let to_remove = replicas
1483 .iter()
1484 .map(|replica_id| (ClusterReplicaKey { id: *replica_id }, None))
1485 .collect();
1486 let mut prev = self.cluster_replicas.set_many(to_remove, self.op_id)?;
1487
1488 prev.retain(|_k, v| v.is_none());
1489 if !prev.is_empty() {
1490 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1491 return Err(SqlCatalogError::UnknownClusterReplica(err).into());
1492 }
1493
1494 Ok(())
1495 }
1496
1497 pub fn remove_item(&mut self, id: CatalogItemId) -> Result<(), CatalogError> {
1504 let prev = self.items.set(ItemKey { id }, None, self.op_id)?;
1505 if prev.is_some() {
1506 Ok(())
1507 } else {
1508 Err(SqlCatalogError::UnknownItem(id.to_string()).into())
1509 }
1510 }
1511
1512 pub fn remove_items(&mut self, ids: &BTreeSet<CatalogItemId>) -> Result<(), CatalogError> {
1519 if ids.is_empty() {
1520 return Ok(());
1521 }
1522
1523 let ks: Vec<_> = ids.clone().into_iter().map(|id| ItemKey { id }).collect();
1524 let n = self.items.delete_by_keys(ks, self.op_id).len();
1525 if n == ids.len() {
1526 Ok(())
1527 } else {
1528 let item_ids = self.items.items().keys().map(|k| k.id).collect();
1529 let mut unknown = ids.difference(&item_ids);
1530 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1531 }
1532 }
1533
1534 pub fn remove_system_object_mappings(
1541 &mut self,
1542 descriptions: BTreeSet<SystemObjectDescription>,
1543 ) -> Result<(), CatalogError> {
1544 if descriptions.is_empty() {
1545 return Ok(());
1546 }
1547
1548 let ks: Vec<_> = descriptions
1549 .clone()
1550 .into_iter()
1551 .map(|desc| GidMappingKey {
1552 schema_name: desc.schema_name,
1553 object_type: desc.object_type,
1554 object_name: desc.object_name,
1555 })
1556 .collect();
1557 let n = self.system_gid_mapping.delete_by_keys(ks, self.op_id).len();
1558
1559 if n == descriptions.len() {
1560 Ok(())
1561 } else {
1562 let item_descriptions = self
1563 .system_gid_mapping
1564 .items()
1565 .keys()
1566 .map(|k| SystemObjectDescription {
1567 schema_name: k.schema_name.clone(),
1568 object_type: k.object_type.clone(),
1569 object_name: k.object_name.clone(),
1570 })
1571 .collect();
1572 let mut unknown = descriptions.difference(&item_descriptions).map(|desc| {
1573 format!(
1574 "{} {}.{}",
1575 desc.object_type, desc.schema_name, desc.object_name
1576 )
1577 });
1578 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1579 }
1580 }
1581
1582 pub fn remove_introspection_source_indexes(
1589 &mut self,
1590 introspection_source_indexes: BTreeSet<(ClusterId, String)>,
1591 ) -> Result<(), CatalogError> {
1592 if introspection_source_indexes.is_empty() {
1593 return Ok(());
1594 }
1595
1596 let ks: Vec<_> = introspection_source_indexes
1597 .clone()
1598 .into_iter()
1599 .map(|(cluster_id, name)| ClusterIntrospectionSourceIndexKey { cluster_id, name })
1600 .collect();
1601 let n = self
1602 .introspection_sources
1603 .delete_by_keys(ks, self.op_id)
1604 .len();
1605 if n == introspection_source_indexes.len() {
1606 Ok(())
1607 } else {
1608 let txn_indexes = self
1609 .introspection_sources
1610 .items()
1611 .keys()
1612 .map(|k| (k.cluster_id, k.name.clone()))
1613 .collect();
1614 let mut unknown = introspection_source_indexes
1615 .difference(&txn_indexes)
1616 .map(|(cluster_id, name)| format!("{cluster_id} {name}"));
1617 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1618 }
1619 }
1620
1621 pub fn update_item(&mut self, id: CatalogItemId, item: Item) -> Result<(), CatalogError> {
1628 let updated =
1629 self.items
1630 .update_by_key(ItemKey { id }, item.into_key_value().1, self.op_id)?;
1631 if updated {
1632 Ok(())
1633 } else {
1634 Err(SqlCatalogError::UnknownItem(id.to_string()).into())
1635 }
1636 }
1637
1638 pub fn update_items(
1646 &mut self,
1647 items: BTreeMap<CatalogItemId, Item>,
1648 ) -> Result<(), CatalogError> {
1649 if items.is_empty() {
1650 return Ok(());
1651 }
1652
1653 let update_ids: BTreeSet<_> = items.keys().cloned().collect();
1654 let kvs: Vec<_> = items
1655 .clone()
1656 .into_iter()
1657 .map(|(id, item)| (ItemKey { id }, item.into_key_value().1))
1658 .collect();
1659 let n = self.items.update_by_keys(kvs, self.op_id)?;
1660 let n = usize::try_from(n.into_inner()).expect("Must be positive and fit in usize");
1661 if n == update_ids.len() {
1662 Ok(())
1663 } else {
1664 let item_ids: BTreeSet<_> = self.items.items().keys().map(|k| k.id).collect();
1665 let mut unknown = update_ids.difference(&item_ids);
1666 Err(SqlCatalogError::UnknownItem(unknown.join(", ")).into())
1667 }
1668 }
1669
1670 pub fn update_role(
1678 &mut self,
1679 id: RoleId,
1680 role: Role,
1681 password: PasswordAction,
1682 ) -> Result<(), CatalogError> {
1683 let key = RoleKey { id };
1684 if self.roles.get(&key).is_some() {
1685 let auth_key = RoleAuthKey { role_id: id };
1686
1687 match password {
1688 PasswordAction::Set(new_password) => {
1689 let hash = mz_auth::hash::scram256_hash(
1690 &new_password.password,
1691 &new_password.scram_iterations,
1692 )
1693 .expect("password hash should be valid");
1694 let value = RoleAuthValue {
1695 password_hash: Some(hash),
1696 updated_at: SYSTEM_TIME(),
1697 };
1698
1699 if self.role_auth.get(&auth_key).is_some() {
1700 self.role_auth
1701 .update_by_key(auth_key.clone(), value, self.op_id)?;
1702 } else {
1703 self.role_auth.insert(auth_key.clone(), value, self.op_id)?;
1704 }
1705 }
1706 PasswordAction::Clear => {
1707 let value = RoleAuthValue {
1708 password_hash: None,
1709 updated_at: SYSTEM_TIME(),
1710 };
1711 if self.role_auth.get(&auth_key).is_some() {
1712 self.role_auth
1713 .update_by_key(auth_key.clone(), value, self.op_id)?;
1714 }
1715 }
1716 PasswordAction::NoChange => {}
1717 }
1718
1719 self.roles
1720 .update_by_key(key, role.into_key_value().1, self.op_id)?;
1721
1722 Ok(())
1723 } else {
1724 Err(SqlCatalogError::UnknownRole(id.to_string()).into())
1725 }
1726 }
1727
1728 pub fn update_roles_without_auth(
1739 &mut self,
1740 roles: BTreeMap<RoleId, Role>,
1741 ) -> Result<(), CatalogError> {
1742 if roles.is_empty() {
1743 return Ok(());
1744 }
1745
1746 let update_role_ids: BTreeSet<_> = roles.keys().cloned().collect();
1747 let kvs: Vec<_> = roles
1748 .into_iter()
1749 .map(|(id, role)| (RoleKey { id }, role.into_key_value().1))
1750 .collect();
1751 let n = self.roles.update_by_keys(kvs, self.op_id)?;
1752 let n = usize::try_from(n.into_inner()).expect("Must be positive and fit in usize");
1753
1754 if n == update_role_ids.len() {
1755 Ok(())
1756 } else {
1757 let role_ids: BTreeSet<_> = self.roles.items().keys().map(|k| k.id).collect();
1758 let mut unknown = update_role_ids.difference(&role_ids);
1759 Err(SqlCatalogError::UnknownRole(unknown.join(", ")).into())
1760 }
1761 }
1762
1763 pub fn update_system_object_mappings(
1768 &mut self,
1769 mappings: BTreeMap<CatalogItemId, SystemObjectMapping>,
1770 ) -> Result<(), CatalogError> {
1771 if mappings.is_empty() {
1772 return Ok(());
1773 }
1774
1775 let n = self.system_gid_mapping.update(
1776 |_k, v| {
1777 if let Some(mapping) = mappings.get(&CatalogItemId::from(v.catalog_id)) {
1778 let (_, new_value) = mapping.clone().into_key_value();
1779 Some(new_value)
1780 } else {
1781 None
1782 }
1783 },
1784 self.op_id,
1785 )?;
1786
1787 if usize::try_from(n.into_inner()).expect("update diff should fit into usize")
1788 != mappings.len()
1789 {
1790 let id_str = mappings.keys().map(|id| id.to_string()).join(",");
1791 return Err(SqlCatalogError::FailedBuiltinSchemaMigration(id_str).into());
1792 }
1793
1794 Ok(())
1795 }
1796
1797 pub fn update_cluster(&mut self, id: ClusterId, cluster: Cluster) -> Result<(), CatalogError> {
1804 let updated = self.clusters.update_by_key(
1805 ClusterKey { id },
1806 cluster.into_key_value().1,
1807 self.op_id,
1808 )?;
1809 if updated {
1810 Ok(())
1811 } else {
1812 Err(SqlCatalogError::UnknownCluster(id.to_string()).into())
1813 }
1814 }
1815
1816 pub fn update_cluster_replica(
1823 &mut self,
1824 replica_id: ReplicaId,
1825 replica: ClusterReplica,
1826 ) -> Result<(), CatalogError> {
1827 let updated = self.cluster_replicas.update_by_key(
1828 ClusterReplicaKey { id: replica_id },
1829 replica.into_key_value().1,
1830 self.op_id,
1831 )?;
1832 if updated {
1833 Ok(())
1834 } else {
1835 Err(SqlCatalogError::UnknownClusterReplica(replica_id.to_string()).into())
1836 }
1837 }
1838
1839 pub fn update_database(
1846 &mut self,
1847 id: DatabaseId,
1848 database: Database,
1849 ) -> Result<(), CatalogError> {
1850 let updated = self.databases.update_by_key(
1851 DatabaseKey { id },
1852 database.into_key_value().1,
1853 self.op_id,
1854 )?;
1855 if updated {
1856 Ok(())
1857 } else {
1858 Err(SqlCatalogError::UnknownDatabase(id.to_string()).into())
1859 }
1860 }
1861
1862 pub fn update_schema(
1869 &mut self,
1870 schema_id: SchemaId,
1871 schema: Schema,
1872 ) -> Result<(), CatalogError> {
1873 let updated = self.schemas.update_by_key(
1874 SchemaKey { id: schema_id },
1875 schema.into_key_value().1,
1876 self.op_id,
1877 )?;
1878 if updated {
1879 Ok(())
1880 } else {
1881 Err(SqlCatalogError::UnknownSchema(schema_id.to_string()).into())
1882 }
1883 }
1884
1885 pub fn update_network_policy(
1892 &mut self,
1893 id: NetworkPolicyId,
1894 network_policy: NetworkPolicy,
1895 ) -> Result<(), CatalogError> {
1896 let updated = self.network_policies.update_by_key(
1897 NetworkPolicyKey { id },
1898 network_policy.into_key_value().1,
1899 self.op_id,
1900 )?;
1901 if updated {
1902 Ok(())
1903 } else {
1904 Err(SqlCatalogError::UnknownNetworkPolicy(id.to_string()).into())
1905 }
1906 }
1907 pub fn remove_network_policies(
1914 &mut self,
1915 network_policies: &BTreeSet<NetworkPolicyId>,
1916 ) -> Result<(), CatalogError> {
1917 if network_policies.is_empty() {
1918 return Ok(());
1919 }
1920
1921 let to_remove = network_policies
1922 .iter()
1923 .map(|policy_id| (NetworkPolicyKey { id: *policy_id }, None))
1924 .collect();
1925 let mut prev = self.network_policies.set_many(to_remove, self.op_id)?;
1926 assert!(
1927 prev.iter().all(|(k, _)| k.id.is_user()),
1928 "cannot delete non-user network policy"
1929 );
1930
1931 prev.retain(|_k, v| v.is_none());
1932 if !prev.is_empty() {
1933 let err = prev.keys().map(|k| k.id.to_string()).join(", ");
1934 return Err(SqlCatalogError::UnknownNetworkPolicy(err).into());
1935 }
1936
1937 Ok(())
1938 }
1939 pub fn set_default_privilege(
1943 &mut self,
1944 role_id: RoleId,
1945 database_id: Option<DatabaseId>,
1946 schema_id: Option<SchemaId>,
1947 object_type: ObjectType,
1948 grantee: RoleId,
1949 privileges: Option<AclMode>,
1950 ) -> Result<(), CatalogError> {
1951 self.default_privileges.set(
1952 DefaultPrivilegesKey {
1953 role_id,
1954 database_id,
1955 schema_id,
1956 object_type,
1957 grantee,
1958 },
1959 privileges.map(|privileges| DefaultPrivilegesValue { privileges }),
1960 self.op_id,
1961 )?;
1962 Ok(())
1963 }
1964
1965 pub fn set_default_privileges(
1967 &mut self,
1968 default_privileges: Vec<DefaultPrivilege>,
1969 ) -> Result<(), CatalogError> {
1970 if default_privileges.is_empty() {
1971 return Ok(());
1972 }
1973
1974 let default_privileges = default_privileges
1975 .into_iter()
1976 .map(DurableType::into_key_value)
1977 .map(|(k, v)| (k, Some(v)))
1978 .collect();
1979 self.default_privileges
1980 .set_many(default_privileges, self.op_id)?;
1981 Ok(())
1982 }
1983
1984 pub fn set_system_privilege(
1988 &mut self,
1989 grantee: RoleId,
1990 grantor: RoleId,
1991 acl_mode: Option<AclMode>,
1992 ) -> Result<(), CatalogError> {
1993 self.system_privileges.set(
1994 SystemPrivilegesKey { grantee, grantor },
1995 acl_mode.map(|acl_mode| SystemPrivilegesValue { acl_mode }),
1996 self.op_id,
1997 )?;
1998 Ok(())
1999 }
2000
2001 pub fn set_system_privileges(
2003 &mut self,
2004 system_privileges: Vec<MzAclItem>,
2005 ) -> Result<(), CatalogError> {
2006 if system_privileges.is_empty() {
2007 return Ok(());
2008 }
2009
2010 let system_privileges = system_privileges
2011 .into_iter()
2012 .map(DurableType::into_key_value)
2013 .map(|(k, v)| (k, Some(v)))
2014 .collect();
2015 self.system_privileges
2016 .set_many(system_privileges, self.op_id)?;
2017 Ok(())
2018 }
2019
2020 pub fn set_setting(&mut self, name: String, value: Option<String>) -> Result<(), CatalogError> {
2022 self.settings.set(
2023 SettingKey { name },
2024 value.map(|value| SettingValue { value }),
2025 self.op_id,
2026 )?;
2027 Ok(())
2028 }
2029
2030 pub fn set_catalog_content_version(&mut self, version: String) -> Result<(), CatalogError> {
2031 self.set_setting(CATALOG_CONTENT_VERSION_KEY.to_string(), Some(version))
2032 }
2033
2034 pub fn insert_introspection_source_indexes(
2036 &mut self,
2037 introspection_source_indexes: Vec<(ClusterId, String, CatalogItemId, GlobalId)>,
2038 temporary_oids: &HashSet<u32>,
2039 ) -> Result<(), CatalogError> {
2040 if introspection_source_indexes.is_empty() {
2041 return Ok(());
2042 }
2043
2044 let amount = usize_to_u64(introspection_source_indexes.len());
2045 let oids = self.allocate_oids(amount, temporary_oids)?;
2046 let introspection_source_indexes: Vec<_> = introspection_source_indexes
2047 .into_iter()
2048 .zip_eq(oids)
2049 .map(
2050 |((cluster_id, name, item_id, index_id), oid)| IntrospectionSourceIndex {
2051 cluster_id,
2052 name,
2053 item_id,
2054 index_id,
2055 oid,
2056 },
2057 )
2058 .collect();
2059
2060 for introspection_source_index in introspection_source_indexes {
2061 let (key, value) = introspection_source_index.into_key_value();
2062 self.introspection_sources.insert(key, value, self.op_id)?;
2063 }
2064
2065 Ok(())
2066 }
2067
2068 pub fn set_system_object_mappings(
2070 &mut self,
2071 mappings: Vec<SystemObjectMapping>,
2072 ) -> Result<(), CatalogError> {
2073 if mappings.is_empty() {
2074 return Ok(());
2075 }
2076
2077 let mappings = mappings
2078 .into_iter()
2079 .map(DurableType::into_key_value)
2080 .map(|(k, v)| (k, Some(v)))
2081 .collect();
2082 self.system_gid_mapping.set_many(mappings, self.op_id)?;
2083 Ok(())
2084 }
2085
2086 pub fn set_replicas(&mut self, replicas: Vec<ClusterReplica>) -> Result<(), CatalogError> {
2088 if replicas.is_empty() {
2089 return Ok(());
2090 }
2091
2092 let replicas = replicas
2093 .into_iter()
2094 .map(DurableType::into_key_value)
2095 .map(|(k, v)| (k, Some(v)))
2096 .collect();
2097 self.cluster_replicas.set_many(replicas, self.op_id)?;
2098 Ok(())
2099 }
2100
2101 pub fn set_config(&mut self, key: String, value: Option<u64>) -> Result<(), CatalogError> {
2103 match value {
2104 Some(value) => {
2105 let config = Config { key, value };
2106 let (key, value) = config.into_key_value();
2107 self.configs.set(key, Some(value), self.op_id)?;
2108 }
2109 None => {
2110 self.configs.set(ConfigKey { key }, None, self.op_id)?;
2111 }
2112 }
2113 Ok(())
2114 }
2115
2116 pub fn get_config(&self, key: String) -> Option<u64> {
2118 self.configs
2119 .get(&ConfigKey { key })
2120 .map(|entry| entry.value)
2121 }
2122
2123 pub fn get_setting(&self, name: String) -> Option<&str> {
2125 self.settings
2126 .get(&SettingKey { name })
2127 .map(|entry| &*entry.value)
2128 }
2129
2130 pub fn get_builtin_migration_shard(&self) -> Option<ShardId> {
2131 self.get_setting(BUILTIN_MIGRATION_SHARD_KEY.to_string())
2132 .map(|shard_id| shard_id.parse().expect("valid ShardId"))
2133 }
2134
2135 pub fn set_builtin_migration_shard(&mut self, shard_id: ShardId) -> Result<(), CatalogError> {
2136 self.set_setting(
2137 BUILTIN_MIGRATION_SHARD_KEY.to_string(),
2138 Some(shard_id.to_string()),
2139 )
2140 }
2141
2142 pub fn get_expression_cache_shard(&self) -> Option<ShardId> {
2143 self.get_setting(EXPRESSION_CACHE_SHARD_KEY.to_string())
2144 .map(|shard_id| shard_id.parse().expect("valid ShardId"))
2145 }
2146
2147 pub fn set_expression_cache_shard(&mut self, shard_id: ShardId) -> Result<(), CatalogError> {
2148 self.set_setting(
2149 EXPRESSION_CACHE_SHARD_KEY.to_string(),
2150 Some(shard_id.to_string()),
2151 )
2152 }
2153
2154 pub fn set_0dt_deployment_max_wait(&mut self, value: Duration) -> Result<(), CatalogError> {
2160 self.set_config(
2161 WITH_0DT_DEPLOYMENT_MAX_WAIT.into(),
2162 Some(
2163 value
2164 .as_millis()
2165 .try_into()
2166 .expect("max wait fits into u64"),
2167 ),
2168 )
2169 }
2170
2171 pub fn set_0dt_deployment_ddl_check_interval(
2178 &mut self,
2179 value: Duration,
2180 ) -> Result<(), CatalogError> {
2181 self.set_config(
2182 WITH_0DT_DEPLOYMENT_DDL_CHECK_INTERVAL.into(),
2183 Some(
2184 value
2185 .as_millis()
2186 .try_into()
2187 .expect("ddl check interval fits into u64"),
2188 ),
2189 )
2190 }
2191
2192 pub fn set_enable_0dt_deployment_panic_after_timeout(
2198 &mut self,
2199 value: bool,
2200 ) -> Result<(), CatalogError> {
2201 self.set_config(
2202 ENABLE_0DT_DEPLOYMENT_PANIC_AFTER_TIMEOUT.into(),
2203 Some(u64::from(value)),
2204 )
2205 }
2206
2207 pub fn reset_0dt_deployment_max_wait(&mut self) -> Result<(), CatalogError> {
2213 self.set_config(WITH_0DT_DEPLOYMENT_MAX_WAIT.into(), None)
2214 }
2215
2216 pub fn reset_0dt_deployment_ddl_check_interval(&mut self) -> Result<(), CatalogError> {
2223 self.set_config(WITH_0DT_DEPLOYMENT_DDL_CHECK_INTERVAL.into(), None)
2224 }
2225
2226 pub fn reset_enable_0dt_deployment_panic_after_timeout(&mut self) -> Result<(), CatalogError> {
2233 self.set_config(ENABLE_0DT_DEPLOYMENT_PANIC_AFTER_TIMEOUT.into(), None)
2234 }
2235
2236 pub fn set_system_config_synced_once(&mut self) -> Result<(), CatalogError> {
2238 self.set_config(SYSTEM_CONFIG_SYNCED_KEY.into(), Some(1))
2239 }
2240
2241 pub fn update_comment(
2242 &mut self,
2243 object_id: CommentObjectId,
2244 sub_component: Option<usize>,
2245 comment: Option<String>,
2246 ) -> Result<(), CatalogError> {
2247 let key = CommentKey {
2248 object_id,
2249 sub_component,
2250 };
2251 let value = comment.map(|c| CommentValue { comment: c });
2252 self.comments.set(key, value, self.op_id)?;
2253
2254 Ok(())
2255 }
2256
2257 pub fn drop_comments(
2258 &mut self,
2259 object_ids: &BTreeSet<CommentObjectId>,
2260 ) -> Result<(), CatalogError> {
2261 if object_ids.is_empty() {
2262 return Ok(());
2263 }
2264
2265 self.comments
2266 .delete(|k, _v| object_ids.contains(&k.object_id), self.op_id);
2267 Ok(())
2268 }
2269
2270 pub fn update_source_references(
2271 &mut self,
2272 source_id: CatalogItemId,
2273 references: Vec<SourceReference>,
2274 updated_at: u64,
2275 ) -> Result<(), CatalogError> {
2276 let key = SourceReferencesKey { source_id };
2277 let value = SourceReferencesValue {
2278 references,
2279 updated_at,
2280 };
2281 self.source_references.set(key, Some(value), self.op_id)?;
2282 Ok(())
2283 }
2284
2285 pub fn upsert_system_config(&mut self, name: &str, value: String) -> Result<(), CatalogError> {
2287 let key = ServerConfigurationKey {
2288 name: name.to_string(),
2289 };
2290 let value = ServerConfigurationValue { value };
2291 self.system_configurations
2292 .set(key, Some(value), self.op_id)?;
2293 Ok(())
2294 }
2295
2296 pub fn remove_system_config(&mut self, name: &str) {
2298 let key = ServerConfigurationKey {
2299 name: name.to_string(),
2300 };
2301 self.system_configurations
2302 .set(key, None, self.op_id)
2303 .expect("cannot have uniqueness violation");
2304 }
2305
2306 pub fn clear_system_configs(&mut self) {
2308 self.system_configurations.delete(|_k, _v| true, self.op_id);
2309 }
2310
2311 pub fn get_cluster_system_configurations(
2313 &self,
2314 ) -> impl Iterator<Item = ClusterSystemConfiguration> + use<'_> {
2315 self.cluster_system_configurations
2316 .items()
2317 .into_iter()
2318 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2319 }
2320
2321 pub fn upsert_cluster_system_config(
2324 &mut self,
2325 cluster_id: ClusterId,
2326 name: &str,
2327 value: String,
2328 ) -> Result<(), CatalogError> {
2329 let key = ClusterSystemConfigurationKey {
2330 cluster_id,
2331 name: name.to_string(),
2332 };
2333 let value = ClusterSystemConfigurationValue { value };
2334 self.cluster_system_configurations
2335 .set(key, Some(value), self.op_id)?;
2336 Ok(())
2337 }
2338
2339 pub fn remove_cluster_system_config(&mut self, cluster_id: ClusterId, name: &str) {
2342 let key = ClusterSystemConfigurationKey {
2343 cluster_id,
2344 name: name.to_string(),
2345 };
2346 self.cluster_system_configurations
2347 .set(key, None, self.op_id)
2348 .expect("cannot have uniqueness violation");
2349 }
2350
2351 pub fn get_replica_system_configurations(
2353 &self,
2354 ) -> impl Iterator<Item = ReplicaSystemConfiguration> + use<'_> {
2355 self.replica_system_configurations
2356 .items()
2357 .into_iter()
2358 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2359 }
2360
2361 pub fn upsert_replica_system_config(
2364 &mut self,
2365 replica_id: ReplicaId,
2366 name: &str,
2367 value: String,
2368 ) -> Result<(), CatalogError> {
2369 let key = ReplicaSystemConfigurationKey {
2370 replica_id,
2371 name: name.to_string(),
2372 };
2373 let value = ReplicaSystemConfigurationValue { value };
2374 self.replica_system_configurations
2375 .set(key, Some(value), self.op_id)?;
2376 Ok(())
2377 }
2378
2379 pub fn remove_replica_system_config(&mut self, replica_id: ReplicaId, name: &str) {
2382 let key = ReplicaSystemConfigurationKey {
2383 replica_id,
2384 name: name.to_string(),
2385 };
2386 self.replica_system_configurations
2387 .set(key, None, self.op_id)
2388 .expect("cannot have uniqueness violation");
2389 }
2390
2391 pub(crate) fn insert_config(&mut self, key: String, value: u64) -> Result<(), CatalogError> {
2392 match self.configs.insert(
2393 ConfigKey { key: key.clone() },
2394 ConfigValue { value },
2395 self.op_id,
2396 ) {
2397 Ok(_) => Ok(()),
2398 Err(_) => Err(SqlCatalogError::ConfigAlreadyExists(key).into()),
2399 }
2400 }
2401
2402 pub fn get_clusters(&self) -> impl Iterator<Item = Cluster> + use<'_> {
2403 self.clusters
2404 .items()
2405 .into_iter()
2406 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2407 }
2408
2409 pub fn get_cluster_replicas(&self) -> impl Iterator<Item = ClusterReplica> + use<'_> {
2410 self.cluster_replicas
2411 .items()
2412 .into_iter()
2413 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2414 }
2415
2416 pub fn get_databases(&self) -> impl Iterator<Item = Database> + use<'_> {
2417 self.databases
2418 .items()
2419 .into_iter()
2420 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2421 }
2422
2423 pub fn get_roles(&self) -> impl Iterator<Item = Role> + use<'_> {
2424 self.roles
2425 .items()
2426 .into_iter()
2427 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2428 }
2429
2430 pub fn get_network_policies(&self) -> impl Iterator<Item = NetworkPolicy> + use<'_> {
2431 self.network_policies
2432 .items()
2433 .into_iter()
2434 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2435 }
2436
2437 pub fn get_system_object_mappings(
2438 &self,
2439 ) -> impl Iterator<Item = SystemObjectMapping> + use<'_> {
2440 self.system_gid_mapping
2441 .items()
2442 .into_iter()
2443 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2444 }
2445
2446 pub fn get_schemas(&self) -> impl Iterator<Item = Schema> + use<'_> {
2447 self.schemas
2448 .items()
2449 .into_iter()
2450 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2451 }
2452
2453 pub fn get_system_configurations(&self) -> impl Iterator<Item = SystemConfiguration> + use<'_> {
2454 self.system_configurations
2455 .items()
2456 .into_iter()
2457 .map(|(k, v)| DurableType::from_key_value(k.clone(), v.clone()))
2458 }
2459
2460 pub fn get_schema(&self, id: &SchemaId) -> Option<Schema> {
2461 let key = SchemaKey { id: *id };
2462 self.schemas
2463 .get(&key)
2464 .map(|v| DurableType::from_key_value(key, v.clone()))
2465 }
2466
2467 pub fn get_introspection_source_indexes(
2468 &self,
2469 cluster_id: ClusterId,
2470 ) -> BTreeMap<&str, (GlobalId, u32)> {
2471 self.introspection_sources
2472 .items()
2473 .into_iter()
2474 .filter(|(k, _v)| k.cluster_id == cluster_id)
2475 .map(|(k, v)| (k.name.as_str(), (v.global_id.into(), v.oid)))
2476 .collect()
2477 }
2478
2479 pub fn get_catalog_content_version(&self) -> Option<&str> {
2480 self.settings
2481 .get(&SettingKey {
2482 name: CATALOG_CONTENT_VERSION_KEY.to_string(),
2483 })
2484 .map(|value| &*value.value)
2485 }
2486
2487 pub fn get_authentication_mock_nonce(&self) -> Option<String> {
2488 self.settings
2489 .get(&SettingKey {
2490 name: MOCK_AUTHENTICATION_NONCE_KEY.to_string(),
2491 })
2492 .map(|value| value.value.clone())
2493 }
2494
2495 #[must_use]
2501 pub fn get_and_commit_op_updates(&mut self) -> Vec<StateUpdate> {
2502 let updates = self.get_op_updates();
2503 self.commit_op();
2504 updates
2505 }
2506
2507 fn get_op_updates(&self) -> Vec<StateUpdate> {
2508 fn get_collection_op_updates<'a, T>(
2509 table_txn: &'a TableTransaction<T::Key, T::Value>,
2510 kind_fn: impl Fn(T) -> StateUpdateKind + 'a,
2511 op: Timestamp,
2512 ) -> impl Iterator<Item = (StateUpdateKind, StateDiff)> + 'a
2513 where
2514 T::Key: Ord + Eq + Clone + Debug,
2515 T::Value: Ord + Clone + Debug,
2516 T: DurableType,
2517 {
2518 table_txn
2519 .pending
2520 .iter()
2521 .flat_map(|(k, vs)| vs.into_iter().map(move |v| (k, v)))
2522 .filter_map(move |(k, v)| {
2523 if v.ts == op {
2524 let key = k.clone();
2525 let value = v.value.clone();
2526 let diff = v.diff.clone().try_into().expect("invalid diff");
2527 let update = DurableType::from_key_value(key, value);
2528 let kind = kind_fn(update);
2529 Some((kind, diff))
2530 } else {
2531 None
2532 }
2533 })
2534 }
2535
2536 fn get_large_collection_op_updates<'a, T>(
2537 collection: &'a Vec<(T::Key, Diff, Timestamp)>,
2538 kind_fn: impl Fn(T) -> StateUpdateKind + 'a,
2539 op: Timestamp,
2540 ) -> impl Iterator<Item = (StateUpdateKind, StateDiff)> + 'a
2541 where
2542 T::Key: Ord + Eq + Clone + Debug,
2543 T: DurableType<Value = ()>,
2544 {
2545 collection.iter().filter_map(move |(k, diff, ts)| {
2546 if *ts == op {
2547 let key = k.clone();
2548 let diff = diff.clone().try_into().expect("invalid diff");
2549 let update = DurableType::from_key_value(key, ());
2550 let kind = kind_fn(update);
2551 Some((kind, diff))
2552 } else {
2553 None
2554 }
2555 })
2556 }
2557
2558 let Transaction {
2559 durable_catalog: _,
2560 databases,
2561 schemas,
2562 items,
2563 comments,
2564 roles,
2565 role_auth,
2566 clusters,
2567 network_policies,
2568 cluster_replicas,
2569 introspection_sources,
2570 system_gid_mapping,
2571 system_configurations,
2572 cluster_system_configurations,
2573 replica_system_configurations,
2574 default_privileges,
2575 source_references,
2576 system_privileges,
2577 audit_log_updates,
2578 storage_collection_metadata,
2579 unfinalized_shards,
2580 id_allocator: _,
2582 configs: _,
2583 settings: _,
2584 txn_wal_shard: _,
2585 upper,
2586 op_id: _,
2587 commit_capability: _,
2588 } = &self;
2589
2590 let updates = std::iter::empty()
2591 .chain(get_collection_op_updates(
2592 roles,
2593 StateUpdateKind::Role,
2594 self.op_id,
2595 ))
2596 .chain(get_collection_op_updates(
2597 role_auth,
2598 StateUpdateKind::RoleAuth,
2599 self.op_id,
2600 ))
2601 .chain(get_collection_op_updates(
2602 databases,
2603 StateUpdateKind::Database,
2604 self.op_id,
2605 ))
2606 .chain(get_collection_op_updates(
2607 schemas,
2608 StateUpdateKind::Schema,
2609 self.op_id,
2610 ))
2611 .chain(get_collection_op_updates(
2612 default_privileges,
2613 StateUpdateKind::DefaultPrivilege,
2614 self.op_id,
2615 ))
2616 .chain(get_collection_op_updates(
2617 system_privileges,
2618 StateUpdateKind::SystemPrivilege,
2619 self.op_id,
2620 ))
2621 .chain(get_collection_op_updates(
2622 system_configurations,
2623 StateUpdateKind::SystemConfiguration,
2624 self.op_id,
2625 ))
2626 .chain(get_collection_op_updates(
2627 cluster_system_configurations,
2628 StateUpdateKind::ClusterSystemConfiguration,
2629 self.op_id,
2630 ))
2631 .chain(get_collection_op_updates(
2632 replica_system_configurations,
2633 StateUpdateKind::ReplicaSystemConfiguration,
2634 self.op_id,
2635 ))
2636 .chain(get_collection_op_updates(
2637 clusters,
2638 StateUpdateKind::Cluster,
2639 self.op_id,
2640 ))
2641 .chain(get_collection_op_updates(
2642 network_policies,
2643 StateUpdateKind::NetworkPolicy,
2644 self.op_id,
2645 ))
2646 .chain(get_collection_op_updates(
2647 introspection_sources,
2648 StateUpdateKind::IntrospectionSourceIndex,
2649 self.op_id,
2650 ))
2651 .chain(get_collection_op_updates(
2652 cluster_replicas,
2653 StateUpdateKind::ClusterReplica,
2654 self.op_id,
2655 ))
2656 .chain(get_collection_op_updates(
2657 system_gid_mapping,
2658 StateUpdateKind::SystemObjectMapping,
2659 self.op_id,
2660 ))
2661 .chain(get_collection_op_updates(
2662 items,
2663 StateUpdateKind::Item,
2664 self.op_id,
2665 ))
2666 .chain(get_collection_op_updates(
2667 comments,
2668 StateUpdateKind::Comment,
2669 self.op_id,
2670 ))
2671 .chain(get_collection_op_updates(
2672 source_references,
2673 StateUpdateKind::SourceReferences,
2674 self.op_id,
2675 ))
2676 .chain(get_collection_op_updates(
2677 storage_collection_metadata,
2678 StateUpdateKind::StorageCollectionMetadata,
2679 self.op_id,
2680 ))
2681 .chain(get_collection_op_updates(
2682 unfinalized_shards,
2683 StateUpdateKind::UnfinalizedShard,
2684 self.op_id,
2685 ))
2686 .chain(get_large_collection_op_updates(
2687 audit_log_updates,
2688 StateUpdateKind::AuditLog,
2689 self.op_id,
2690 ))
2691 .map(|(kind, diff)| StateUpdate {
2692 kind,
2693 ts: upper.clone(),
2694 diff,
2695 })
2696 .collect();
2697
2698 updates
2699 }
2700
2701 pub fn is_savepoint(&self) -> bool {
2702 self.durable_catalog.is_savepoint()
2703 }
2704
2705 fn commit_op(&mut self) {
2706 self.op_id += 1;
2707 }
2708
2709 pub fn op_id(&self) -> Timestamp {
2710 self.op_id
2711 }
2712
2713 pub fn upper(&self) -> mz_repr::Timestamp {
2714 self.upper
2715 }
2716
2717 fn ensure_committable(&self) -> Result<(), CatalogError> {
2718 match self.commit_capability {
2719 Some(_) => Ok(()),
2720 None => Err(DurableCatalogError::DryRunTransaction.into()),
2721 }
2722 }
2723
2724 pub(super) async fn ensure_not_out_of_sync(&mut self) -> Result<(), CatalogError> {
2726 self.durable_catalog
2727 .ensure_not_out_of_sync(self.upper)
2728 .await
2729 }
2730
2731 pub(crate) fn into_parts(
2732 self,
2733 ) -> Result<(TransactionBatch, &'a mut dyn DurableCatalogState), CatalogError> {
2734 let commit_capability = self
2735 .commit_capability
2736 .ok_or(DurableCatalogError::DryRunTransaction)?;
2737 let audit_log_updates = self
2738 .audit_log_updates
2739 .into_iter()
2740 .map(|(k, diff, _op)| (k.into_proto(), (), diff))
2741 .collect();
2742
2743 let txn_batch = TransactionBatch {
2744 databases: self.databases.pending(),
2745 schemas: self.schemas.pending(),
2746 items: self.items.pending(),
2747 comments: self.comments.pending(),
2748 roles: self.roles.pending(),
2749 role_auth: self.role_auth.pending(),
2750 clusters: self.clusters.pending(),
2751 cluster_replicas: self.cluster_replicas.pending(),
2752 network_policies: self.network_policies.pending(),
2753 introspection_sources: self.introspection_sources.pending(),
2754 id_allocator: self.id_allocator.pending(),
2755 configs: self.configs.pending(),
2756 source_references: self.source_references.pending(),
2757 settings: self.settings.pending(),
2758 system_gid_mapping: self.system_gid_mapping.pending(),
2759 system_configurations: self.system_configurations.pending(),
2760 cluster_system_configurations: self.cluster_system_configurations.pending(),
2761 replica_system_configurations: self.replica_system_configurations.pending(),
2762 default_privileges: self.default_privileges.pending(),
2763 system_privileges: self.system_privileges.pending(),
2764 storage_collection_metadata: self.storage_collection_metadata.pending(),
2765 unfinalized_shards: self.unfinalized_shards.pending(),
2766 txn_wal_shard: self.txn_wal_shard.pending(),
2767 audit_log_updates,
2768 upper: self.upper,
2769 _commit_capability: commit_capability,
2770 };
2771 Ok((txn_batch, self.durable_catalog))
2772 }
2773
2774 #[mz_ore::instrument(level = "debug")]
2790 pub(crate) async fn commit_internal(
2791 self,
2792 commit_ts: mz_repr::Timestamp,
2793 ) -> Result<(&'a mut dyn DurableCatalogState, mz_repr::Timestamp), CatalogError> {
2794 self.ensure_committable()?;
2795 let (mut txn_batch, durable_catalog) = self.into_parts()?;
2796 let TransactionBatch {
2797 databases,
2798 schemas,
2799 items,
2800 comments,
2801 roles,
2802 role_auth,
2803 clusters,
2804 cluster_replicas,
2805 network_policies,
2806 introspection_sources,
2807 id_allocator,
2808 configs,
2809 source_references,
2810 settings,
2811 system_gid_mapping,
2812 system_configurations,
2813 cluster_system_configurations,
2814 replica_system_configurations,
2815 default_privileges,
2816 system_privileges,
2817 storage_collection_metadata,
2818 unfinalized_shards,
2819 txn_wal_shard,
2820 audit_log_updates,
2821 upper: _,
2822 _commit_capability: _,
2823 } = &mut txn_batch;
2824 differential_dataflow::consolidation::consolidate_updates(databases);
2827 differential_dataflow::consolidation::consolidate_updates(schemas);
2828 differential_dataflow::consolidation::consolidate_updates(items);
2829 differential_dataflow::consolidation::consolidate_updates(comments);
2830 differential_dataflow::consolidation::consolidate_updates(roles);
2831 differential_dataflow::consolidation::consolidate_updates(role_auth);
2832 differential_dataflow::consolidation::consolidate_updates(clusters);
2833 differential_dataflow::consolidation::consolidate_updates(cluster_replicas);
2834 differential_dataflow::consolidation::consolidate_updates(network_policies);
2835 differential_dataflow::consolidation::consolidate_updates(introspection_sources);
2836 differential_dataflow::consolidation::consolidate_updates(id_allocator);
2837 differential_dataflow::consolidation::consolidate_updates(configs);
2838 differential_dataflow::consolidation::consolidate_updates(settings);
2839 differential_dataflow::consolidation::consolidate_updates(source_references);
2840 differential_dataflow::consolidation::consolidate_updates(system_gid_mapping);
2841 differential_dataflow::consolidation::consolidate_updates(system_configurations);
2842 differential_dataflow::consolidation::consolidate_updates(cluster_system_configurations);
2843 differential_dataflow::consolidation::consolidate_updates(replica_system_configurations);
2844 differential_dataflow::consolidation::consolidate_updates(default_privileges);
2845 differential_dataflow::consolidation::consolidate_updates(system_privileges);
2846 differential_dataflow::consolidation::consolidate_updates(storage_collection_metadata);
2847 differential_dataflow::consolidation::consolidate_updates(unfinalized_shards);
2848 differential_dataflow::consolidation::consolidate_updates(txn_wal_shard);
2849 differential_dataflow::consolidation::consolidate_updates(audit_log_updates);
2850
2851 let upper = durable_catalog
2852 .commit_transaction(txn_batch, commit_ts)
2853 .await?;
2854 Ok((durable_catalog, upper))
2855 }
2856
2857 #[mz_ore::instrument(level = "debug")]
2878 pub async fn commit(self, commit_ts: mz_repr::Timestamp) -> Result<(), CatalogError> {
2879 self.ensure_committable()?;
2880 let op_updates = self.get_op_updates();
2881 assert!(
2882 op_updates.is_empty(),
2883 "unconsumed transaction updates: {op_updates:?}"
2884 );
2885
2886 let (durable_storage, upper) = self.commit_internal(commit_ts).await?;
2887 let updates = durable_storage.sync_updates(upper).await?;
2889 soft_assert_no_log!(
2898 durable_storage.is_read_only()
2899 || updates
2900 .iter()
2901 .all(|update| update.ts >= commit_ts && update.ts < upper),
2902 "unconsumed updates existed before transaction commit: commit_ts={commit_ts:?}, upper={upper:?}, updates:{updates:?}"
2903 );
2904 Ok(())
2905 }
2906}
2907
2908use crate::durable::async_trait;
2909
2910use super::objects::{RoleAuthKey, RoleAuthValue};
2911
2912#[async_trait]
2913impl StorageTxn for Transaction<'_> {
2914 fn get_collection_metadata(&self) -> BTreeMap<GlobalId, ShardId> {
2915 self.storage_collection_metadata
2916 .items()
2917 .into_iter()
2918 .map(
2919 |(
2920 StorageCollectionMetadataKey { id },
2921 StorageCollectionMetadataValue { shard },
2922 )| { (*id, shard.clone()) },
2923 )
2924 .collect()
2925 }
2926
2927 fn insert_collection_metadata(
2928 &mut self,
2929 metadata: BTreeMap<GlobalId, ShardId>,
2930 ) -> Result<(), StorageError> {
2931 for (id, shard) in metadata {
2932 self.storage_collection_metadata
2933 .insert(
2934 StorageCollectionMetadataKey { id },
2935 StorageCollectionMetadataValue {
2936 shard: shard.clone(),
2937 },
2938 self.op_id,
2939 )
2940 .map_err(|err| match err {
2941 DurableCatalogError::DuplicateKey => {
2942 StorageError::CollectionMetadataAlreadyExists(id)
2943 }
2944 DurableCatalogError::UniquenessViolation => {
2945 StorageError::PersistShardAlreadyInUse(shard)
2946 }
2947 err => StorageError::Generic(anyhow::anyhow!(err)),
2948 })?;
2949 }
2950 Ok(())
2951 }
2952
2953 fn delete_collection_metadata(&mut self, ids: BTreeSet<GlobalId>) -> Vec<(GlobalId, ShardId)> {
2954 let ks: Vec<_> = ids
2955 .into_iter()
2956 .map(|id| StorageCollectionMetadataKey { id })
2957 .collect();
2958 self.storage_collection_metadata
2959 .delete_by_keys(ks, self.op_id)
2960 .into_iter()
2961 .map(
2962 |(
2963 StorageCollectionMetadataKey { id },
2964 StorageCollectionMetadataValue { shard },
2965 )| (id, shard),
2966 )
2967 .collect()
2968 }
2969
2970 fn get_unfinalized_shards(&self) -> BTreeSet<ShardId> {
2971 self.unfinalized_shards
2972 .items()
2973 .into_iter()
2974 .map(|(UnfinalizedShardKey { shard }, ())| *shard)
2975 .collect()
2976 }
2977
2978 fn insert_unfinalized_shards(&mut self, s: BTreeSet<ShardId>) -> Result<(), StorageError> {
2979 for shard in s {
2980 match self
2981 .unfinalized_shards
2982 .insert(UnfinalizedShardKey { shard }, (), self.op_id)
2983 {
2984 Ok(()) | Err(DurableCatalogError::DuplicateKey) => {}
2986 Err(e) => Err(StorageError::Generic(anyhow::anyhow!(e)))?,
2987 };
2988 }
2989 Ok(())
2990 }
2991
2992 fn remove_unfinalized_shards(&mut self, shards: BTreeSet<ShardId>) {
2993 let ks: Vec<_> = shards
2994 .into_iter()
2995 .map(|shard| UnfinalizedShardKey { shard })
2996 .collect();
2997 let _ = self.unfinalized_shards.delete_by_keys(ks, self.op_id);
2998 }
2999
3000 fn get_txn_wal_shard(&self) -> Option<ShardId> {
3001 self.txn_wal_shard
3002 .values()
3003 .iter()
3004 .next()
3005 .map(|TxnWalShardValue { shard }| *shard)
3006 }
3007
3008 fn write_txn_wal_shard(&mut self, shard: ShardId) -> Result<(), StorageError> {
3009 self.txn_wal_shard
3010 .insert((), TxnWalShardValue { shard }, self.op_id)
3011 .map_err(|err| match err {
3012 DurableCatalogError::DuplicateKey => StorageError::TxnWalShardAlreadyExists,
3013 err => StorageError::Generic(anyhow::anyhow!(err)),
3014 })
3015 }
3016}
3017
3018#[derive(Debug, PartialEq)]
3020pub struct TransactionBatch {
3021 pub(crate) databases: Vec<(proto::DatabaseKey, proto::DatabaseValue, Diff)>,
3022 pub(crate) schemas: Vec<(proto::SchemaKey, proto::SchemaValue, Diff)>,
3023 pub(crate) items: Vec<(proto::ItemKey, proto::ItemValue, Diff)>,
3024 pub(crate) comments: Vec<(proto::CommentKey, proto::CommentValue, Diff)>,
3025 pub(crate) roles: Vec<(proto::RoleKey, proto::RoleValue, Diff)>,
3026 pub(crate) role_auth: Vec<(proto::RoleAuthKey, proto::RoleAuthValue, Diff)>,
3027 pub(crate) clusters: Vec<(proto::ClusterKey, proto::ClusterValue, Diff)>,
3028 pub(crate) cluster_replicas: Vec<(proto::ClusterReplicaKey, proto::ClusterReplicaValue, Diff)>,
3029 pub(crate) network_policies: Vec<(proto::NetworkPolicyKey, proto::NetworkPolicyValue, Diff)>,
3030 pub(crate) introspection_sources: Vec<(
3031 proto::ClusterIntrospectionSourceIndexKey,
3032 proto::ClusterIntrospectionSourceIndexValue,
3033 Diff,
3034 )>,
3035 pub(crate) id_allocator: Vec<(proto::IdAllocKey, proto::IdAllocValue, Diff)>,
3036 pub(crate) configs: Vec<(proto::ConfigKey, proto::ConfigValue, Diff)>,
3037 pub(crate) settings: Vec<(proto::SettingKey, proto::SettingValue, Diff)>,
3038 pub(crate) system_gid_mapping: Vec<(proto::GidMappingKey, proto::GidMappingValue, Diff)>,
3039 pub(crate) system_configurations: Vec<(
3040 proto::ServerConfigurationKey,
3041 proto::ServerConfigurationValue,
3042 Diff,
3043 )>,
3044 pub(crate) cluster_system_configurations: Vec<(
3045 proto::ClusterSystemConfigurationKey,
3046 proto::ClusterSystemConfigurationValue,
3047 Diff,
3048 )>,
3049 pub(crate) replica_system_configurations: Vec<(
3050 proto::ReplicaSystemConfigurationKey,
3051 proto::ReplicaSystemConfigurationValue,
3052 Diff,
3053 )>,
3054 pub(crate) default_privileges: Vec<(
3055 proto::DefaultPrivilegesKey,
3056 proto::DefaultPrivilegesValue,
3057 Diff,
3058 )>,
3059 pub(crate) source_references: Vec<(
3060 proto::SourceReferencesKey,
3061 proto::SourceReferencesValue,
3062 Diff,
3063 )>,
3064 pub(crate) system_privileges: Vec<(
3065 proto::SystemPrivilegesKey,
3066 proto::SystemPrivilegesValue,
3067 Diff,
3068 )>,
3069 pub(crate) storage_collection_metadata: Vec<(
3070 proto::StorageCollectionMetadataKey,
3071 proto::StorageCollectionMetadataValue,
3072 Diff,
3073 )>,
3074 pub(crate) unfinalized_shards: Vec<(proto::UnfinalizedShardKey, (), Diff)>,
3075 pub(crate) txn_wal_shard: Vec<((), proto::TxnWalShardValue, Diff)>,
3076 pub(crate) audit_log_updates: Vec<(proto::AuditLogKey, (), Diff)>,
3077 pub(crate) upper: mz_repr::Timestamp,
3079 _commit_capability: CommitCapability,
3082}
3083
3084impl TransactionBatch {
3085 pub fn is_empty(&self) -> bool {
3086 let TransactionBatch {
3087 databases,
3088 schemas,
3089 items,
3090 comments,
3091 roles,
3092 role_auth,
3093 clusters,
3094 cluster_replicas,
3095 network_policies,
3096 introspection_sources,
3097 id_allocator,
3098 configs,
3099 settings,
3100 source_references,
3101 system_gid_mapping,
3102 system_configurations,
3103 cluster_system_configurations,
3104 replica_system_configurations,
3105 default_privileges,
3106 system_privileges,
3107 storage_collection_metadata,
3108 unfinalized_shards,
3109 txn_wal_shard,
3110 audit_log_updates,
3111 upper: _,
3112 _commit_capability: _,
3113 } = self;
3114 databases.is_empty()
3115 && schemas.is_empty()
3116 && items.is_empty()
3117 && comments.is_empty()
3118 && roles.is_empty()
3119 && role_auth.is_empty()
3120 && clusters.is_empty()
3121 && cluster_replicas.is_empty()
3122 && network_policies.is_empty()
3123 && introspection_sources.is_empty()
3124 && id_allocator.is_empty()
3125 && configs.is_empty()
3126 && settings.is_empty()
3127 && source_references.is_empty()
3128 && system_gid_mapping.is_empty()
3129 && system_configurations.is_empty()
3130 && cluster_system_configurations.is_empty()
3131 && replica_system_configurations.is_empty()
3132 && default_privileges.is_empty()
3133 && system_privileges.is_empty()
3134 && storage_collection_metadata.is_empty()
3135 && unfinalized_shards.is_empty()
3136 && txn_wal_shard.is_empty()
3137 && audit_log_updates.is_empty()
3138 }
3139}
3140
3141#[derive(Debug, Clone, PartialEq, Eq)]
3142struct TransactionUpdate<V> {
3143 value: V,
3144 ts: Timestamp,
3145 diff: Diff,
3146}
3147
3148trait UniqueName {
3150 const HAS_UNIQUE_NAME: bool;
3153 fn unique_name(&self) -> &str;
3155}
3156
3157mod unique_name {
3158 use crate::durable::objects::*;
3159
3160 macro_rules! impl_unique_name {
3161 ($($t:ty),* $(,)?) => {
3162 $(
3163 impl crate::durable::transaction::UniqueName for $t {
3164 const HAS_UNIQUE_NAME: bool = true;
3165 fn unique_name(&self) -> &str {
3166 &self.name
3167 }
3168 }
3169 )*
3170 };
3171 }
3172
3173 macro_rules! impl_no_unique_name {
3174 ($($t:ty),* $(,)?) => {
3175 $(
3176 impl crate::durable::transaction::UniqueName for $t {
3177 const HAS_UNIQUE_NAME: bool = false;
3178 fn unique_name(&self) -> &str {
3179 ""
3180 }
3181 }
3182 )*
3183 };
3184 }
3185
3186 impl_unique_name! {
3187 ClusterReplicaValue,
3188 ClusterValue,
3189 DatabaseValue,
3190 ItemValue,
3191 NetworkPolicyValue,
3192 RoleValue,
3193 SchemaValue,
3194 }
3195
3196 impl_no_unique_name!(
3197 (),
3198 ClusterIntrospectionSourceIndexValue,
3199 ClusterSystemConfigurationValue,
3200 CommentValue,
3201 ConfigValue,
3202 DefaultPrivilegesValue,
3203 GidMappingValue,
3204 IdAllocValue,
3205 ReplicaSystemConfigurationValue,
3206 ServerConfigurationValue,
3207 SettingValue,
3208 SourceReferencesValue,
3209 StorageCollectionMetadataValue,
3210 SystemPrivilegesValue,
3211 TxnWalShardValue,
3212 RoleAuthValue,
3213 );
3214
3215 #[cfg(test)]
3216 mod test {
3217 impl_no_unique_name!(String,);
3218 }
3219}
3220
3221#[derive(Debug)]
3229struct UniquenessCheck<V> {
3230 violation: fn(a: &V, b: &V) -> bool,
3231 is_unique_key_unchanged_after_update: fn(prev: &V, next: &V) -> bool,
3232}
3233
3234#[derive(Debug)]
3244struct TableTransaction<K, V> {
3245 initial: BTreeMap<K, V>,
3246 pending: BTreeMap<K, Vec<TransactionUpdate<V>>>,
3249 uniqueness_check: Option<UniquenessCheck<V>>,
3251}
3252
3253impl<K, V> TableTransaction<K, V>
3254where
3255 K: Ord + Eq + Clone + Debug,
3256 V: Ord + Clone + Debug + UniqueName,
3257{
3258 fn new<KP, VP>(initial: BTreeMap<KP, VP>) -> Result<Self, TryFromProtoError>
3265 where
3266 K: RustType<KP>,
3267 V: RustType<VP>,
3268 {
3269 let initial = initial
3270 .into_iter()
3271 .map(RustType::from_proto)
3272 .collect::<Result<_, _>>()?;
3273
3274 Ok(Self {
3275 initial,
3276 pending: BTreeMap::new(),
3277 uniqueness_check: None,
3278 })
3279 }
3280
3281 fn new_with_uniqueness_fn<KP, VP>(
3284 initial: BTreeMap<KP, VP>,
3285 uniqueness_violation: fn(a: &V, b: &V) -> bool,
3286 is_unique_key_unchanged_after_update: fn(prev: &V, next: &V) -> bool,
3287 ) -> Result<Self, TryFromProtoError>
3288 where
3289 K: RustType<KP>,
3290 V: RustType<VP>,
3291 {
3292 let initial = initial
3293 .into_iter()
3294 .map(RustType::from_proto)
3295 .collect::<Result<_, _>>()?;
3296
3297 Ok(Self {
3298 initial,
3299 pending: BTreeMap::new(),
3300 uniqueness_check: Some(UniquenessCheck {
3301 violation: uniqueness_violation,
3302 is_unique_key_unchanged_after_update,
3303 }),
3304 })
3305 }
3306
3307 fn pending<KP, VP>(self) -> Vec<(KP, VP, Diff)>
3310 where
3311 K: RustType<KP>,
3312 V: RustType<VP>,
3313 {
3314 soft_assert_no_log!(self.verify().is_ok());
3315 self.pending
3318 .into_iter()
3319 .flat_map(|(k, v)| {
3320 let mut v: Vec<_> = v
3321 .into_iter()
3322 .map(|TransactionUpdate { value, ts: _, diff }| (value, diff))
3323 .collect();
3324 differential_dataflow::consolidation::consolidate(&mut v);
3325 v.into_iter().map(move |(v, diff)| (k.clone(), v, diff))
3326 })
3327 .map(|(key, val, diff)| (key.into_proto(), val.into_proto(), diff))
3328 .collect()
3329 }
3330
3331 fn verify(&self) -> Result<(), DurableCatalogError> {
3336 if let Some(check) = &self.uniqueness_check {
3337 let items = self.values();
3339 if V::HAS_UNIQUE_NAME {
3340 let by_name: BTreeMap<_, _> = items
3341 .iter()
3342 .enumerate()
3343 .map(|(v, vi)| (vi.unique_name(), (v, vi)))
3344 .collect();
3345 for (i, vi) in items.iter().enumerate() {
3346 if let Some((j, vj)) = by_name.get(vi.unique_name()) {
3347 if i != *j && (check.violation)(vi, *vj) {
3348 return Err(DurableCatalogError::UniquenessViolation);
3349 }
3350 }
3351 }
3352 } else {
3353 for (i, vi) in items.iter().enumerate() {
3354 for (j, vj) in items.iter().enumerate() {
3355 if i != j && (check.violation)(vi, vj) {
3356 return Err(DurableCatalogError::UniquenessViolation);
3357 }
3358 }
3359 }
3360 }
3361 }
3362 soft_assert_no_log!(
3363 self.pending
3364 .values()
3365 .all(|pending| { pending.is_sorted_by(|a, b| a.ts <= b.ts) }),
3366 "pending should be sorted by timestamp: {:?}",
3367 self.pending
3368 );
3369 Ok(())
3370 }
3371
3372 fn verify_keys<'a>(
3377 &self,
3378 keys: impl IntoIterator<Item = &'a K>,
3379 ) -> Result<(), DurableCatalogError>
3380 where
3381 K: 'a,
3382 {
3383 if let Some(check) = &self.uniqueness_check {
3384 let entries: Vec<_> = keys
3385 .into_iter()
3386 .filter_map(|key| self.get(key).map(|value| (key, value)))
3387 .collect();
3388 for (ki, vi) in self.items() {
3390 for (kj, vj) in &entries {
3391 if ki != *kj && (check.violation)(vi, vj) {
3392 return Err(DurableCatalogError::UniquenessViolation);
3393 }
3394 }
3395 }
3396 }
3397 soft_assert_no_log!(self.verify().is_ok());
3398 Ok(())
3399 }
3400
3401 fn for_values<'a, F: FnMut(&'a K, &'a V)>(&'a self, mut f: F) {
3404 let mut seen = BTreeSet::new();
3405 for k in self.pending.keys() {
3406 seen.insert(k);
3407 let v = self.get(k);
3408 if let Some(v) = v {
3411 f(k, v);
3412 }
3413 }
3414 for (k, v) in self.initial.iter() {
3415 if !seen.contains(k) {
3417 f(k, v);
3418 }
3419 }
3420 }
3421
3422 fn get(&self, k: &K) -> Option<&V> {
3424 let pending = self.pending.get(k).map(Vec::as_slice).unwrap_or_default();
3425 let mut updates = Vec::with_capacity(pending.len() + 1);
3426 if let Some(initial) = self.initial.get(k) {
3427 updates.push((initial, Diff::ONE));
3428 }
3429 updates.extend(
3430 pending
3431 .into_iter()
3432 .map(|TransactionUpdate { value, ts: _, diff }| (value, *diff)),
3433 );
3434
3435 differential_dataflow::consolidation::consolidate(&mut updates);
3436 assert!(updates.len() <= 1);
3437 updates.into_iter().next().map(|(v, _)| v)
3438 }
3439
3440 #[cfg(test)]
3445 fn items_cloned(&self) -> BTreeMap<K, V> {
3446 let mut items = BTreeMap::new();
3447 self.for_values(|k, v| {
3448 items.insert(k.clone(), v.clone());
3449 });
3450 items
3451 }
3452
3453 fn current_items_proto<KP, VP>(&self) -> BTreeMap<KP, VP>
3457 where
3458 K: RustType<KP>,
3459 V: RustType<VP>,
3460 KP: Ord,
3461 {
3462 let mut items = BTreeMap::new();
3463 self.for_values(|k, v| {
3464 items.insert(k.into_proto(), v.into_proto());
3465 });
3466 items
3467 }
3468
3469 fn items(&self) -> BTreeMap<&K, &V> {
3472 let mut items = BTreeMap::new();
3473 self.for_values(|k, v| {
3474 items.insert(k, v);
3475 });
3476 items
3477 }
3478
3479 fn values(&self) -> BTreeSet<&V> {
3481 let mut items = BTreeSet::new();
3482 self.for_values(|_, v| {
3483 items.insert(v);
3484 });
3485 items
3486 }
3487
3488 fn len(&self) -> usize {
3490 let mut count = 0;
3491 self.for_values(|_, _| {
3492 count += 1;
3493 });
3494 count
3495 }
3496
3497 fn for_values_mut<F: FnMut(&mut BTreeMap<K, Vec<TransactionUpdate<V>>>, &K, &V)>(
3501 &mut self,
3502 mut f: F,
3503 ) {
3504 let mut pending = BTreeMap::new();
3505 self.for_values(|k, v| f(&mut pending, k, v));
3506 for (k, updates) in pending {
3507 self.pending.entry(k).or_default().extend(updates);
3508 }
3509 }
3510
3511 fn insert(&mut self, k: K, v: V, ts: Timestamp) -> Result<(), DurableCatalogError> {
3515 let mut violation = None;
3516 let uniqueness_violation = self.uniqueness_check.as_ref().map(|check| check.violation);
3517 self.for_values(|for_k, for_v| {
3518 if &k == for_k {
3519 violation = Some(DurableCatalogError::DuplicateKey);
3520 }
3521 if let Some(uniqueness_violation) = uniqueness_violation {
3522 if uniqueness_violation(for_v, &v) {
3523 violation = Some(DurableCatalogError::UniquenessViolation);
3524 }
3525 }
3526 });
3527 if let Some(violation) = violation {
3528 return Err(violation);
3529 }
3530 self.pending.entry(k).or_default().push(TransactionUpdate {
3531 value: v,
3532 ts,
3533 diff: Diff::ONE,
3534 });
3535 soft_assert_no_log!(self.verify().is_ok());
3536 Ok(())
3537 }
3538
3539 fn update<F: Fn(&K, &V) -> Option<V>>(
3548 &mut self,
3549 f: F,
3550 ts: Timestamp,
3551 ) -> Result<Diff, DurableCatalogError> {
3552 let mut changed = Diff::ZERO;
3553 let mut keys = BTreeSet::new();
3554 let pending = self.pending.clone();
3556 self.for_values_mut(|p, k, v| {
3557 if let Some(next) = f(k, v) {
3558 changed += Diff::ONE;
3559 keys.insert(k.clone());
3560 let updates = p.entry(k.clone()).or_default();
3561 updates.push(TransactionUpdate {
3562 value: v.clone(),
3563 ts,
3564 diff: Diff::MINUS_ONE,
3565 });
3566 updates.push(TransactionUpdate {
3567 value: next,
3568 ts,
3569 diff: Diff::ONE,
3570 });
3571 }
3572 });
3573 if let Err(err) = self.verify_keys(&keys) {
3575 self.pending = pending;
3576 Err(err)
3577 } else {
3578 Ok(changed)
3579 }
3580 }
3581
3582 fn update_by_key(&mut self, k: K, v: V, ts: Timestamp) -> Result<bool, DurableCatalogError> {
3587 if let Some(cur_v) = self.get(&k) {
3588 if v != *cur_v {
3589 self.set(k, Some(v), ts)?;
3590 }
3591 Ok(true)
3592 } else {
3593 Ok(false)
3594 }
3595 }
3596
3597 fn update_by_keys(
3602 &mut self,
3603 kvs: impl IntoIterator<Item = (K, V)>,
3604 ts: Timestamp,
3605 ) -> Result<Diff, DurableCatalogError> {
3606 let kvs: Vec<_> = kvs
3607 .into_iter()
3608 .filter_map(|(k, v)| match self.get(&k) {
3609 Some(cur_v) => Some((*cur_v == v, k, v)),
3611 None => None,
3612 })
3613 .collect();
3614 let changed = kvs.len();
3615 let changed =
3616 Diff::try_from(changed).map_err(|e| DurableCatalogError::Internal(e.to_string()))?;
3617 let kvs = kvs
3618 .into_iter()
3619 .filter(|(no_op, _, _)| !no_op)
3621 .map(|(_, k, v)| (k, Some(v)))
3622 .collect();
3623 self.set_many(kvs, ts)?;
3624 Ok(changed)
3625 }
3626
3627 fn update_needs_uniqueness_check(&self, prev: Option<&V>, next: Option<&V>) -> bool {
3629 match (&self.uniqueness_check, prev, next) {
3630 (None, _, _) | (_, _, None) => false,
3632 (Some(check), Some(prev), Some(next)) => {
3634 !(check.is_unique_key_unchanged_after_update)(prev, next)
3635 }
3636 (Some(_), None, Some(_)) => true,
3638 }
3639 }
3640
3641 fn set(&mut self, k: K, v: Option<V>, ts: Timestamp) -> Result<Option<V>, DurableCatalogError> {
3648 let prev = self.get(&k).cloned();
3649 let needs_uniqueness_check = self.update_needs_uniqueness_check(prev.as_ref(), v.as_ref());
3650 let entry = self.pending.entry(k.clone()).or_default();
3651 let restore_len = entry.len();
3652
3653 match (v, prev.clone()) {
3654 (Some(v), Some(prev)) => {
3655 entry.push(TransactionUpdate {
3656 value: prev,
3657 ts,
3658 diff: Diff::MINUS_ONE,
3659 });
3660 entry.push(TransactionUpdate {
3661 value: v,
3662 ts,
3663 diff: Diff::ONE,
3664 });
3665 }
3666 (Some(v), None) => {
3667 entry.push(TransactionUpdate {
3668 value: v,
3669 ts,
3670 diff: Diff::ONE,
3671 });
3672 }
3673 (None, Some(prev)) => {
3674 entry.push(TransactionUpdate {
3675 value: prev,
3676 ts,
3677 diff: Diff::MINUS_ONE,
3678 });
3679 }
3680 (None, None) => {}
3681 }
3682
3683 if needs_uniqueness_check {
3685 if let Err(err) = self.verify_keys([&k]) {
3686 let pending = self.pending.get_mut(&k).expect("inserted above");
3689 pending.truncate(restore_len);
3690 return Err(err);
3691 }
3692 }
3693 Ok(prev)
3694 }
3695
3696 fn set_many(
3701 &mut self,
3702 kvs: BTreeMap<K, Option<V>>,
3703 ts: Timestamp,
3704 ) -> Result<BTreeMap<K, Option<V>>, DurableCatalogError> {
3705 if kvs.is_empty() {
3706 return Ok(BTreeMap::new());
3707 }
3708
3709 let mut prevs = BTreeMap::new();
3710 let mut restores = BTreeMap::new();
3711 let mut keys_to_verify_uniqueness = Vec::new();
3713
3714 for (k, v) in kvs {
3715 let prev = self.get(&k).cloned();
3716 if self.update_needs_uniqueness_check(prev.as_ref(), v.as_ref()) {
3717 keys_to_verify_uniqueness.push(k.clone());
3718 }
3719 let entry = self.pending.entry(k.clone()).or_default();
3720 restores.insert(k.clone(), entry.len());
3721
3722 match (v, prev.clone()) {
3723 (Some(v), Some(prev)) => {
3724 entry.push(TransactionUpdate {
3725 value: prev,
3726 ts,
3727 diff: Diff::MINUS_ONE,
3728 });
3729 entry.push(TransactionUpdate {
3730 value: v,
3731 ts,
3732 diff: Diff::ONE,
3733 });
3734 }
3735 (Some(v), None) => {
3736 entry.push(TransactionUpdate {
3737 value: v,
3738 ts,
3739 diff: Diff::ONE,
3740 });
3741 }
3742 (None, Some(prev)) => {
3743 entry.push(TransactionUpdate {
3744 value: prev,
3745 ts,
3746 diff: Diff::MINUS_ONE,
3747 });
3748 }
3749 (None, None) => {}
3750 }
3751
3752 prevs.insert(k, prev);
3753 }
3754
3755 if let Err(err) = self.verify_keys(keys_to_verify_uniqueness.iter()) {
3757 for (k, restore_len) in restores {
3758 let pending = self.pending.get_mut(&k).expect("inserted above");
3761 pending.truncate(restore_len);
3762 }
3763 Err(err)
3764 } else {
3765 Ok(prevs)
3766 }
3767 }
3768
3769 fn delete<F: Fn(&K, &V) -> bool>(&mut self, f: F, ts: Timestamp) -> Vec<(K, V)> {
3775 let mut deleted = Vec::new();
3776 self.for_values_mut(|p, k, v| {
3777 if f(k, v) {
3778 deleted.push((k.clone(), v.clone()));
3779 p.entry(k.clone()).or_default().push(TransactionUpdate {
3780 value: v.clone(),
3781 ts,
3782 diff: Diff::MINUS_ONE,
3783 });
3784 }
3785 });
3786 soft_assert_no_log!(self.verify().is_ok());
3787 deleted
3788 }
3789
3790 fn delete_by_key(&mut self, k: K, ts: Timestamp) -> Option<V> {
3794 self.set(k, None, ts)
3795 .expect("deleting an entry cannot violate uniqueness")
3796 }
3797
3798 fn delete_by_keys(&mut self, ks: impl IntoIterator<Item = K>, ts: Timestamp) -> Vec<(K, V)> {
3802 let kvs = ks.into_iter().map(|k| (k, None)).collect();
3803 let prevs = self
3804 .set_many(kvs, ts)
3805 .expect("deleting entries cannot violate uniqueness");
3806 prevs
3807 .into_iter()
3808 .filter_map(|(k, v)| v.map(|v| (k, v)))
3809 .collect()
3810 }
3811}
3812
3813#[cfg(test)]
3814#[allow(clippy::unwrap_used)]
3815mod tests {
3816 use super::*;
3817
3818 use mz_controller::clusters::ReplicaLogging;
3819 use mz_ore::now::SYSTEM_TIME;
3820 use mz_ore::{assert_none, assert_ok};
3821 use mz_persist_client::cache::PersistClientCache;
3822 use mz_persist_types::PersistLocation;
3823 use semver::Version;
3824
3825 use crate::durable::{
3826 ReplicaConfig, ReplicaLocation, TestCatalogStateBuilder, test_bootstrap_args,
3827 };
3828 use crate::memory;
3829
3830 #[mz_ore::test]
3831 fn test_table_transaction_simple() {
3832 fn uniqueness_violation(a: &String, b: &String) -> bool {
3833 a == b
3834 }
3835 let mut table = TableTransaction::new_with_uniqueness_fn(
3836 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "a".to_string())]),
3837 uniqueness_violation,
3838 uniqueness_violation,
3839 )
3840 .unwrap();
3841
3842 assert_ok!(table.insert(2i64.to_le_bytes().to_vec(), "b".to_string(), 0));
3845 assert_ok!(table.insert(3i64.to_le_bytes().to_vec(), "c".to_string(), 0));
3846 assert!(
3847 table
3848 .insert(1i64.to_le_bytes().to_vec(), "c".to_string(), 0)
3849 .is_err()
3850 );
3851 assert!(
3852 table
3853 .insert(4i64.to_le_bytes().to_vec(), "c".to_string(), 0)
3854 .is_err()
3855 );
3856 }
3857
3858 #[mz_ore::test]
3859 fn test_skip_scan_when_unique_key_unchanged() {
3860 fn first_char_same(prev: &String, next: &String) -> bool {
3861 prev.chars().next() == next.chars().next()
3862 }
3863
3864 fn panic_uniqueness_violation(_: &String, _: &String) -> bool {
3866 panic!("uniqueness scan ran for an update that kept the same unique key");
3867 }
3868 let mut table = TableTransaction::new_with_uniqueness_fn(
3869 BTreeMap::from([
3870 (1i64.to_le_bytes().to_vec(), "a1".to_string()),
3871 (2i64.to_le_bytes().to_vec(), "b1".to_string()),
3872 ]),
3873 panic_uniqueness_violation,
3874 first_char_same,
3876 )
3877 .unwrap();
3878 assert!(
3881 table
3882 .update_by_key(1i64.to_le_bytes().to_vec(), "a2".to_string(), 0)
3883 .unwrap()
3884 );
3885
3886 fn real_uniqueness_violation(a: &String, b: &String) -> bool {
3888 a.chars().next() == b.chars().next()
3889 }
3890 let mut table = TableTransaction::new_with_uniqueness_fn(
3891 BTreeMap::from([
3892 (1i64.to_le_bytes().to_vec(), "a1".to_string()),
3893 (2i64.to_le_bytes().to_vec(), "b1".to_string()),
3894 ]),
3895 real_uniqueness_violation,
3896 first_char_same,
3897 )
3898 .unwrap();
3899 assert!(
3902 table
3903 .update_by_key(1i64.to_le_bytes().to_vec(), "b2".to_string(), 0)
3904 .is_err()
3905 );
3906 }
3907
3908 #[mz_ore::test]
3909 fn test_table_transaction() {
3910 fn uniqueness_violation(a: &String, b: &String) -> bool {
3911 a == b
3912 }
3913 let mut table: BTreeMap<Vec<u8>, String> = BTreeMap::new();
3914
3915 fn commit(
3916 table: &mut BTreeMap<Vec<u8>, String>,
3917 mut pending: Vec<(Vec<u8>, String, Diff)>,
3918 ) {
3919 pending.sort_by(|a, b| a.2.cmp(&b.2));
3921 for (k, v, diff) in pending {
3922 if diff == Diff::MINUS_ONE {
3923 let prev = table.remove(&k);
3924 assert_eq!(prev, Some(v));
3925 } else if diff == Diff::ONE {
3926 let prev = table.insert(k, v);
3927 assert_eq!(prev, None);
3928 } else {
3929 panic!("unexpected diff: {diff}");
3930 }
3931 }
3932 }
3933
3934 table.insert(1i64.to_le_bytes().to_vec(), "v1".to_string());
3935 table.insert(2i64.to_le_bytes().to_vec(), "v2".to_string());
3936 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
3937 table.clone(),
3938 uniqueness_violation,
3939 uniqueness_violation,
3940 )
3941 .unwrap();
3942 assert_eq!(table_txn.items_cloned(), table);
3943 assert_eq!(table_txn.delete(|_k, _v| false, 0).len(), 0);
3944 assert_eq!(table_txn.delete(|_k, v| v == "v2", 1).len(), 1);
3945 assert_eq!(
3946 table_txn.items_cloned(),
3947 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v1".to_string())])
3948 );
3949 assert_eq!(
3950 table_txn
3951 .update(|_k, _v| Some("v3".to_string()), 2)
3952 .unwrap(),
3953 Diff::ONE
3954 );
3955
3956 table_txn
3958 .insert(3i64.to_le_bytes().to_vec(), "v3".to_string(), 3)
3959 .unwrap_err();
3960
3961 table_txn
3962 .insert(3i64.to_le_bytes().to_vec(), "v4".to_string(), 4)
3963 .unwrap();
3964 assert_eq!(
3965 table_txn.items_cloned(),
3966 BTreeMap::from([
3967 (1i64.to_le_bytes().to_vec(), "v3".to_string()),
3968 (3i64.to_le_bytes().to_vec(), "v4".to_string()),
3969 ])
3970 );
3971 let err = table_txn
3972 .update(|_k, _v| Some("v1".to_string()), 5)
3973 .unwrap_err();
3974 assert!(
3975 matches!(err, DurableCatalogError::UniquenessViolation),
3976 "unexpected err: {err:?}"
3977 );
3978 let pending = table_txn.pending();
3979 assert_eq!(
3980 pending,
3981 vec![
3982 (
3983 1i64.to_le_bytes().to_vec(),
3984 "v1".to_string(),
3985 Diff::MINUS_ONE
3986 ),
3987 (1i64.to_le_bytes().to_vec(), "v3".to_string(), Diff::ONE),
3988 (
3989 2i64.to_le_bytes().to_vec(),
3990 "v2".to_string(),
3991 Diff::MINUS_ONE
3992 ),
3993 (3i64.to_le_bytes().to_vec(), "v4".to_string(), Diff::ONE),
3994 ]
3995 );
3996 commit(&mut table, pending);
3997 assert_eq!(
3998 table,
3999 BTreeMap::from([
4000 (1i64.to_le_bytes().to_vec(), "v3".to_string()),
4001 (3i64.to_le_bytes().to_vec(), "v4".to_string())
4002 ])
4003 );
4004
4005 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4006 table.clone(),
4007 uniqueness_violation,
4008 uniqueness_violation,
4009 )
4010 .unwrap();
4011 assert_eq!(
4013 table_txn.delete(|k, _v| k == &1i64.to_le_bytes(), 0).len(),
4014 1
4015 );
4016 table_txn
4017 .insert(1i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4018 .unwrap();
4019 table_txn
4021 .insert(5i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4022 .unwrap_err();
4023 table_txn
4025 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 0)
4026 .unwrap_err();
4027 assert_eq!(
4028 table_txn.delete(|k, _v| k == &1i64.to_le_bytes(), 0).len(),
4029 1
4030 );
4031 table_txn
4033 .insert(5i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4034 .unwrap();
4035 table_txn
4036 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 0)
4037 .unwrap();
4038 let pending = table_txn.pending();
4039 assert_eq!(
4040 pending,
4041 vec![
4042 (
4043 1i64.to_le_bytes().to_vec(),
4044 "v3".to_string(),
4045 Diff::MINUS_ONE
4046 ),
4047 (1i64.to_le_bytes().to_vec(), "v5".to_string(), Diff::ONE),
4048 (5i64.to_le_bytes().to_vec(), "v3".to_string(), Diff::ONE),
4049 ]
4050 );
4051 commit(&mut table, pending);
4052 assert_eq!(
4053 table,
4054 BTreeMap::from([
4055 (1i64.to_le_bytes().to_vec(), "v5".to_string()),
4056 (3i64.to_le_bytes().to_vec(), "v4".to_string()),
4057 (5i64.to_le_bytes().to_vec(), "v3".to_string()),
4058 ])
4059 );
4060
4061 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4062 table.clone(),
4063 uniqueness_violation,
4064 uniqueness_violation,
4065 )
4066 .unwrap();
4067 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 3);
4068 table_txn
4069 .insert(1i64.to_le_bytes().to_vec(), "v1".to_string(), 0)
4070 .unwrap();
4071
4072 commit(&mut table, table_txn.pending());
4073 assert_eq!(
4074 table,
4075 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v1".to_string()),])
4076 );
4077
4078 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4079 table.clone(),
4080 uniqueness_violation,
4081 uniqueness_violation,
4082 )
4083 .unwrap();
4084 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 1);
4085 table_txn
4086 .insert(1i64.to_le_bytes().to_vec(), "v2".to_string(), 0)
4087 .unwrap();
4088 commit(&mut table, table_txn.pending());
4089 assert_eq!(
4090 table,
4091 BTreeMap::from([(1i64.to_le_bytes().to_vec(), "v2".to_string()),])
4092 );
4093
4094 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4096 table.clone(),
4097 uniqueness_violation,
4098 uniqueness_violation,
4099 )
4100 .unwrap();
4101 assert_eq!(table_txn.delete(|_k, _v| true, 0).len(), 1);
4102 table_txn
4103 .insert(1i64.to_le_bytes().to_vec(), "v3".to_string(), 0)
4104 .unwrap();
4105 table_txn
4106 .insert(1i64.to_le_bytes().to_vec(), "v4".to_string(), 1)
4107 .unwrap_err();
4108 assert_eq!(table_txn.delete(|_k, _v| true, 1).len(), 1);
4109 table_txn
4110 .insert(1i64.to_le_bytes().to_vec(), "v5".to_string(), 1)
4111 .unwrap();
4112 commit(&mut table, table_txn.pending());
4113 assert_eq!(
4114 table.clone().into_iter().collect::<Vec<_>>(),
4115 vec![(1i64.to_le_bytes().to_vec(), "v5".to_string())]
4116 );
4117
4118 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4120 table.clone(),
4121 uniqueness_violation,
4122 uniqueness_violation,
4123 )
4124 .unwrap();
4125 table_txn
4127 .set(2i64.to_le_bytes().to_vec(), Some("v5".to_string()), 0)
4128 .unwrap_err();
4129 table_txn
4130 .set(3i64.to_le_bytes().to_vec(), Some("v6".to_string()), 1)
4131 .unwrap();
4132 table_txn.set(2i64.to_le_bytes().to_vec(), None, 2).unwrap();
4133 table_txn.set(1i64.to_le_bytes().to_vec(), None, 2).unwrap();
4134 let pending = table_txn.pending();
4135 assert_eq!(
4136 pending,
4137 vec![
4138 (
4139 1i64.to_le_bytes().to_vec(),
4140 "v5".to_string(),
4141 Diff::MINUS_ONE
4142 ),
4143 (3i64.to_le_bytes().to_vec(), "v6".to_string(), Diff::ONE),
4144 ]
4145 );
4146 commit(&mut table, pending);
4147 assert_eq!(
4148 table,
4149 BTreeMap::from([(3i64.to_le_bytes().to_vec(), "v6".to_string())])
4150 );
4151
4152 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4154 table.clone(),
4155 uniqueness_violation,
4156 uniqueness_violation,
4157 )
4158 .unwrap();
4159 table_txn
4160 .set(3i64.to_le_bytes().to_vec(), Some("v6".to_string()), 0)
4161 .unwrap();
4162 let pending = table_txn.pending::<Vec<u8>, String>();
4163 assert!(pending.is_empty());
4164
4165 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4167 table.clone(),
4168 uniqueness_violation,
4169 uniqueness_violation,
4170 )
4171 .unwrap();
4172 table_txn
4174 .set_many(
4175 BTreeMap::from([
4176 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4177 (42i64.to_le_bytes().to_vec(), Some("v1".to_string())),
4178 ]),
4179 0,
4180 )
4181 .unwrap_err();
4182 table_txn
4183 .set_many(
4184 BTreeMap::from([
4185 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4186 (3i64.to_le_bytes().to_vec(), Some("v1".to_string())),
4187 ]),
4188 1,
4189 )
4190 .unwrap();
4191 table_txn
4192 .set_many(
4193 BTreeMap::from([
4194 (42i64.to_le_bytes().to_vec(), Some("v7".to_string())),
4195 (3i64.to_le_bytes().to_vec(), None),
4196 ]),
4197 2,
4198 )
4199 .unwrap();
4200 let pending = table_txn.pending();
4201 assert_eq!(
4202 pending,
4203 vec![
4204 (1i64.to_le_bytes().to_vec(), "v6".to_string(), Diff::ONE),
4205 (
4206 3i64.to_le_bytes().to_vec(),
4207 "v6".to_string(),
4208 Diff::MINUS_ONE
4209 ),
4210 (42i64.to_le_bytes().to_vec(), "v7".to_string(), Diff::ONE),
4211 ]
4212 );
4213 commit(&mut table, pending);
4214 assert_eq!(
4215 table,
4216 BTreeMap::from([
4217 (1i64.to_le_bytes().to_vec(), "v6".to_string()),
4218 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4219 ])
4220 );
4221
4222 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4224 table.clone(),
4225 uniqueness_violation,
4226 uniqueness_violation,
4227 )
4228 .unwrap();
4229 table_txn
4230 .set_many(
4231 BTreeMap::from([
4232 (1i64.to_le_bytes().to_vec(), Some("v6".to_string())),
4233 (42i64.to_le_bytes().to_vec(), Some("v7".to_string())),
4234 ]),
4235 0,
4236 )
4237 .unwrap();
4238 let pending = table_txn.pending::<Vec<u8>, String>();
4239 assert!(pending.is_empty());
4240 commit(&mut table, pending);
4241 assert_eq!(
4242 table,
4243 BTreeMap::from([
4244 (1i64.to_le_bytes().to_vec(), "v6".to_string()),
4245 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4246 ])
4247 );
4248
4249 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4251 table.clone(),
4252 uniqueness_violation,
4253 uniqueness_violation,
4254 )
4255 .unwrap();
4256 table_txn
4258 .update_by_key(1i64.to_le_bytes().to_vec(), "v7".to_string(), 0)
4259 .unwrap_err();
4260 assert!(
4261 table_txn
4262 .update_by_key(1i64.to_le_bytes().to_vec(), "v8".to_string(), 1)
4263 .unwrap()
4264 );
4265 assert!(
4266 !table_txn
4267 .update_by_key(5i64.to_le_bytes().to_vec(), "v8".to_string(), 2)
4268 .unwrap()
4269 );
4270 let pending = table_txn.pending();
4271 assert_eq!(
4272 pending,
4273 vec![
4274 (
4275 1i64.to_le_bytes().to_vec(),
4276 "v6".to_string(),
4277 Diff::MINUS_ONE
4278 ),
4279 (1i64.to_le_bytes().to_vec(), "v8".to_string(), Diff::ONE),
4280 ]
4281 );
4282 commit(&mut table, pending);
4283 assert_eq!(
4284 table,
4285 BTreeMap::from([
4286 (1i64.to_le_bytes().to_vec(), "v8".to_string()),
4287 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4288 ])
4289 );
4290
4291 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4293 table.clone(),
4294 uniqueness_violation,
4295 uniqueness_violation,
4296 )
4297 .unwrap();
4298 assert!(
4299 table_txn
4300 .update_by_key(1i64.to_le_bytes().to_vec(), "v8".to_string(), 0)
4301 .unwrap()
4302 );
4303 let pending = table_txn.pending::<Vec<u8>, String>();
4304 assert!(pending.is_empty());
4305 commit(&mut table, pending);
4306 assert_eq!(
4307 table,
4308 BTreeMap::from([
4309 (1i64.to_le_bytes().to_vec(), "v8".to_string()),
4310 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4311 ])
4312 );
4313
4314 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4316 table.clone(),
4317 uniqueness_violation,
4318 uniqueness_violation,
4319 )
4320 .unwrap();
4321 table_txn
4323 .update_by_keys(
4324 [
4325 (1i64.to_le_bytes().to_vec(), "v7".to_string()),
4326 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4327 ],
4328 0,
4329 )
4330 .unwrap_err();
4331 let n = table_txn
4332 .update_by_keys(
4333 [
4334 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4335 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4336 ],
4337 1,
4338 )
4339 .unwrap();
4340 assert_eq!(n, Diff::ONE);
4341 let n = table_txn
4342 .update_by_keys(
4343 [
4344 (15i64.to_le_bytes().to_vec(), "v9".to_string()),
4345 (5i64.to_le_bytes().to_vec(), "v7".to_string()),
4346 ],
4347 2,
4348 )
4349 .unwrap();
4350 assert_eq!(n, Diff::ZERO);
4351 let pending = table_txn.pending();
4352 assert_eq!(
4353 pending,
4354 vec![
4355 (
4356 1i64.to_le_bytes().to_vec(),
4357 "v8".to_string(),
4358 Diff::MINUS_ONE
4359 ),
4360 (1i64.to_le_bytes().to_vec(), "v9".to_string(), Diff::ONE),
4361 ]
4362 );
4363 commit(&mut table, pending);
4364 assert_eq!(
4365 table,
4366 BTreeMap::from([
4367 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4368 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4369 ])
4370 );
4371
4372 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4374 table.clone(),
4375 uniqueness_violation,
4376 uniqueness_violation,
4377 )
4378 .unwrap();
4379 let n = table_txn
4380 .update_by_keys(
4381 [
4382 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4383 (42i64.to_le_bytes().to_vec(), "v7".to_string()),
4384 ],
4385 0,
4386 )
4387 .unwrap();
4388 assert_eq!(n, Diff::from(2));
4389 let pending = table_txn.pending::<Vec<u8>, String>();
4390 assert!(pending.is_empty());
4391 commit(&mut table, pending);
4392 assert_eq!(
4393 table,
4394 BTreeMap::from([
4395 (1i64.to_le_bytes().to_vec(), "v9".to_string()),
4396 (42i64.to_le_bytes().to_vec(), "v7".to_string())
4397 ])
4398 );
4399
4400 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4402 table.clone(),
4403 uniqueness_violation,
4404 uniqueness_violation,
4405 )
4406 .unwrap();
4407 let prev = table_txn.delete_by_key(1i64.to_le_bytes().to_vec(), 0);
4408 assert_eq!(prev, Some("v9".to_string()));
4409 let prev = table_txn.delete_by_key(5i64.to_le_bytes().to_vec(), 1);
4410 assert_none!(prev);
4411 let prev = table_txn.delete_by_key(1i64.to_le_bytes().to_vec(), 2);
4412 assert_none!(prev);
4413 let pending = table_txn.pending();
4414 assert_eq!(
4415 pending,
4416 vec![(
4417 1i64.to_le_bytes().to_vec(),
4418 "v9".to_string(),
4419 Diff::MINUS_ONE
4420 ),]
4421 );
4422 commit(&mut table, pending);
4423 assert_eq!(
4424 table,
4425 BTreeMap::from([(42i64.to_le_bytes().to_vec(), "v7".to_string())])
4426 );
4427
4428 let mut table_txn = TableTransaction::new_with_uniqueness_fn(
4430 table.clone(),
4431 uniqueness_violation,
4432 uniqueness_violation,
4433 )
4434 .unwrap();
4435 let prevs = table_txn.delete_by_keys(
4436 [42i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4437 0,
4438 );
4439 assert_eq!(
4440 prevs,
4441 vec![(42i64.to_le_bytes().to_vec(), "v7".to_string())]
4442 );
4443 let prevs = table_txn.delete_by_keys(
4444 [42i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4445 1,
4446 );
4447 assert_eq!(prevs, vec![]);
4448 let prevs = table_txn.delete_by_keys(
4449 [10i64.to_le_bytes().to_vec(), 55i64.to_le_bytes().to_vec()],
4450 2,
4451 );
4452 assert_eq!(prevs, vec![]);
4453 let pending = table_txn.pending();
4454 assert_eq!(
4455 pending,
4456 vec![(
4457 42i64.to_le_bytes().to_vec(),
4458 "v7".to_string(),
4459 Diff::MINUS_ONE
4460 ),]
4461 );
4462 commit(&mut table, pending);
4463 assert_eq!(table, BTreeMap::new());
4464 }
4465
4466 #[mz_ore::test(tokio::test)]
4467 #[cfg_attr(miri, ignore)] async fn test_savepoint() {
4469 const VERSION: Version = Version::new(26, 0, 0);
4470 let mut persist_cache = PersistClientCache::new_no_metrics();
4471 persist_cache.cfg.build_version = VERSION;
4472 let persist_client = persist_cache
4473 .open(PersistLocation::new_in_mem())
4474 .await
4475 .unwrap();
4476 let state_builder = TestCatalogStateBuilder::new(persist_client)
4477 .with_default_deploy_generation()
4478 .with_version(VERSION);
4479
4480 let _ = state_builder
4482 .clone()
4483 .unwrap_build()
4484 .await
4485 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4486 .await
4487 .unwrap();
4488 let mut savepoint_state = state_builder
4489 .unwrap_build()
4490 .await
4491 .open_savepoint(SYSTEM_TIME().into(), &test_bootstrap_args())
4492 .await
4493 .unwrap();
4494
4495 let initial_snapshot = savepoint_state.sync_to_current_updates().await.unwrap();
4496 assert!(!initial_snapshot.is_empty());
4497
4498 let db_name = "db";
4499 let db_owner = RoleId::User(42);
4500 let db_privileges = Vec::new();
4501 let mut txn = savepoint_state.transaction().await.unwrap();
4502 let (db_id, db_oid) = txn
4503 .insert_user_database(db_name, db_owner, db_privileges.clone(), &HashSet::new())
4504 .unwrap();
4505 let commit_ts = txn.upper();
4506 txn.commit_internal(commit_ts).await.unwrap();
4507 let updates = savepoint_state.sync_to_current_updates().await.unwrap();
4508 let update = updates.into_element();
4509
4510 assert_eq!(update.diff, StateDiff::Addition);
4511
4512 let db = match update.kind {
4513 memory::objects::StateUpdateKind::Database(db) => db,
4514 update => panic!("unexpected update: {update:?}"),
4515 };
4516
4517 assert_eq!(db_id, db.id);
4518 assert_eq!(db_oid, db.oid);
4519 assert_eq!(db_name, db.name);
4520 assert_eq!(db_owner, db.owner_id);
4521 assert_eq!(db_privileges, db.privileges);
4522 }
4523
4524 #[mz_ore::test(tokio::test)]
4525 #[cfg_attr(miri, ignore)] async fn test_dry_run_transaction_rejects_internal_commit() {
4527 const VERSION: Version = Version::new(26, 0, 0);
4528 let mut persist_cache = PersistClientCache::new_no_metrics();
4529 persist_cache.cfg.build_version = VERSION;
4530 let persist_client = persist_cache
4531 .open(PersistLocation::new_in_mem())
4532 .await
4533 .unwrap();
4534 let mut state = TestCatalogStateBuilder::new(persist_client)
4535 .with_default_deploy_generation()
4536 .with_version(VERSION)
4537 .unwrap_build()
4538 .await
4539 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4540 .await
4541 .unwrap();
4542 let _ = state.sync_to_current_updates().await.unwrap();
4543
4544 let initial_id = state.get_next_id(USER_ITEM_ALLOC_KEY).await.unwrap();
4545 let initial_upper = state.current_upper().await;
4546 let snapshot = state.snapshot().await.unwrap();
4547 let mut dry_run = state.transaction_from_snapshot(snapshot).unwrap();
4548 let ids = dry_run
4549 .transaction_mut()
4550 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), 1)
4551 .unwrap();
4552 assert_eq!(ids, vec![initial_id]);
4553
4554 let transaction = dry_run.transaction;
4555 let err = transaction
4556 .commit_internal(initial_upper)
4557 .await
4558 .unwrap_err();
4559 assert!(matches!(
4560 err,
4561 CatalogError::Durable(DurableCatalogError::DryRunTransaction)
4562 ));
4563 assert_eq!(state.current_upper().await, initial_upper);
4564 assert_eq!(
4565 state.get_next_id(USER_ITEM_ALLOC_KEY).await.unwrap(),
4566 initial_id
4567 );
4568 }
4569
4570 #[mz_ore::test(tokio::test)]
4571 #[cfg_attr(miri, ignore)] async fn test_dry_run_transaction_rejects_into_parts_escape() {
4573 const VERSION: Version = Version::new(26, 0, 0);
4574 let mut persist_cache = PersistClientCache::new_no_metrics();
4575 persist_cache.cfg.build_version = VERSION;
4576 let persist_client = persist_cache
4577 .open(PersistLocation::new_in_mem())
4578 .await
4579 .unwrap();
4580 let mut dry_run_state = TestCatalogStateBuilder::new(persist_client.clone())
4581 .with_default_deploy_generation()
4582 .with_version(VERSION)
4583 .unwrap_build()
4584 .await
4585 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4586 .await
4587 .unwrap();
4588 let mut replacement_state = TestCatalogStateBuilder::new(persist_client)
4589 .with_default_deploy_generation()
4590 .with_version(VERSION)
4591 .unwrap_build()
4592 .await
4593 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4594 .await
4595 .unwrap();
4596 let _ = dry_run_state.sync_to_current_updates().await.unwrap();
4597 let _ = replacement_state.sync_to_current_updates().await.unwrap();
4598
4599 let initial_id = dry_run_state
4600 .get_next_id(USER_ITEM_ALLOC_KEY)
4601 .await
4602 .unwrap();
4603 let initial_upper = dry_run_state.current_upper().await;
4604 let snapshot = dry_run_state.snapshot().await.unwrap();
4605 let mut dry_run = dry_run_state.transaction_from_snapshot(snapshot).unwrap();
4606 let ids = dry_run
4607 .transaction_mut()
4608 .get_and_increment_id_by(USER_ITEM_ALLOC_KEY.to_string(), 1)
4609 .unwrap();
4610 assert_eq!(ids, vec![initial_id]);
4611
4612 let replacement = replacement_state.transaction().await.unwrap();
4613 let escaped = std::mem::replace(dry_run.transaction_mut(), replacement);
4614 drop(dry_run);
4615
4616 let err = match escaped.into_parts() {
4617 Ok(_) => panic!("dry-run transaction decomposed into committable parts"),
4618 Err(err) => err,
4619 };
4620 assert!(matches!(
4621 err,
4622 CatalogError::Durable(DurableCatalogError::DryRunTransaction)
4623 ));
4624 assert_eq!(dry_run_state.current_upper().await, initial_upper);
4625 assert_eq!(
4626 dry_run_state
4627 .get_next_id(USER_ITEM_ALLOC_KEY)
4628 .await
4629 .unwrap(),
4630 initial_id
4631 );
4632 }
4633
4634 #[mz_ore::test(tokio::test)]
4638 #[cfg_attr(miri, ignore)] async fn test_insert_replica_with_id_does_not_consume_allocator() {
4640 const VERSION: Version = Version::new(26, 0, 0);
4641 let mut persist_cache = PersistClientCache::new_no_metrics();
4642 persist_cache.cfg.build_version = VERSION;
4643 let persist_client = persist_cache
4644 .open(PersistLocation::new_in_mem())
4645 .await
4646 .unwrap();
4647 let state_builder = TestCatalogStateBuilder::new(persist_client)
4648 .with_default_deploy_generation()
4649 .with_version(VERSION);
4650 let mut state = state_builder
4651 .unwrap_build()
4652 .await
4653 .open(SYSTEM_TIME().into(), &test_bootstrap_args())
4654 .await
4655 .unwrap();
4656
4657 let cluster_id = ClusterId::User(1);
4660 let owner_id = RoleId::User(1);
4661 let config = ReplicaConfig {
4662 location: ReplicaLocation::Managed {
4663 size: "1".to_string(),
4664 availability_zones: Vec::new(),
4665 internal: false,
4666 billed_as: None,
4667 pending: false,
4668 },
4669 logging: ReplicaLogging {
4670 log_logging: false,
4671 interval: Some(Duration::from_secs(1)),
4672 },
4673 arrangement_compression: false,
4674 };
4675
4676 let commit_ts = state.current_upper().await;
4678 let a = state
4679 .allocate_user_replica_ids(1, commit_ts)
4680 .await
4681 .unwrap()
4682 .into_element();
4683 assert!(a.is_user());
4684
4685 let initial_updates = state.sync_to_current_updates().await.unwrap();
4686 assert!(!initial_updates.is_empty());
4687
4688 let mut txn = state.transaction().await.unwrap();
4690 txn.insert_cluster_replica_with_id(cluster_id, a, "explicit", config, owner_id)
4691 .unwrap();
4692 let commit_ts = txn.upper();
4693 txn.commit_internal(commit_ts).await.unwrap();
4694 let _ = state.sync_to_current_updates().await.unwrap();
4695
4696 let commit_ts = state.current_upper().await;
4698 let b = state
4699 .allocate_user_replica_ids(1, commit_ts)
4700 .await
4701 .unwrap()
4702 .into_element();
4703
4704 assert_eq!(b.inner_id(), a.inner_id() + 1);
4707
4708 let txn = state.transaction().await.unwrap();
4710 let found = txn
4711 .get_cluster_replicas()
4712 .any(|replica| replica.replica_id == a);
4713 assert!(found, "explicitly inserted replica {a} not found");
4714 }
4715
4716 #[mz_ore::test]
4717 fn test_allocate_introspection_source_index_id() {
4718 let cluster_variant: u8 = 0b0000_0001;
4719 let cluster_id_inner: u64 =
4720 0b0000_0000_1100_0101_1100_0011_1010_1101_0000_1011_1111_1001_0110_1010;
4721 let timely_messages_received_log_variant: u8 = 0b0000_1000;
4722
4723 let cluster_id = ClusterId::System(cluster_id_inner);
4724 let log_variant = LogVariant::Timely(TimelyLog::MessagesReceived);
4725
4726 let introspection_source_index_id: u64 =
4727 0b0000_0001_1100_0101_1100_0011_1010_1101_0000_1011_1111_1001_0110_1010_0000_1000;
4728
4729 {
4731 let mut cluster_variant_mask = 0xFF << 56;
4732 cluster_variant_mask &= introspection_source_index_id;
4733 cluster_variant_mask >>= 56;
4734 assert_eq!(cluster_variant_mask, u64::from(cluster_variant));
4735 }
4736
4737 {
4739 let mut cluster_id_inner_mask = 0xFFFF_FFFF_FFFF << 8;
4740 cluster_id_inner_mask &= introspection_source_index_id;
4741 cluster_id_inner_mask >>= 8;
4742 assert_eq!(cluster_id_inner_mask, cluster_id_inner);
4743 }
4744
4745 {
4747 let mut log_variant_mask = 0xFF;
4748 log_variant_mask &= introspection_source_index_id;
4749 assert_eq!(
4750 log_variant_mask,
4751 u64::from(timely_messages_received_log_variant)
4752 );
4753 }
4754
4755 let (catalog_item_id, global_id) =
4756 Transaction::allocate_introspection_source_index_id(&cluster_id, log_variant);
4757
4758 assert_eq!(
4759 catalog_item_id,
4760 CatalogItemId::IntrospectionSourceIndex(introspection_source_index_id)
4761 );
4762 assert_eq!(
4763 global_id,
4764 GlobalId::IntrospectionSourceIndex(introspection_source_index_id)
4765 );
4766 }
4767}