1use std::borrow::Cow;
15use std::collections::{BTreeMap, BTreeSet};
16use std::convert;
17use std::sync::Arc;
18use std::sync::atomic::AtomicU64;
19
20use futures::future::BoxFuture;
21use futures::{Future, FutureExt};
22use itertools::Itertools;
23use mz_adapter_types::bootstrap_builtin_cluster_config::{
24 ANALYTICS_CLUSTER_DEFAULT_REPLICATION_FACTOR, BootstrapBuiltinClusterConfig,
25 CATALOG_SERVER_CLUSTER_DEFAULT_REPLICATION_FACTOR, PROBE_CLUSTER_DEFAULT_REPLICATION_FACTOR,
26 SUPPORT_CLUSTER_DEFAULT_REPLICATION_FACTOR, SYSTEM_CLUSTER_DEFAULT_REPLICATION_FACTOR,
27};
28use mz_adapter_types::connection::ConnectionId;
29use mz_audit_log::{EventType, FullNameV1, ObjectType, VersionedStorageUsage};
30use mz_build_info::{BuildInfo, DUMMY_BUILD_INFO};
31use mz_catalog::builtin::{
32 BUILTIN_PREFIXES, BuiltinCluster, BuiltinLog, BuiltinSource, BuiltinTable,
33 MZ_CATALOG_SERVER_CLUSTER,
34};
35use mz_catalog::config::{
36 AwsPrincipalContext, BuiltinItemMigrationConfig, ClusterReplicaSizeMap, Config, StateConfig,
37};
38#[cfg(test)]
39use mz_catalog::durable::CatalogError;
40use mz_catalog::durable::{
41 BootstrapArgs, DurableCatalogState, STORAGE_USAGE_ID_ALLOC_KEY, TestCatalogStateBuilder,
42 test_bootstrap_args,
43};
44use mz_catalog::expr_cache::{ExpressionCacheHandle, GlobalExpressions, LocalExpressions};
45use mz_catalog::memory::error::{Error, ErrorKind};
46use mz_catalog::memory::objects::{
47 CatalogCollectionEntry, CatalogEntry, CatalogItem, Cluster, ClusterReplica, Database,
48 NetworkPolicy, Role, RoleAuth, Schema,
49};
50use mz_cloud_resources::AwsExternalIdPrefix;
51use mz_compute_types::dataflows::DataflowDescription;
52use mz_controller::clusters::ReplicaLocation;
53use mz_controller_types::{ClusterId, ReplicaId};
54use mz_expr::OptimizedMirRelationExpr;
55use mz_license_keys::ValidatedLicenseKey;
56use mz_ore::metrics::MetricsRegistry;
57use mz_ore::now::{EpochMillis, NowFn, SYSTEM_TIME};
58use mz_ore::result::ResultExt as _;
59use mz_persist_client::PersistClient;
60use mz_repr::adt::mz_acl_item::{AclMode, PrivilegeMap};
61use mz_repr::explain::ExprHumanizer;
62use mz_repr::network_policy_id::NetworkPolicyId;
63use mz_repr::optimize::OptimizerFeatures;
64use mz_repr::role_id::RoleId;
65use mz_repr::{
66 CatalogItemId, Diff, GlobalId, RelationVersion, RelationVersionSelector, SqlScalarType,
67};
68use mz_secrets::InMemorySecretsController;
69use mz_sql::catalog::{
70 CatalogCluster, CatalogClusterReplica, CatalogDatabase, CatalogError as SqlCatalogError,
71 CatalogItem as SqlCatalogItem, CatalogItemType as SqlCatalogItemType, CatalogNetworkPolicy,
72 CatalogRole, CatalogSchema, DefaultPrivilegeAclItem, DefaultPrivilegeObject, EnvironmentId,
73 SessionCatalog, SystemObjectType,
74};
75use mz_sql::names::{
76 CommentObjectId, DatabaseId, FullItemName, FullSchemaName, ItemQualifiers, ObjectId,
77 PUBLIC_ROLE_NAME, PartialItemName, QualifiedItemName, QualifiedSchemaName,
78 ResolvedDatabaseSpecifier, ResolvedIds, SchemaId, SchemaSpecifier, SystemObjectId,
79};
80use mz_sql::plan::{Plan, PlanNotice, StatementDesc};
81use mz_sql::rbac;
82use mz_sql::session::metadata::SessionMetadata;
83use mz_sql::session::user::{MZ_SYSTEM_ROLE_ID, SUPPORT_USER, SYSTEM_USER};
84use mz_sql::session::vars::SystemVars;
85use mz_sql_parser::ast::QualifiedReplica;
86use mz_storage_types::connections::ConnectionContext;
87use mz_storage_types::connections::inline::{ConnectionResolver, InlinedConnection};
88use mz_transform::dataflow::DataflowMetainfo;
89use mz_transform::notice::OptimizerNotice;
90use tokio::sync::MutexGuard;
91use tokio::sync::mpsc::UnboundedSender;
92use uuid::Uuid;
93
94pub use crate::catalog::builtin_table_updates::BuiltinTableUpdate;
96pub use crate::catalog::open::{InitializeStateResult, OpenCatalogResult};
97pub use crate::catalog::state::CatalogState;
98#[cfg(test)]
99pub(crate) use crate::catalog::state::LocalExpressionCache;
100pub(crate) use crate::catalog::transact::{CommandExecutionError, CommandWork};
101pub use crate::catalog::transact::{
102 DropObjectInfo, InjectedAuditEvent, Op, ReplicaCreateDropReason, TransactionResult,
103};
104use crate::command::CatalogDump;
105use crate::coord::TargetCluster;
106#[cfg(test)]
107use crate::coord::catalog_implications::parsed_state_updates::ParsedStateUpdate;
108use crate::session::{Portal, PreparedStatement, Session};
109use crate::util::ResultExt;
110use crate::{AdapterError, AdapterNotice, ExecuteResponse};
111
112mod builtin_table_updates;
113pub(crate) mod command;
114pub(crate) mod consistency;
115mod limits;
116mod migrate;
117
118mod apply;
119pub(crate) mod cluster_state;
120mod open;
121mod state;
122mod timeline;
123mod transact;
124
125#[derive(Debug)]
148pub struct Catalog {
149 state: CatalogState,
150 expr_cache_handle: Option<ExpressionCacheHandle>,
151 storage: Arc<tokio::sync::Mutex<Box<dyn mz_catalog::durable::DurableCatalogState>>>,
152 transient_revision: u64,
153 shared_transient_revision: Arc<AtomicU64>,
166}
167
168#[derive(Debug, Clone)]
170pub struct CatalogUpperHandle {
171 storage: Arc<tokio::sync::Mutex<Box<dyn mz_catalog::durable::DurableCatalogState>>>,
172}
173
174impl CatalogUpperHandle {
175 pub async fn advance_upper(
177 &self,
178 new_upper: mz_repr::Timestamp,
179 ) -> Result<(), mz_catalog::durable::CatalogError> {
180 self.storage.lock().await.advance_upper(new_upper).await
181 }
182}
183
184impl Clone for Catalog {
187 fn clone(&self) -> Self {
188 Self {
189 state: self.state.clone(),
190 expr_cache_handle: self.expr_cache_handle.clone(),
191 storage: Arc::clone(&self.storage),
192 transient_revision: self.transient_revision,
193 shared_transient_revision: Arc::clone(&self.shared_transient_revision),
194 }
195 }
196}
197
198impl Catalog {
199 #[mz_ore::instrument(level = "trace")]
205 pub fn set_optimized_plan(
206 &mut self,
207 id: GlobalId,
208 plan: DataflowDescription<OptimizedMirRelationExpr>,
209 ) {
210 self.state.set_optimized_plan(id, plan);
211 }
212
213 #[mz_ore::instrument(level = "trace")]
219 pub fn set_physical_plan(
220 &mut self,
221 id: GlobalId,
222 plan: mz_compute_types::dataflows::LirDataflowDescription,
223 ) {
224 self.state.set_physical_plan(id, plan);
225 }
226
227 #[mz_ore::instrument(level = "trace")]
229 pub fn try_get_optimized_plan(
230 &self,
231 id: &GlobalId,
232 ) -> Option<&DataflowDescription<OptimizedMirRelationExpr>> {
233 let entry = self.state.try_get_entry_by_global_id(id)?;
234 entry.item().optimized_plan().map(AsRef::as_ref)
235 }
236
237 #[mz_ore::instrument(level = "trace")]
239 pub fn try_get_physical_plan(
240 &self,
241 id: &GlobalId,
242 ) -> Option<&mz_compute_types::dataflows::LirDataflowDescription> {
243 let entry = self.state.try_get_entry_by_global_id(id)?;
244 entry.item().physical_plan().map(AsRef::as_ref)
245 }
246
247 #[mz_ore::instrument(level = "trace")]
253 pub fn set_dataflow_metainfo(
254 &mut self,
255 id: GlobalId,
256 metainfo: DataflowMetainfo<Arc<OptimizerNotice>>,
257 ) {
258 self.state.set_dataflow_metainfo(id, metainfo);
259 }
260
261 #[mz_ore::instrument(level = "trace")]
263 pub fn try_get_dataflow_metainfo(
264 &self,
265 id: &GlobalId,
266 ) -> Option<&DataflowMetainfo<Arc<OptimizerNotice>>> {
267 let entry = self.state.try_get_entry_by_global_id(id)?;
268 entry.item().dataflow_metainfo()
269 }
270}
271
272#[derive(Debug)]
273pub struct ConnCatalog<'a> {
274 state: Cow<'a, CatalogState>,
275 unresolvable_ids: BTreeSet<CatalogItemId>,
285 conn_id: ConnectionId,
286 cluster: String,
287 database: Option<DatabaseId>,
288 search_path: Vec<(ResolvedDatabaseSpecifier, SchemaSpecifier)>,
289 role_id: RoleId,
290 prepared_statements: Option<&'a BTreeMap<String, PreparedStatement>>,
291 portals: Option<&'a BTreeMap<String, Portal>>,
292 notices_tx: UnboundedSender<AdapterNotice>,
293 restrict_to_user_objects: bool,
294}
295
296impl ConnCatalog<'_> {
297 pub fn conn_id(&self) -> &ConnectionId {
298 &self.conn_id
299 }
300
301 pub fn state(&self) -> &CatalogState {
302 &*self.state
303 }
304
305 pub fn for_system_session(&self) -> ConnCatalog<'_> {
307 self.state.for_system_session()
308 }
309
310 pub fn mark_id_unresolvable_for_replanning(&mut self, id: CatalogItemId) {
320 assert_eq!(
321 self.role_id, MZ_SYSTEM_ROLE_ID,
322 "only the system role can mark IDs unresolvable",
323 );
324 self.unresolvable_ids.insert(id);
325 }
326
327 pub fn effective_search_path(
333 &self,
334 include_temp_schema: bool,
335 ) -> Vec<(ResolvedDatabaseSpecifier, SchemaSpecifier)> {
336 self.state
337 .effective_search_path(&self.search_path, include_temp_schema)
338 }
339}
340
341impl ConnectionResolver for ConnCatalog<'_> {
342 fn resolve_connection(
343 &self,
344 id: CatalogItemId,
345 ) -> mz_storage_types::connections::Connection<InlinedConnection> {
346 self.state().resolve_connection(id)
347 }
348}
349
350#[derive(Debug, Clone, Default)]
361pub struct DebugAwsContext {
362 pub aws_account_id: Option<String>,
363 pub aws_external_id_prefix: Option<String>,
364 pub aws_connection_role_arn: Option<String>,
365}
366
367impl Catalog {
368 pub fn transient_revision(&self) -> u64 {
372 self.transient_revision
373 }
374
375 pub fn transient_revision_is_current(&self) -> bool {
381 self.transient_revision
382 == self
383 .shared_transient_revision
384 .load(std::sync::atomic::Ordering::SeqCst)
385 }
386
387 pub async fn with_debug<F, Fut, T>(f: F) -> T
399 where
400 F: FnOnce(Catalog) -> Fut,
401 Fut: Future<Output = T>,
402 {
403 let persist_client = PersistClient::new_for_tests().await;
404 let organization_id = Uuid::new_v4();
405 let bootstrap_args = test_bootstrap_args();
406 let catalog = Self::open_debug_catalog(persist_client, organization_id, &bootstrap_args)
407 .await
408 .expect("can open debug catalog");
409 f(catalog).await
410 }
411
412 pub async fn with_debug_aws_context<F, Fut, T>(aws_context: DebugAwsContext, f: F) -> T
416 where
417 F: FnOnce(Catalog) -> Fut,
418 Fut: Future<Output = T>,
419 {
420 let persist_client = PersistClient::new_for_tests().await;
421 let organization_id = Uuid::new_v4();
422 let bootstrap_args = test_bootstrap_args();
423 let catalog = Self::open_debug_catalog_with_aws_context(
424 persist_client,
425 organization_id,
426 &bootstrap_args,
427 Some(aws_context),
428 )
429 .await
430 .expect("can open debug catalog");
431 f(catalog).await
432 }
433
434 pub async fn with_debug_in_bootstrap<F, Fut, T>(f: F) -> T
437 where
438 F: FnOnce(Catalog) -> Fut,
439 Fut: Future<Output = T>,
440 {
441 let persist_client = PersistClient::new_for_tests().await;
442 let organization_id = Uuid::new_v4();
443 let bootstrap_args = test_bootstrap_args();
444 let mut catalog =
445 Self::open_debug_catalog(persist_client.clone(), organization_id, &bootstrap_args)
446 .await
447 .expect("can open debug catalog");
448
449 let now = SYSTEM_TIME.clone();
451 let openable_storage = TestCatalogStateBuilder::new(persist_client)
452 .with_organization_id(organization_id)
453 .with_default_deploy_generation()
454 .build()
455 .await
456 .expect("can create durable catalog");
457 let mut storage = openable_storage
458 .open(now().into(), &bootstrap_args)
459 .await
460 .expect("can open durable catalog");
461 let _ = storage
463 .sync_to_current_updates()
464 .await
465 .expect("can sync to current updates");
466 catalog.storage = Arc::new(tokio::sync::Mutex::new(storage));
467
468 f(catalog).await
469 }
470
471 pub async fn open_debug_catalog(
475 persist_client: PersistClient,
476 organization_id: Uuid,
477 bootstrap_args: &BootstrapArgs,
478 ) -> Result<Catalog, anyhow::Error> {
479 let now = SYSTEM_TIME.clone();
480 let environment_id = None;
481 let openable_storage = TestCatalogStateBuilder::new(persist_client.clone())
482 .with_organization_id(organization_id)
483 .with_default_deploy_generation()
484 .build()
485 .await?;
486 let storage = openable_storage.open(now().into(), bootstrap_args).await?;
487 let system_parameter_defaults = BTreeMap::default();
488 Self::open_debug_catalog_inner(
489 persist_client,
490 storage,
491 now,
492 environment_id,
493 &DUMMY_BUILD_INFO,
494 system_parameter_defaults,
495 bootstrap_args,
496 None,
497 None,
498 )
499 .await
500 }
501
502 pub async fn open_debug_catalog_with_aws_context(
511 persist_client: PersistClient,
512 organization_id: Uuid,
513 bootstrap_args: &BootstrapArgs,
514 aws_context: Option<DebugAwsContext>,
515 ) -> Result<Catalog, anyhow::Error> {
516 let now = SYSTEM_TIME.clone();
517 let environment_id = None;
518 let openable_storage = TestCatalogStateBuilder::new(persist_client.clone())
519 .with_organization_id(organization_id)
520 .with_default_deploy_generation()
521 .build()
522 .await?;
523 let storage = openable_storage.open(now().into(), bootstrap_args).await?;
524 let system_parameter_defaults = BTreeMap::default();
525 Self::open_debug_catalog_inner(
526 persist_client,
527 storage,
528 now,
529 environment_id,
530 &DUMMY_BUILD_INFO,
531 system_parameter_defaults,
532 bootstrap_args,
533 None,
534 aws_context,
535 )
536 .await
537 }
538
539 pub async fn open_debug_read_only_catalog(
544 persist_client: PersistClient,
545 organization_id: Uuid,
546 bootstrap_args: &BootstrapArgs,
547 ) -> Result<Catalog, anyhow::Error> {
548 let now = SYSTEM_TIME.clone();
549 let environment_id = None;
550 let openable_storage = TestCatalogStateBuilder::new(persist_client.clone())
551 .with_organization_id(organization_id)
552 .build()
553 .await?;
554 let storage = openable_storage
555 .open_read_only(&test_bootstrap_args())
556 .await?;
557 let system_parameter_defaults = BTreeMap::default();
558 Self::open_debug_catalog_inner(
559 persist_client,
560 storage,
561 now,
562 environment_id,
563 &DUMMY_BUILD_INFO,
564 system_parameter_defaults,
565 bootstrap_args,
566 None,
567 None,
568 )
569 .await
570 }
571
572 pub async fn open_debug_read_only_persist_catalog_config(
577 persist_client: PersistClient,
578 now: NowFn,
579 environment_id: EnvironmentId,
580 system_parameter_defaults: BTreeMap<String, String>,
581 build_info: &'static BuildInfo,
582 bootstrap_args: &BootstrapArgs,
583 enable_expression_cache_override: Option<bool>,
584 aws_context: Option<DebugAwsContext>,
585 ) -> Result<Catalog, anyhow::Error> {
586 let openable_storage = TestCatalogStateBuilder::new(persist_client.clone())
587 .with_organization_id(environment_id.organization_id())
588 .with_version(
589 build_info
590 .version
591 .parse()
592 .expect("build version is parseable"),
593 )
594 .build()
595 .await?;
596 let storage = openable_storage.open_read_only(bootstrap_args).await?;
597 Self::open_debug_catalog_inner(
598 persist_client,
599 storage,
600 now,
601 Some(environment_id),
602 build_info,
603 system_parameter_defaults,
604 bootstrap_args,
605 enable_expression_cache_override,
606 aws_context,
607 )
608 .await
609 }
610
611 async fn open_debug_catalog_inner(
612 persist_client: PersistClient,
613 storage: Box<dyn DurableCatalogState>,
614 now: NowFn,
615 environment_id: Option<EnvironmentId>,
616 build_info: &'static BuildInfo,
617 system_parameter_defaults: BTreeMap<String, String>,
618 bootstrap_args: &BootstrapArgs,
619 enable_expression_cache_override: Option<bool>,
620 aws_context: Option<DebugAwsContext>,
621 ) -> Result<Catalog, anyhow::Error> {
622 let metrics_registry = &MetricsRegistry::new();
623 let secrets_reader = Arc::new(InMemorySecretsController::new());
624 let previous_ts = now().into();
627 let replica_size = &bootstrap_args.default_cluster_replica_size;
628 let read_only = false;
629
630 let mut connection_context = ConnectionContext::for_tests(secrets_reader);
636 let aws_principal_context = match aws_context {
637 None => None,
638 Some(aws_context) => {
639 connection_context.aws_external_id_prefix =
640 aws_context.aws_external_id_prefix.as_deref().map(|prefix| {
641 AwsExternalIdPrefix::new_from_cli_argument_or_environment_variable(prefix)
642 .expect("infallible")
643 });
644 connection_context.aws_connection_role_arn = aws_context.aws_connection_role_arn;
645 aws_context.aws_account_id.map(|aws_account_id| {
646 AwsPrincipalContext {
647 aws_account_id,
648 aws_external_id_prefix:
652 AwsExternalIdPrefix::new_from_cli_argument_or_environment_variable(
653 "debug",
654 )
655 .expect("infallible"),
656 }
657 })
658 }
659 };
660
661 let OpenCatalogResult {
662 catalog,
663 last_seen_version: _,
664 migrated_storage_collections_0dt: _,
665 new_builtin_collections: _,
666 builtin_table_updates: _,
667 cached_global_exprs: _,
668 uncached_local_exprs: _,
669 } = Catalog::open(Config {
670 storage,
671 metrics_registry,
672 state: StateConfig {
673 unsafe_mode: true,
674 all_features: false,
675 build_info,
676 environment_id: environment_id.unwrap_or_else(EnvironmentId::for_tests),
677 read_only,
678 now,
679 boot_ts: previous_ts,
680 skip_migrations: true,
681 cluster_replica_sizes: bootstrap_args.cluster_replica_size_map.clone(),
682 builtin_system_cluster_config: BootstrapBuiltinClusterConfig {
683 size: replica_size.clone(),
684 replication_factor: SYSTEM_CLUSTER_DEFAULT_REPLICATION_FACTOR,
685 },
686 builtin_catalog_server_cluster_config: BootstrapBuiltinClusterConfig {
687 size: replica_size.clone(),
688 replication_factor: CATALOG_SERVER_CLUSTER_DEFAULT_REPLICATION_FACTOR,
689 },
690 builtin_probe_cluster_config: BootstrapBuiltinClusterConfig {
691 size: replica_size.clone(),
692 replication_factor: PROBE_CLUSTER_DEFAULT_REPLICATION_FACTOR,
693 },
694 builtin_support_cluster_config: BootstrapBuiltinClusterConfig {
695 size: replica_size.clone(),
696 replication_factor: SUPPORT_CLUSTER_DEFAULT_REPLICATION_FACTOR,
697 },
698 builtin_analytics_cluster_config: BootstrapBuiltinClusterConfig {
699 size: replica_size.clone(),
700 replication_factor: ANALYTICS_CLUSTER_DEFAULT_REPLICATION_FACTOR,
701 },
702 system_parameter_defaults,
703 remote_system_parameters: None,
704 availability_zones: vec![],
705 egress_addresses: vec![],
706 aws_principal_context,
707 aws_privatelink_availability_zones: None,
708 http_host_name: None,
709 connection_context,
710 builtin_item_migration_config: BuiltinItemMigrationConfig {
711 persist_client: persist_client.clone(),
712 read_only,
713 force_migration: None,
714 },
715 persist_client,
716 enable_expression_cache_override,
717 helm_chart_version: None,
718 external_login_password_mz_system: None,
719 license_key: ValidatedLicenseKey::for_tests(),
720 },
721 })
722 .await?;
723 Ok(catalog)
724 }
725
726 pub fn for_session<'a>(&'a self, session: &'a Session) -> ConnCatalog<'a> {
727 self.state.for_session(session)
728 }
729
730 pub fn for_sessionless_user(&self, role_id: RoleId) -> ConnCatalog<'_> {
731 self.state.for_sessionless_user(role_id)
732 }
733
734 pub fn for_system_session(&self) -> ConnCatalog<'_> {
735 self.state.for_system_session()
736 }
737
738 async fn storage<'a>(
739 &'a self,
740 ) -> MutexGuard<'a, Box<dyn mz_catalog::durable::DurableCatalogState>> {
741 self.storage.lock().await
742 }
743
744 pub async fn current_upper(&self) -> mz_repr::Timestamp {
745 self.storage().await.current_upper().await
746 }
747
748 pub async fn allocate_user_id(
751 &self,
752 commit_ts: mz_repr::Timestamp,
753 ) -> Result<(CatalogItemId, GlobalId), Error> {
754 Ok(self
755 .storage()
756 .await
757 .allocate_user_id(commit_ts)
758 .await
759 .maybe_terminate("allocating user ids")?)
760 }
761
762 pub async fn allocate_user_ids(
764 &self,
765 amount: u64,
766 commit_ts: mz_repr::Timestamp,
767 ) -> Result<Vec<(CatalogItemId, GlobalId)>, Error> {
768 Ok(self
769 .storage()
770 .await
771 .allocate_user_ids(amount, commit_ts)
772 .await
773 .maybe_terminate("allocating user ids")?)
774 }
775
776 pub async fn allocate_user_id_for_test(&self) -> Result<(CatalogItemId, GlobalId), Error> {
777 let commit_ts = self.storage().await.current_upper().await;
778 self.allocate_user_id(commit_ts).await
779 }
780
781 pub async fn allocate_storage_usage_id(
789 &self,
790 commit_ts: mz_repr::Timestamp,
791 ) -> Result<u64, Error> {
792 use mz_ore::collections::CollectionExt;
793
794 self.storage()
795 .await
796 .allocate_id(STORAGE_USAGE_ID_ALLOC_KEY, 1, commit_ts)
797 .await
798 .maybe_terminate("allocating storage usage id")
799 .map(|ids| ids.into_element())
800 .err_into()
801 }
802
803 pub async fn get_next_user_item_id(&self) -> Result<u64, Error> {
805 self.storage()
806 .await
807 .get_next_user_item_id()
808 .await
809 .err_into()
810 }
811
812 #[cfg(test)]
813 pub async fn allocate_system_id(
814 &self,
815 commit_ts: mz_repr::Timestamp,
816 ) -> Result<(CatalogItemId, GlobalId), Error> {
817 use mz_ore::collections::CollectionExt;
818
819 let mut storage = self.storage().await;
820 let mut txn = storage.transaction().await?;
821 let id = txn
822 .allocate_system_item_ids(1)
823 .maybe_terminate("allocating system ids")?
824 .into_element();
825 let _ = txn.get_and_commit_op_updates();
827 txn.commit(commit_ts).await?;
828 Ok(id)
829 }
830
831 #[cfg(test)]
834 pub async fn register_collection_shard(
835 &mut self,
836 id: GlobalId,
837 ) -> Result<mz_persist_types::ShardId, Error> {
838 use mz_storage_client::controller::StorageTxn;
839
840 let shard_id = mz_persist_types::ShardId::new();
841 let mut storage = self.storage().await;
842 let mut txn = storage.transaction().await?;
843 txn.insert_collection_metadata([(id, shard_id)].into())
844 .expect("a fresh shard for an unregistered collection");
845 let updates = txn.get_and_commit_op_updates();
846 let commit_ts = txn.upper();
847 txn.commit(commit_ts).await?;
848 drop(storage);
849 let (_builtin_updates, _catalog_updates) = self
850 .state
851 .apply_updates(updates, &mut LocalExpressionCache::Closed)
852 .await;
853 Ok(shard_id)
854 }
855
856 pub async fn get_next_system_item_id(&self) -> Result<u64, Error> {
858 self.storage()
859 .await
860 .get_next_system_item_id()
861 .await
862 .err_into()
863 }
864
865 pub async fn allocate_user_cluster_id(
868 &self,
869 commit_ts: mz_repr::Timestamp,
870 ) -> Result<ClusterId, Error> {
871 Ok(self
872 .storage()
873 .await
874 .allocate_user_cluster_id(commit_ts)
875 .await
876 .maybe_terminate("allocating user cluster ids")?)
877 }
878
879 pub async fn allocate_user_replica_ids(
882 &self,
883 amount: u64,
884 commit_ts: mz_repr::Timestamp,
885 ) -> Result<Vec<ReplicaId>, Error> {
886 Ok(self
887 .storage()
888 .await
889 .allocate_user_replica_ids(amount, commit_ts)
890 .await
891 .maybe_terminate("allocating user replica ids")?)
892 }
893
894 pub async fn allocate_system_replica_ids(
897 &self,
898 amount: u64,
899 commit_ts: mz_repr::Timestamp,
900 ) -> Result<Vec<ReplicaId>, Error> {
901 Ok(self
902 .storage()
903 .await
904 .allocate_system_replica_ids(amount, commit_ts)
905 .await
906 .maybe_terminate("allocating system replica ids")?)
907 }
908
909 pub async fn allocate_replica_ids(
912 &self,
913 cluster_id: ClusterId,
914 amount: u64,
915 commit_ts: mz_repr::Timestamp,
916 ) -> Result<Vec<ReplicaId>, Error> {
917 if cluster_id.is_system() {
918 self.allocate_system_replica_ids(amount, commit_ts).await
919 } else {
920 self.allocate_user_replica_ids(amount, commit_ts).await
921 }
922 }
923
924 pub async fn get_next_system_replica_id(&self) -> Result<u64, Error> {
926 self.storage()
927 .await
928 .get_next_system_replica_id()
929 .await
930 .err_into()
931 }
932
933 pub async fn get_next_user_replica_id(&self) -> Result<u64, Error> {
935 self.storage()
936 .await
937 .get_next_user_replica_id()
938 .await
939 .err_into()
940 }
941
942 pub fn resolve_database(&self, database_name: &str) -> Result<&Database, SqlCatalogError> {
943 self.state.resolve_database(database_name)
944 }
945
946 pub fn resolve_schema(
947 &self,
948 current_database: Option<&DatabaseId>,
949 database_name: Option<&str>,
950 schema_name: &str,
951 conn_id: &ConnectionId,
952 ) -> Result<&Schema, SqlCatalogError> {
953 self.state
954 .resolve_schema(current_database, database_name, schema_name, conn_id)
955 }
956
957 pub fn resolve_schema_in_database(
958 &self,
959 database_spec: &ResolvedDatabaseSpecifier,
960 schema_name: &str,
961 conn_id: &ConnectionId,
962 ) -> Result<&Schema, SqlCatalogError> {
963 self.state
964 .resolve_schema_in_database(database_spec, schema_name, conn_id)
965 }
966
967 pub fn resolve_replica_in_cluster(
968 &self,
969 cluster_id: &ClusterId,
970 replica_name: &str,
971 ) -> Result<&ClusterReplica, SqlCatalogError> {
972 self.state
973 .resolve_replica_in_cluster(cluster_id, replica_name)
974 }
975
976 pub fn resolve_system_schema(&self, name: &'static str) -> SchemaId {
977 self.state.resolve_system_schema(name)
978 }
979
980 pub fn resolve_search_path(
981 &self,
982 session: &Session,
983 ) -> Vec<(ResolvedDatabaseSpecifier, SchemaSpecifier)> {
984 self.state.resolve_search_path(session)
985 }
986
987 pub fn resolve_entry(
989 &self,
990 current_database: Option<&DatabaseId>,
991 search_path: &Vec<(ResolvedDatabaseSpecifier, SchemaSpecifier)>,
992 name: &PartialItemName,
993 conn_id: &ConnectionId,
994 ) -> Result<&CatalogEntry, SqlCatalogError> {
995 self.state
996 .resolve_entry(current_database, search_path, name, conn_id)
997 }
998
999 pub fn resolve_builtin_table(&self, builtin: &'static BuiltinTable) -> CatalogItemId {
1001 self.state.resolve_builtin_table(builtin)
1002 }
1003
1004 pub fn resolve_builtin_log(&self, builtin: &'static BuiltinLog) -> CatalogItemId {
1006 self.state.resolve_builtin_log(builtin).0
1007 }
1008
1009 pub fn resolve_builtin_storage_collection(
1011 &self,
1012 builtin: &'static BuiltinSource,
1013 ) -> CatalogItemId {
1014 self.state.resolve_builtin_source(builtin)
1015 }
1016
1017 pub fn resolve_function(
1019 &self,
1020 current_database: Option<&DatabaseId>,
1021 search_path: &Vec<(ResolvedDatabaseSpecifier, SchemaSpecifier)>,
1022 name: &PartialItemName,
1023 conn_id: &ConnectionId,
1024 ) -> Result<&CatalogEntry, SqlCatalogError> {
1025 self.state
1026 .resolve_function(current_database, search_path, name, conn_id)
1027 }
1028
1029 pub fn resolve_type(
1031 &self,
1032 current_database: Option<&DatabaseId>,
1033 search_path: &Vec<(ResolvedDatabaseSpecifier, SchemaSpecifier)>,
1034 name: &PartialItemName,
1035 conn_id: &ConnectionId,
1036 ) -> Result<&CatalogEntry, SqlCatalogError> {
1037 self.state
1038 .resolve_type(current_database, search_path, name, conn_id)
1039 }
1040
1041 pub fn resolve_cluster(&self, name: &str) -> Result<&Cluster, SqlCatalogError> {
1042 self.state.resolve_cluster(name)
1043 }
1044
1045 pub fn resolve_builtin_cluster(&self, cluster: &BuiltinCluster) -> &Cluster {
1051 self.state.resolve_builtin_cluster(cluster)
1052 }
1053
1054 pub fn get_mz_catalog_server_cluster_id(&self) -> &ClusterId {
1055 &self.resolve_builtin_cluster(&MZ_CATALOG_SERVER_CLUSTER).id
1056 }
1057
1058 pub fn resolve_target_cluster(
1060 &self,
1061 target_cluster: TargetCluster,
1062 session: &Session,
1063 ) -> Result<&Cluster, AdapterError> {
1064 match target_cluster {
1065 TargetCluster::CatalogServer => {
1066 Ok(self.resolve_builtin_cluster(&MZ_CATALOG_SERVER_CLUSTER))
1067 }
1068 TargetCluster::Active => self.active_cluster(session),
1069 TargetCluster::Transaction(cluster_id) => self
1070 .try_get_cluster(cluster_id)
1071 .ok_or(AdapterError::ConcurrentClusterDrop),
1072 }
1073 }
1074
1075 pub fn active_cluster(&self, session: &Session) -> Result<&Cluster, AdapterError> {
1076 if session.user().name != SYSTEM_USER.name
1080 && session.user().name != SUPPORT_USER.name
1081 && session.vars().cluster() == SYSTEM_USER.name
1082 {
1083 coord_bail!(
1084 "system cluster '{}' cannot execute user queries",
1085 SYSTEM_USER.name
1086 );
1087 }
1088 let cluster = self.resolve_cluster(session.vars().cluster())?;
1089 Ok(cluster)
1090 }
1091
1092 pub fn state(&self) -> &CatalogState {
1093 &self.state
1094 }
1095
1096 pub fn resolve_full_name(
1097 &self,
1098 name: &QualifiedItemName,
1099 conn_id: Option<&ConnectionId>,
1100 ) -> FullItemName {
1101 self.state.resolve_full_name(name, conn_id)
1102 }
1103
1104 pub fn try_get_entry(&self, id: &CatalogItemId) -> Option<&CatalogEntry> {
1105 self.state.try_get_entry(id)
1106 }
1107
1108 pub fn try_get_entry_by_global_id(&self, id: &GlobalId) -> Option<&CatalogEntry> {
1109 self.state.try_get_entry_by_global_id(id)
1110 }
1111
1112 pub fn get_entry(&self, id: &CatalogItemId) -> &CatalogEntry {
1113 self.state.get_entry(id)
1114 }
1115
1116 pub fn get_entry_by_global_id(&self, id: &GlobalId) -> CatalogCollectionEntry {
1117 self.state.get_entry_by_global_id(id)
1118 }
1119
1120 pub fn get_global_ids<'a>(
1121 &'a self,
1122 id: &CatalogItemId,
1123 ) -> impl Iterator<Item = GlobalId> + use<'a> {
1124 self.get_entry(id).global_ids()
1125 }
1126
1127 pub fn resolve_item_id(&self, id: &GlobalId) -> CatalogItemId {
1128 self.get_entry_by_global_id(id).id()
1129 }
1130
1131 pub fn try_resolve_item_id(&self, id: &GlobalId) -> Option<CatalogItemId> {
1132 let item = self.try_get_entry_by_global_id(id)?;
1133 Some(item.id())
1134 }
1135
1136 pub fn get_schema(
1137 &self,
1138 database_spec: &ResolvedDatabaseSpecifier,
1139 schema_spec: &SchemaSpecifier,
1140 conn_id: &ConnectionId,
1141 ) -> &Schema {
1142 self.state.get_schema(database_spec, schema_spec, conn_id)
1143 }
1144
1145 pub fn try_get_schema(
1146 &self,
1147 database_spec: &ResolvedDatabaseSpecifier,
1148 schema_spec: &SchemaSpecifier,
1149 conn_id: &ConnectionId,
1150 ) -> Option<&Schema> {
1151 self.state
1152 .try_get_schema(database_spec, schema_spec, conn_id)
1153 }
1154
1155 pub fn get_mz_catalog_schema_id(&self) -> SchemaId {
1156 self.state.get_mz_catalog_schema_id()
1157 }
1158
1159 pub fn get_pg_catalog_schema_id(&self) -> SchemaId {
1160 self.state.get_pg_catalog_schema_id()
1161 }
1162
1163 pub fn get_information_schema_id(&self) -> SchemaId {
1164 self.state.get_information_schema_id()
1165 }
1166
1167 pub fn get_mz_internal_schema_id(&self) -> SchemaId {
1168 self.state.get_mz_internal_schema_id()
1169 }
1170
1171 pub fn get_mz_introspection_schema_id(&self) -> SchemaId {
1172 self.state.get_mz_introspection_schema_id()
1173 }
1174
1175 pub fn get_mz_unsafe_schema_id(&self) -> SchemaId {
1176 self.state.get_mz_unsafe_schema_id()
1177 }
1178
1179 pub fn system_schema_ids(&self) -> impl Iterator<Item = SchemaId> + '_ {
1180 self.state.system_schema_ids()
1181 }
1182
1183 pub fn get_database(&self, id: &DatabaseId) -> &Database {
1184 self.state.get_database(id)
1185 }
1186
1187 pub fn try_get_role(&self, id: &RoleId) -> Option<&Role> {
1188 self.state.try_get_role(id)
1189 }
1190
1191 pub fn get_role(&self, id: &RoleId) -> &Role {
1192 self.state.get_role(id)
1193 }
1194
1195 pub fn try_get_role_by_name(&self, role_name: &str) -> Option<&Role> {
1196 self.state.try_get_role_by_name(role_name)
1197 }
1198
1199 pub fn try_get_role_auth_by_id(&self, id: &RoleId) -> Option<&RoleAuth> {
1200 self.state.try_get_role_auth_by_id(id)
1201 }
1202
1203 pub fn register_temporary_namespace(&mut self, conn_id: &ConnectionId, uuid: Uuid) {
1213 self.state
1214 .temporary_namespaces
1215 .register(conn_id.clone(), uuid);
1216 }
1217
1218 fn item_exists_in_temp_schemas(&self, conn_id: &ConnectionId, item_name: &str) -> bool {
1219 self.state
1222 .temporary_namespaces
1223 .schema(conn_id)
1224 .map(|schema| schema.items.contains_key(item_name))
1225 .unwrap_or(false)
1226 }
1227
1228 pub fn drop_temporary_namespace(&mut self, conn_id: &ConnectionId) {
1230 self.state.temporary_namespaces.unregister(conn_id)
1231 }
1232
1233 pub(crate) fn object_dependents(
1234 &self,
1235 object_ids: &Vec<ObjectId>,
1236 conn_id: &ConnectionId,
1237 ) -> Vec<ObjectId> {
1238 let mut seen = BTreeSet::new();
1239 self.state.object_dependents(object_ids, conn_id, &mut seen)
1240 }
1241
1242 fn full_name_detail(name: &FullItemName) -> FullNameV1 {
1243 FullNameV1 {
1244 database: name.database.to_string(),
1245 schema: name.schema.clone(),
1246 item: name.item.clone(),
1247 }
1248 }
1249
1250 pub fn find_available_cluster_name(&self, name: &str) -> String {
1251 let mut i = 0;
1252 let mut candidate = name.to_string();
1253 while self.state.clusters_by_name.contains_key(&candidate) {
1254 i += 1;
1255 candidate = format!("{}{}", name, i);
1256 }
1257 candidate
1258 }
1259
1260 pub fn get_role_allowed_cluster_sizes(&self, role_id: &Option<RoleId>) -> Vec<String> {
1261 if role_id == &Some(MZ_SYSTEM_ROLE_ID) {
1264 self.cluster_replica_sizes().0.keys().cloned().collect()
1265 } else {
1266 self.system_config().allowed_cluster_replica_sizes()
1267 }
1268 }
1269
1270 pub fn concretize_replica_location(
1271 &self,
1272 location: mz_catalog::durable::ReplicaLocation,
1273 allowed_sizes: &Vec<String>,
1274 allowed_availability_zones: Option<&[String]>,
1275 allow_disabled: bool,
1276 ) -> Result<ReplicaLocation, Error> {
1277 self.state.concretize_replica_location(
1278 location,
1279 allowed_sizes,
1280 allowed_availability_zones,
1281 allow_disabled,
1282 )
1283 }
1284
1285 pub(crate) fn ensure_valid_replica_size(
1286 &self,
1287 allowed_sizes: &[String],
1288 size: &String,
1289 allow_disabled: bool,
1290 ) -> Result<(), Error> {
1291 self.state
1292 .ensure_valid_replica_size(allowed_sizes, size, allow_disabled)
1293 }
1294
1295 pub fn cluster_replica_sizes(&self) -> &ClusterReplicaSizeMap {
1296 self.state.cluster_replica_sizes()
1297 }
1298
1299 pub fn get_privileges(
1301 &self,
1302 id: &SystemObjectId,
1303 conn_id: &ConnectionId,
1304 ) -> Option<&PrivilegeMap> {
1305 match id {
1306 SystemObjectId::Object(id) => match id {
1307 ObjectId::Cluster(id) => Some(self.get_cluster(*id).privileges()),
1308 ObjectId::Database(id) => Some(self.get_database(id).privileges()),
1309 ObjectId::Schema((database_spec, schema_spec)) => Some(
1310 self.get_schema(database_spec, schema_spec, conn_id)
1311 .privileges(),
1312 ),
1313 ObjectId::Item(id) => Some(self.get_entry(id).privileges()),
1314 ObjectId::ClusterReplica(_) | ObjectId::Role(_) => None,
1315 ObjectId::NetworkPolicy(id) => Some(self.get_network_policy(*id).privileges()),
1316 },
1317 SystemObjectId::System => Some(&self.state.system_privileges),
1318 }
1319 }
1320
1321 #[mz_ore::instrument(level = "debug")]
1327 pub async fn advance_upper(&self, new_upper: mz_repr::Timestamp) -> Result<(), AdapterError> {
1328 Ok(self.storage().await.advance_upper(new_upper).await?)
1329 }
1330
1331 pub fn upper_handle(&self) -> CatalogUpperHandle {
1333 CatalogUpperHandle {
1334 storage: Arc::clone(&self.storage),
1335 }
1336 }
1337
1338 pub fn introspection_dependencies(&self, id: CatalogItemId) -> Vec<CatalogItemId> {
1340 self.state.introspection_dependencies(id)
1341 }
1342
1343 pub fn dump(&self) -> Result<CatalogDump, Error> {
1349 Ok(CatalogDump::new(self.state.dump(None)?))
1350 }
1351
1352 pub fn check_consistency(&self) -> Result<(), serde_json::Value> {
1356 self.state.check_consistency().map_err(|inconsistencies| {
1357 serde_json::to_value(inconsistencies).unwrap_or_else(|_| {
1358 serde_json::Value::String("failed to serialize inconsistencies".to_string())
1359 })
1360 })
1361 }
1362
1363 pub fn config(&self) -> &mz_sql::catalog::CatalogConfig {
1364 self.state.config()
1365 }
1366
1367 pub fn entries(&self) -> impl Iterator<Item = &CatalogEntry> {
1368 self.state.entries()
1369 }
1370
1371 pub fn user_connections(&self) -> impl Iterator<Item = &CatalogEntry> {
1372 self.state.user_connections()
1373 }
1374
1375 pub fn user_tables(&self) -> impl Iterator<Item = &CatalogEntry> {
1376 self.state.user_tables()
1377 }
1378
1379 pub fn user_sources(&self) -> impl Iterator<Item = &CatalogEntry> {
1380 self.state.user_sources()
1381 }
1382
1383 pub fn user_sinks(&self) -> impl Iterator<Item = &CatalogEntry> {
1384 self.state.user_sinks()
1385 }
1386
1387 pub fn user_materialized_views(&self) -> impl Iterator<Item = &CatalogEntry> {
1388 self.state.user_materialized_views()
1389 }
1390
1391 pub fn user_secrets(&self) -> impl Iterator<Item = &CatalogEntry> {
1392 self.state.user_secrets()
1393 }
1394
1395 pub fn get_network_policy(&self, network_policy_id: NetworkPolicyId) -> &NetworkPolicy {
1396 self.state.get_network_policy(&network_policy_id)
1397 }
1398
1399 pub fn get_network_policy_by_name(&self, name: &str) -> Option<&NetworkPolicy> {
1400 self.state.try_get_network_policy_by_name(name)
1401 }
1402
1403 pub fn clusters(&self) -> impl Iterator<Item = &Cluster> {
1404 self.state.clusters()
1405 }
1406
1407 pub fn get_cluster(&self, cluster_id: ClusterId) -> &Cluster {
1408 self.state.get_cluster(cluster_id)
1409 }
1410
1411 pub fn try_get_cluster(&self, cluster_id: ClusterId) -> Option<&Cluster> {
1412 self.state.try_get_cluster(cluster_id)
1413 }
1414
1415 pub fn user_clusters(&self) -> impl Iterator<Item = &Cluster> {
1416 self.state.user_clusters()
1417 }
1418
1419 pub fn get_cluster_replica(
1420 &self,
1421 cluster_id: ClusterId,
1422 replica_id: ReplicaId,
1423 ) -> &ClusterReplica {
1424 self.state.get_cluster_replica(cluster_id, replica_id)
1425 }
1426
1427 pub fn try_get_cluster_replica(
1428 &self,
1429 cluster_id: ClusterId,
1430 replica_id: ReplicaId,
1431 ) -> Option<&ClusterReplica> {
1432 self.state.try_get_cluster_replica(cluster_id, replica_id)
1433 }
1434
1435 pub fn user_cluster_replicas(&self) -> impl Iterator<Item = &ClusterReplica> {
1436 self.state.user_cluster_replicas()
1437 }
1438
1439 pub fn databases(&self) -> impl Iterator<Item = &Database> {
1440 self.state.databases()
1441 }
1442
1443 pub fn user_roles(&self) -> impl Iterator<Item = &Role> {
1444 self.state.user_roles()
1445 }
1446
1447 pub fn user_network_policies(&self) -> impl Iterator<Item = &NetworkPolicy> {
1448 self.state.user_network_policies()
1449 }
1450
1451 pub fn system_privileges(&self) -> &PrivilegeMap {
1452 &self.state.system_privileges
1453 }
1454
1455 pub fn default_privileges(
1456 &self,
1457 ) -> impl Iterator<
1458 Item = (
1459 &DefaultPrivilegeObject,
1460 impl Iterator<Item = &DefaultPrivilegeAclItem>,
1461 ),
1462 > {
1463 self.state.default_privileges.iter()
1464 }
1465
1466 pub fn pack_item_update(&self, id: CatalogItemId, diff: Diff) -> Vec<BuiltinTableUpdate> {
1467 self.state
1468 .resolve_builtin_table_updates(self.state.pack_item_update(id, diff))
1469 }
1470
1471 pub fn pack_storage_usage_update(
1472 &self,
1473 event: VersionedStorageUsage,
1474 diff: Diff,
1475 ) -> BuiltinTableUpdate {
1476 self.state
1477 .resolve_builtin_table_update(self.state.pack_storage_usage_update(event, diff))
1478 }
1479
1480 pub fn system_config(&self) -> &SystemVars {
1481 self.state.system_config()
1482 }
1483
1484 pub fn system_config_mut(&mut self) -> &mut SystemVars {
1485 self.state.system_config_mut()
1486 }
1487
1488 pub fn ensure_not_reserved_role(&self, role_id: &RoleId) -> Result<(), Error> {
1489 self.state.ensure_not_reserved_role(role_id)
1490 }
1491
1492 pub fn ensure_grantable_role(&self, role_id: &RoleId) -> Result<(), Error> {
1493 self.state.ensure_grantable_role(role_id)
1494 }
1495
1496 pub fn ensure_not_system_role(&self, role_id: &RoleId) -> Result<(), Error> {
1497 self.state.ensure_not_system_role(role_id)
1498 }
1499
1500 pub fn ensure_not_predefined_role(&self, role_id: &RoleId) -> Result<(), Error> {
1501 self.state.ensure_not_predefined_role(role_id)
1502 }
1503
1504 pub fn ensure_not_reserved_network_policy(
1505 &self,
1506 network_policy_id: &NetworkPolicyId,
1507 ) -> Result<(), Error> {
1508 self.state
1509 .ensure_not_reserved_network_policy(network_policy_id)
1510 }
1511
1512 pub fn ensure_not_reserved_object(
1513 &self,
1514 object_id: &ObjectId,
1515 conn_id: &ConnectionId,
1516 ) -> Result<(), Error> {
1517 match object_id {
1518 ObjectId::Cluster(cluster_id) => {
1519 if cluster_id.is_system() {
1520 let cluster = self.get_cluster(*cluster_id);
1521 Err(Error::new(ErrorKind::ReadOnlyCluster(
1522 cluster.name().to_string(),
1523 )))
1524 } else {
1525 Ok(())
1526 }
1527 }
1528 ObjectId::ClusterReplica((cluster_id, replica_id)) => {
1529 if replica_id.is_system() {
1530 let replica = self.get_cluster_replica(*cluster_id, *replica_id);
1531 Err(Error::new(ErrorKind::ReadOnlyClusterReplica(
1532 replica.name().to_string(),
1533 )))
1534 } else {
1535 Ok(())
1536 }
1537 }
1538 ObjectId::Database(database_id) => {
1539 if database_id.is_system() {
1540 let database = self.get_database(database_id);
1541 Err(Error::new(ErrorKind::ReadOnlyDatabase(
1542 database.name().to_string(),
1543 )))
1544 } else {
1545 Ok(())
1546 }
1547 }
1548 ObjectId::Schema((database_spec, schema_spec)) => {
1549 if schema_spec.is_system() {
1550 let schema = self.get_schema(database_spec, schema_spec, conn_id);
1551 Err(Error::new(ErrorKind::ReadOnlySystemSchema(
1552 schema.name().schema.clone(),
1553 )))
1554 } else {
1555 Ok(())
1556 }
1557 }
1558 ObjectId::Role(role_id) => self.ensure_not_reserved_role(role_id),
1559 ObjectId::Item(item_id) => {
1560 if item_id.is_system() {
1561 let item = self.get_entry(item_id);
1562 let name = self.resolve_full_name(item.name(), Some(conn_id));
1563 Err(Error::new(ErrorKind::ReadOnlyItem(name.to_string())))
1564 } else {
1565 Ok(())
1566 }
1567 }
1568 ObjectId::NetworkPolicy(network_policy_id) => {
1569 self.ensure_not_reserved_network_policy(network_policy_id)
1570 }
1571 }
1572 }
1573
1574 pub(crate) fn deserialize_plan_with_enable_for_item_parsing(
1576 &mut self,
1577 create_sql: &str,
1578 force_if_exists_skip: bool,
1579 ) -> Result<(Plan, ResolvedIds), AdapterError> {
1580 self.state
1581 .deserialize_plan_with_enable_for_item_parsing(create_sql, force_if_exists_skip)
1582 }
1583
1584 pub(crate) fn cache_expressions(
1596 &self,
1597 id: GlobalId,
1598 local_mir: Option<OptimizedMirRelationExpr>,
1599 mut global_mir: DataflowDescription<OptimizedMirRelationExpr>,
1600 mut physical_plan: mz_compute_types::dataflows::LirDataflowDescription,
1601 dataflow_metainfos: DataflowMetainfo<Arc<OptimizerNotice>>,
1602 optimizer_features: OptimizerFeatures,
1603 ) -> BoxFuture<'static, ()> {
1604 global_mir.as_of = None;
1608 global_mir.until = Default::default();
1609 physical_plan.as_of = None;
1610 physical_plan.until = Default::default();
1611
1612 let mut local_exprs = Vec::new();
1613 if let Some(local_mir) = local_mir {
1614 local_exprs.push((
1615 id,
1616 LocalExpressions {
1617 local_mir,
1618 optimizer_features: optimizer_features.clone(),
1619 item_version: RelationVersion::root(),
1620 },
1621 ));
1622 }
1623 let global_exprs = vec![(
1624 id,
1625 GlobalExpressions {
1626 global_mir,
1627 physical_plan,
1628 dataflow_metainfos,
1629 optimizer_features,
1630 item_version: RelationVersion::root(),
1631 },
1632 )];
1633 self.update_expression_cache(local_exprs, global_exprs, Default::default())
1634 }
1635
1636 pub(crate) fn cache_local_expression(
1640 &self,
1641 id: GlobalId,
1642 local_mir: OptimizedMirRelationExpr,
1643 optimizer_features: OptimizerFeatures,
1644 ) -> BoxFuture<'static, ()> {
1645 let local_exprs = vec![(
1646 id,
1647 LocalExpressions {
1648 local_mir,
1649 optimizer_features,
1650 item_version: RelationVersion::root(),
1651 },
1652 )];
1653 self.update_expression_cache(local_exprs, Vec::new(), Default::default())
1654 }
1655
1656 pub(crate) fn update_expression_cache<'a, 'b>(
1657 &'a self,
1658 new_local_expressions: Vec<(GlobalId, LocalExpressions)>,
1659 new_global_expressions: Vec<(GlobalId, GlobalExpressions)>,
1660 invalidate_ids: BTreeSet<GlobalId>,
1661 ) -> BoxFuture<'b, ()> {
1662 if let Some(expr_cache) = &self.expr_cache_handle {
1663 expr_cache
1664 .update(
1665 new_local_expressions,
1666 new_global_expressions,
1667 invalidate_ids,
1668 )
1669 .boxed()
1670 } else {
1671 async {}.boxed()
1672 }
1673 }
1674
1675 #[cfg(test)]
1679 async fn sync_to_current_updates(
1680 &mut self,
1681 ) -> Result<
1682 (
1683 Vec<BuiltinTableUpdate<&'static BuiltinTable>>,
1684 Vec<ParsedStateUpdate>,
1685 ),
1686 CatalogError,
1687 > {
1688 let updates = self.storage().await.sync_to_current_updates().await?;
1689 let (builtin_table_updates, catalog_updates) = self
1690 .state
1691 .apply_updates(updates, &mut state::LocalExpressionCache::Closed)
1692 .await;
1693 Ok((builtin_table_updates, catalog_updates))
1694 }
1695}
1696
1697pub fn is_reserved_name(name: &str) -> bool {
1698 BUILTIN_PREFIXES
1699 .iter()
1700 .any(|prefix| name.starts_with(prefix))
1701}
1702
1703const RESERVED_ROLE_SPECIFICATION_NAMES: [&str; 5] = [
1713 "current_user",
1714 "current_role",
1715 "session_user",
1716 "user",
1717 "none",
1718];
1719
1720pub fn is_reserved_role_name(name: &str) -> bool {
1721 is_reserved_name(name)
1722 || is_public_role(name)
1723 || RESERVED_ROLE_SPECIFICATION_NAMES.contains(&name)
1724}
1725
1726pub fn is_public_role(name: &str) -> bool {
1727 name == &*PUBLIC_ROLE_NAME
1728}
1729
1730pub(crate) fn catalog_type_to_audit_object_type(sql_type: SqlCatalogItemType) -> ObjectType {
1731 object_type_to_audit_object_type(sql_type.into())
1732}
1733
1734pub(crate) fn comment_id_to_audit_object_type(id: CommentObjectId) -> ObjectType {
1735 match id {
1736 CommentObjectId::Table(_) => ObjectType::Table,
1737 CommentObjectId::View(_) => ObjectType::View,
1738 CommentObjectId::MaterializedView(_) => ObjectType::MaterializedView,
1739 CommentObjectId::Source(_) => ObjectType::Source,
1740 CommentObjectId::Sink(_) => ObjectType::Sink,
1741 CommentObjectId::MetricSink(_) => ObjectType::MetricSink,
1744 CommentObjectId::Index(_) => ObjectType::Index,
1745 CommentObjectId::Func(_) => ObjectType::Func,
1746 CommentObjectId::Connection(_) => ObjectType::Connection,
1747 CommentObjectId::Type(_) => ObjectType::Type,
1748 CommentObjectId::Secret(_) => ObjectType::Secret,
1749 CommentObjectId::Role(_) => ObjectType::Role,
1750 CommentObjectId::Database(_) => ObjectType::Database,
1751 CommentObjectId::Schema(_) => ObjectType::Schema,
1752 CommentObjectId::Cluster(_) => ObjectType::Cluster,
1753 CommentObjectId::ClusterReplica(_) => ObjectType::ClusterReplica,
1754 CommentObjectId::NetworkPolicy(_) => ObjectType::NetworkPolicy,
1755 }
1756}
1757
1758pub(crate) fn object_type_to_audit_object_type(
1759 object_type: mz_sql::catalog::ObjectType,
1760) -> ObjectType {
1761 system_object_type_to_audit_object_type(&SystemObjectType::Object(object_type))
1762}
1763
1764pub(crate) fn system_object_type_to_audit_object_type(
1765 system_type: &SystemObjectType,
1766) -> ObjectType {
1767 match system_type {
1768 SystemObjectType::Object(object_type) => match object_type {
1769 mz_sql::catalog::ObjectType::Table => ObjectType::Table,
1770 mz_sql::catalog::ObjectType::View => ObjectType::View,
1771 mz_sql::catalog::ObjectType::MaterializedView => ObjectType::MaterializedView,
1772 mz_sql::catalog::ObjectType::Source => ObjectType::Source,
1773 mz_sql::catalog::ObjectType::Sink => ObjectType::Sink,
1774 mz_sql::catalog::ObjectType::MetricSink => ObjectType::MetricSink,
1775 mz_sql::catalog::ObjectType::Index => ObjectType::Index,
1776 mz_sql::catalog::ObjectType::Type => ObjectType::Type,
1777 mz_sql::catalog::ObjectType::Role => ObjectType::Role,
1778 mz_sql::catalog::ObjectType::Cluster => ObjectType::Cluster,
1779 mz_sql::catalog::ObjectType::ClusterReplica => ObjectType::ClusterReplica,
1780 mz_sql::catalog::ObjectType::Secret => ObjectType::Secret,
1781 mz_sql::catalog::ObjectType::Connection => ObjectType::Connection,
1782 mz_sql::catalog::ObjectType::Database => ObjectType::Database,
1783 mz_sql::catalog::ObjectType::Schema => ObjectType::Schema,
1784 mz_sql::catalog::ObjectType::Func => ObjectType::Func,
1785 mz_sql::catalog::ObjectType::NetworkPolicy => ObjectType::NetworkPolicy,
1786 },
1787 SystemObjectType::System => ObjectType::System,
1788 }
1789}
1790
1791#[derive(Debug, Copy, Clone)]
1792pub enum UpdatePrivilegeVariant {
1793 Grant,
1794 Revoke,
1795}
1796
1797impl From<UpdatePrivilegeVariant> for ExecuteResponse {
1798 fn from(variant: UpdatePrivilegeVariant) -> Self {
1799 match variant {
1800 UpdatePrivilegeVariant::Grant => ExecuteResponse::GrantedPrivilege,
1801 UpdatePrivilegeVariant::Revoke => ExecuteResponse::RevokedPrivilege,
1802 }
1803 }
1804}
1805
1806impl From<UpdatePrivilegeVariant> for EventType {
1807 fn from(variant: UpdatePrivilegeVariant) -> Self {
1808 match variant {
1809 UpdatePrivilegeVariant::Grant => EventType::Grant,
1810 UpdatePrivilegeVariant::Revoke => EventType::Revoke,
1811 }
1812 }
1813}
1814
1815impl ConnCatalog<'_> {
1816 fn resolve_item_name(
1817 &self,
1818 name: &PartialItemName,
1819 ) -> Result<&QualifiedItemName, SqlCatalogError> {
1820 self.resolve_item(name).map(|entry| entry.name())
1821 }
1822
1823 fn resolve_function_name(
1824 &self,
1825 name: &PartialItemName,
1826 ) -> Result<&QualifiedItemName, SqlCatalogError> {
1827 self.resolve_function(name).map(|entry| entry.name())
1828 }
1829
1830 fn resolve_type_name(
1831 &self,
1832 name: &PartialItemName,
1833 ) -> Result<&QualifiedItemName, SqlCatalogError> {
1834 self.resolve_type(name).map(|entry| entry.name())
1835 }
1836}
1837
1838impl ExprHumanizer for ConnCatalog<'_> {
1839 fn humanize_id(&self, id: GlobalId) -> Option<String> {
1840 let entry = self.state.try_get_entry_by_global_id(&id)?;
1841 Some(self.resolve_full_name(entry.name()).to_string())
1842 }
1843
1844 fn humanize_id_unqualified(&self, id: GlobalId) -> Option<String> {
1845 let entry = self.state.try_get_entry_by_global_id(&id)?;
1846 Some(entry.name().item.clone())
1847 }
1848
1849 fn humanize_id_parts(&self, id: GlobalId) -> Option<Vec<String>> {
1850 let entry = self.state.try_get_entry_by_global_id(&id)?;
1851 Some(self.resolve_full_name(entry.name()).into_parts())
1852 }
1853
1854 fn humanize_sql_scalar_type(&self, typ: &SqlScalarType, postgres_compat: bool) -> String {
1855 use SqlScalarType::*;
1856
1857 match typ {
1858 Array(t) => format!("{}[]", self.humanize_sql_scalar_type(t, postgres_compat)),
1859 List {
1860 custom_id: Some(item_id),
1861 ..
1862 }
1863 | Map {
1864 custom_id: Some(item_id),
1865 ..
1866 } => {
1867 let item = self.get_item(item_id);
1868 self.minimal_qualification(item.name()).to_string()
1869 }
1870 List { element_type, .. } => {
1871 format!(
1872 "{} list",
1873 self.humanize_sql_scalar_type(element_type, postgres_compat)
1874 )
1875 }
1876 Map { value_type, .. } => format!(
1877 "map[{}=>{}]",
1878 self.humanize_sql_scalar_type(&SqlScalarType::String, postgres_compat),
1879 self.humanize_sql_scalar_type(value_type, postgres_compat)
1880 ),
1881 Record {
1882 custom_id: Some(item_id),
1883 ..
1884 } => {
1885 let item = self.get_item(item_id);
1886 self.minimal_qualification(item.name()).to_string()
1887 }
1888 Record { fields, .. } => format!(
1889 "record({})",
1890 fields
1891 .iter()
1892 .map(|f| format!(
1893 "{}: {}",
1894 f.0,
1895 self.humanize_sql_column_type(&f.1, postgres_compat)
1896 ))
1897 .join(",")
1898 ),
1899 PgLegacyChar => "\"char\"".into(),
1900 Char { length } if !postgres_compat => match length {
1901 None => "char".into(),
1902 Some(length) => format!("char({})", length.into_u32()),
1903 },
1904 VarChar { max_length } if !postgres_compat => match max_length {
1905 None => "varchar".into(),
1906 Some(length) => format!("varchar({})", length.into_u32()),
1907 },
1908 UInt16 => "uint2".into(),
1909 UInt32 => "uint4".into(),
1910 UInt64 => "uint8".into(),
1911 ty => {
1912 let pgrepr_type = mz_pgrepr::Type::from(ty);
1913 let pg_catalog_schema = SchemaSpecifier::Id(self.state.get_pg_catalog_schema_id());
1914
1915 let res = if self
1916 .effective_search_path(true)
1917 .iter()
1918 .any(|(_, schema)| schema == &pg_catalog_schema)
1919 {
1920 pgrepr_type.name().to_string()
1921 } else {
1922 let name = QualifiedItemName {
1925 qualifiers: ItemQualifiers {
1926 database_spec: ResolvedDatabaseSpecifier::Ambient,
1927 schema_spec: pg_catalog_schema,
1928 },
1929 item: pgrepr_type.name().to_string(),
1930 };
1931 self.resolve_full_name(&name).to_string()
1932 };
1933 res
1934 }
1935 }
1936 }
1937
1938 fn column_names_for_id(&self, id: GlobalId) -> Option<Vec<String>> {
1939 let entry = self.state.try_get_entry_by_global_id(&id)?;
1940
1941 match entry.index() {
1942 Some(index) => {
1943 let on_desc = self.state.try_get_desc_by_global_id(&index.on)?;
1944 let mut on_names = on_desc
1945 .iter_names()
1946 .map(|col_name| col_name.to_string())
1947 .collect::<Vec<_>>();
1948
1949 let (p, _) = mz_expr::permutation_for_arrangement(&index.keys, on_desc.arity());
1950
1951 let ix_arity = p.iter().map(|x| *x + 1).max().unwrap_or(0);
1955 let mut ix_names = vec![String::new(); ix_arity];
1956
1957 for (on_pos, ix_pos) in p.into_iter().enumerate() {
1959 let on_name = on_names.get_mut(on_pos).expect("on_name");
1960 let ix_name = ix_names.get_mut(ix_pos).expect("ix_name");
1961 std::mem::swap(on_name, ix_name);
1962 }
1963
1964 Some(ix_names) }
1966 None => {
1967 let desc = self.state.try_get_desc_by_global_id(&id)?;
1968 let column_names = desc
1969 .iter_names()
1970 .map(|col_name| col_name.to_string())
1971 .collect();
1972
1973 Some(column_names)
1974 }
1975 }
1976 }
1977
1978 fn humanize_column(&self, id: GlobalId, column: usize) -> Option<String> {
1979 let desc = self.state.try_get_desc_by_global_id(&id)?;
1980 Some(desc.get_name(column).to_string())
1981 }
1982
1983 fn id_exists(&self, id: GlobalId) -> bool {
1984 self.state.entry_by_global_id.contains_key(&id)
1985 }
1986}
1987
1988impl SessionCatalog for ConnCatalog<'_> {
1989 fn active_role_id(&self) -> &RoleId {
1990 &self.role_id
1991 }
1992
1993 fn restrict_to_user_objects(&self) -> bool {
1994 self.restrict_to_user_objects
1995 }
1996
1997 fn get_prepared_statement_desc(&self, name: &str) -> Option<&StatementDesc> {
1998 self.prepared_statements
1999 .as_ref()
2000 .map(|ps| ps.get(name).map(|ps| ps.desc()))
2001 .flatten()
2002 }
2003
2004 fn get_portal_desc_unverified(&self, portal_name: &str) -> Option<&StatementDesc> {
2005 self.portals
2006 .and_then(|portals| portals.get(portal_name).map(|portal| &portal.desc))
2007 }
2008
2009 fn active_database(&self) -> Option<&DatabaseId> {
2010 self.database.as_ref()
2011 }
2012
2013 fn active_cluster(&self) -> &str {
2014 &self.cluster
2015 }
2016
2017 fn search_path(&self) -> &[(ResolvedDatabaseSpecifier, SchemaSpecifier)] {
2018 &self.search_path
2019 }
2020
2021 fn resolve_database(
2022 &self,
2023 database_name: &str,
2024 ) -> Result<&dyn mz_sql::catalog::CatalogDatabase, SqlCatalogError> {
2025 Ok(self.state.resolve_database(database_name)?)
2026 }
2027
2028 fn get_database(&self, id: &DatabaseId) -> &dyn mz_sql::catalog::CatalogDatabase {
2029 self.state
2030 .database_by_id
2031 .get(id)
2032 .expect("database doesn't exist")
2033 }
2034
2035 #[allow(clippy::as_conversions)]
2037 fn get_databases(&self) -> Vec<&dyn CatalogDatabase> {
2038 self.state
2039 .database_by_id
2040 .values()
2041 .map(|database| database as &dyn CatalogDatabase)
2042 .collect()
2043 }
2044
2045 fn resolve_schema(
2046 &self,
2047 database_name: Option<&str>,
2048 schema_name: &str,
2049 ) -> Result<&dyn mz_sql::catalog::CatalogSchema, SqlCatalogError> {
2050 Ok(self.state.resolve_schema(
2051 self.database.as_ref(),
2052 database_name,
2053 schema_name,
2054 &self.conn_id,
2055 )?)
2056 }
2057
2058 fn resolve_schema_in_database(
2059 &self,
2060 database_spec: &ResolvedDatabaseSpecifier,
2061 schema_name: &str,
2062 ) -> Result<&dyn mz_sql::catalog::CatalogSchema, SqlCatalogError> {
2063 Ok(self
2064 .state
2065 .resolve_schema_in_database(database_spec, schema_name, &self.conn_id)?)
2066 }
2067
2068 fn get_schema(
2069 &self,
2070 database_spec: &ResolvedDatabaseSpecifier,
2071 schema_spec: &SchemaSpecifier,
2072 ) -> &dyn CatalogSchema {
2073 self.state
2074 .get_schema(database_spec, schema_spec, &self.conn_id)
2075 }
2076
2077 #[allow(clippy::as_conversions)]
2079 fn get_schemas(&self) -> Vec<&dyn CatalogSchema> {
2080 self.get_databases()
2081 .into_iter()
2082 .flat_map(|database| database.schemas().into_iter())
2083 .chain(
2084 self.state
2085 .ambient_schemas_by_id
2086 .values()
2087 .chain(self.state.temporary_namespaces.schemas())
2088 .map(|schema| schema as &dyn CatalogSchema),
2089 )
2090 .collect()
2091 }
2092
2093 fn get_mz_internal_schema_id(&self) -> SchemaId {
2094 self.state().get_mz_internal_schema_id()
2095 }
2096
2097 fn get_mz_unsafe_schema_id(&self) -> SchemaId {
2098 self.state().get_mz_unsafe_schema_id()
2099 }
2100
2101 fn is_system_schema_specifier(&self, schema: SchemaSpecifier) -> bool {
2102 self.state.is_system_schema_specifier(schema)
2103 }
2104
2105 fn resolve_role(
2106 &self,
2107 role_name: &str,
2108 ) -> Result<&dyn mz_sql::catalog::CatalogRole, SqlCatalogError> {
2109 match self.state.try_get_role_by_name(role_name) {
2110 Some(role) => Ok(role),
2111 None => Err(SqlCatalogError::UnknownRole(role_name.into())),
2112 }
2113 }
2114
2115 fn resolve_network_policy(
2116 &self,
2117 policy_name: &str,
2118 ) -> Result<&dyn mz_sql::catalog::CatalogNetworkPolicy, SqlCatalogError> {
2119 match self.state.try_get_network_policy_by_name(policy_name) {
2120 Some(policy) => Ok(policy),
2121 None => Err(SqlCatalogError::UnknownNetworkPolicy(policy_name.into())),
2122 }
2123 }
2124
2125 fn try_get_role(&self, id: &RoleId) -> Option<&dyn CatalogRole> {
2126 Some(self.state.roles_by_id.get(id)?)
2127 }
2128
2129 fn get_role(&self, id: &RoleId) -> &dyn mz_sql::catalog::CatalogRole {
2130 self.state.get_role(id)
2131 }
2132
2133 fn get_roles(&self) -> Vec<&dyn CatalogRole> {
2134 #[allow(clippy::as_conversions)]
2136 self.state
2137 .roles_by_id
2138 .values()
2139 .map(|role| role as &dyn CatalogRole)
2140 .collect()
2141 }
2142
2143 fn mz_system_role_id(&self) -> RoleId {
2144 MZ_SYSTEM_ROLE_ID
2145 }
2146
2147 fn collect_role_membership(&self, id: &RoleId) -> BTreeSet<RoleId> {
2148 self.state.collect_role_membership(id)
2149 }
2150
2151 fn get_network_policy(
2152 &self,
2153 id: &NetworkPolicyId,
2154 ) -> &dyn mz_sql::catalog::CatalogNetworkPolicy {
2155 self.state.get_network_policy(id)
2156 }
2157
2158 fn get_network_policies(&self) -> Vec<&dyn mz_sql::catalog::CatalogNetworkPolicy> {
2159 #[allow(clippy::as_conversions)]
2161 self.state
2162 .network_policies_by_id
2163 .values()
2164 .map(|policy| policy as &dyn CatalogNetworkPolicy)
2165 .collect()
2166 }
2167
2168 fn resolve_cluster(
2169 &self,
2170 cluster_name: Option<&str>,
2171 ) -> Result<&dyn mz_sql::catalog::CatalogCluster<'_>, SqlCatalogError> {
2172 Ok(self
2173 .state
2174 .resolve_cluster(cluster_name.unwrap_or_else(|| self.active_cluster()))?)
2175 }
2176
2177 fn resolve_cluster_replica(
2178 &self,
2179 cluster_replica_name: &QualifiedReplica,
2180 ) -> Result<&dyn CatalogClusterReplica<'_>, SqlCatalogError> {
2181 Ok(self.state.resolve_cluster_replica(cluster_replica_name)?)
2182 }
2183
2184 fn resolve_item(
2185 &self,
2186 name: &PartialItemName,
2187 ) -> Result<&dyn mz_sql::catalog::CatalogItem, SqlCatalogError> {
2188 let r = self.state.resolve_entry(
2189 self.database.as_ref(),
2190 &self.effective_search_path(true),
2191 name,
2192 &self.conn_id,
2193 )?;
2194 if self.unresolvable_ids.contains(&r.id()) {
2195 Err(SqlCatalogError::UnknownItem(name.to_string()))
2196 } else {
2197 Ok(r)
2198 }
2199 }
2200
2201 fn resolve_function(
2202 &self,
2203 name: &PartialItemName,
2204 ) -> Result<&dyn mz_sql::catalog::CatalogItem, SqlCatalogError> {
2205 let r = self.state.resolve_function(
2206 self.database.as_ref(),
2207 &self.effective_search_path(false),
2208 name,
2209 &self.conn_id,
2210 )?;
2211
2212 if self.unresolvable_ids.contains(&r.id()) {
2213 Err(SqlCatalogError::UnknownFunction {
2214 name: name.to_string(),
2215 alternative: None,
2216 })
2217 } else {
2218 Ok(r)
2219 }
2220 }
2221
2222 fn resolve_type(
2223 &self,
2224 name: &PartialItemName,
2225 ) -> Result<&dyn mz_sql::catalog::CatalogItem, SqlCatalogError> {
2226 let r = self.state.resolve_type(
2227 self.database.as_ref(),
2228 &self.effective_search_path(false),
2229 name,
2230 &self.conn_id,
2231 )?;
2232
2233 if self.unresolvable_ids.contains(&r.id()) {
2234 Err(SqlCatalogError::UnknownType {
2235 name: name.to_string(),
2236 })
2237 } else {
2238 Ok(r)
2239 }
2240 }
2241
2242 fn get_system_type(&self, name: &str) -> &dyn mz_sql::catalog::CatalogItem {
2243 self.state.get_system_type(name)
2244 }
2245
2246 fn try_get_item(&self, id: &CatalogItemId) -> Option<&dyn mz_sql::catalog::CatalogItem> {
2247 Some(self.state.try_get_entry(id)?)
2248 }
2249
2250 fn try_get_item_by_global_id(
2251 &self,
2252 id: &GlobalId,
2253 ) -> Option<Box<dyn mz_sql::catalog::CatalogCollectionItem>> {
2254 let entry = self.state.try_get_entry_by_global_id(id)?;
2255 let entry = match &entry.item {
2256 CatalogItem::Table(table) => {
2257 let (version, _gid) = table
2258 .collections
2259 .iter()
2260 .find(|(_version, gid)| *gid == id)
2261 .expect("catalog out of sync, mismatched GlobalId");
2262 entry.at_version(RelationVersionSelector::Specific(*version))
2263 }
2264 _ => entry.at_version(RelationVersionSelector::Latest),
2265 };
2266 Some(entry)
2267 }
2268
2269 fn get_item(&self, id: &CatalogItemId) -> &dyn mz_sql::catalog::CatalogItem {
2270 self.state.get_entry(id)
2271 }
2272
2273 fn get_item_by_global_id(
2274 &self,
2275 id: &GlobalId,
2276 ) -> Box<dyn mz_sql::catalog::CatalogCollectionItem> {
2277 let entry = self.state.get_entry_by_global_id(id);
2278 let entry = match &entry.item {
2279 CatalogItem::Table(table) => {
2280 let (version, _gid) = table
2281 .collections
2282 .iter()
2283 .find(|(_version, gid)| *gid == id)
2284 .expect("catalog out of sync, mismatched GlobalId");
2285 entry.at_version(RelationVersionSelector::Specific(*version))
2286 }
2287 _ => entry.at_version(RelationVersionSelector::Latest),
2288 };
2289 entry
2290 }
2291
2292 fn get_items(&self) -> Vec<&dyn mz_sql::catalog::CatalogItem> {
2293 self.get_schemas()
2294 .into_iter()
2295 .flat_map(|schema| schema.item_ids())
2296 .map(|id| self.get_item(&id))
2297 .collect()
2298 }
2299
2300 fn get_item_by_name(&self, name: &QualifiedItemName) -> Option<&dyn SqlCatalogItem> {
2301 self.state
2302 .get_item_by_name(name, &self.conn_id)
2303 .map(|item| convert::identity::<&dyn SqlCatalogItem>(item))
2304 }
2305
2306 fn get_type_by_name(&self, name: &QualifiedItemName) -> Option<&dyn SqlCatalogItem> {
2307 self.state
2308 .get_type_by_name(name, &self.conn_id)
2309 .map(|item| convert::identity::<&dyn SqlCatalogItem>(item))
2310 }
2311
2312 fn get_cluster(&self, id: ClusterId) -> &dyn mz_sql::catalog::CatalogCluster<'_> {
2313 &self.state.clusters_by_id[&id]
2314 }
2315
2316 fn get_clusters(&self) -> Vec<&dyn mz_sql::catalog::CatalogCluster<'_>> {
2317 self.state
2318 .clusters_by_id
2319 .values()
2320 .map(|cluster| convert::identity::<&dyn mz_sql::catalog::CatalogCluster>(cluster))
2321 .collect()
2322 }
2323
2324 fn get_cluster_replica(
2325 &self,
2326 cluster_id: ClusterId,
2327 replica_id: ReplicaId,
2328 ) -> &dyn mz_sql::catalog::CatalogClusterReplica<'_> {
2329 let cluster = self.get_cluster(cluster_id);
2330 cluster.replica(replica_id)
2331 }
2332
2333 fn get_cluster_replicas(&self) -> Vec<&dyn mz_sql::catalog::CatalogClusterReplica<'_>> {
2334 self.get_clusters()
2335 .into_iter()
2336 .flat_map(|cluster| cluster.replicas().into_iter())
2337 .collect()
2338 }
2339
2340 fn get_system_privileges(&self) -> &PrivilegeMap {
2341 &self.state.system_privileges
2342 }
2343
2344 fn get_default_privileges(
2345 &self,
2346 ) -> Vec<(&DefaultPrivilegeObject, Vec<&DefaultPrivilegeAclItem>)> {
2347 self.state
2348 .default_privileges
2349 .iter()
2350 .map(|(object, acl_items)| (object, acl_items.collect()))
2351 .collect()
2352 }
2353
2354 fn find_available_name(&self, name: QualifiedItemName) -> QualifiedItemName {
2355 self.state.find_available_name(name, &self.conn_id)
2356 }
2357
2358 fn resolve_full_name(&self, name: &QualifiedItemName) -> FullItemName {
2359 self.state.resolve_full_name(name, Some(&self.conn_id))
2360 }
2361
2362 fn resolve_full_schema_name(&self, name: &QualifiedSchemaName) -> FullSchemaName {
2363 self.state.resolve_full_schema_name(name)
2364 }
2365
2366 fn resolve_item_id(&self, global_id: &GlobalId) -> CatalogItemId {
2367 self.state.get_entry_by_global_id(global_id).id()
2368 }
2369
2370 fn resolve_global_id(
2371 &self,
2372 item_id: &CatalogItemId,
2373 version: RelationVersionSelector,
2374 ) -> GlobalId {
2375 self.state
2376 .get_entry(item_id)
2377 .at_version(version)
2378 .global_id()
2379 }
2380
2381 fn config(&self) -> &mz_sql::catalog::CatalogConfig {
2382 self.state.config()
2383 }
2384
2385 fn now(&self) -> EpochMillis {
2386 (self.state.config().now)()
2387 }
2388
2389 fn aws_privatelink_availability_zones(&self) -> Option<BTreeSet<String>> {
2390 self.state.aws_privatelink_availability_zones.clone()
2391 }
2392
2393 fn system_vars(&self) -> &SystemVars {
2394 &self.state.system_configuration
2395 }
2396
2397 fn system_vars_mut(&mut self) -> &mut SystemVars {
2398 Arc::make_mut(&mut self.state.to_mut().system_configuration)
2399 }
2400
2401 fn get_owner_id(&self, id: &ObjectId) -> Option<RoleId> {
2402 self.state().get_owner_id(id, self.conn_id())
2403 }
2404
2405 fn get_privileges(&self, id: &SystemObjectId) -> Option<&PrivilegeMap> {
2406 match id {
2407 SystemObjectId::System => Some(&self.state.system_privileges),
2408 SystemObjectId::Object(ObjectId::Cluster(id)) => {
2409 Some(self.get_cluster(*id).privileges())
2410 }
2411 SystemObjectId::Object(ObjectId::Database(id)) => {
2412 Some(self.get_database(id).privileges())
2413 }
2414 SystemObjectId::Object(ObjectId::Schema((database_spec, schema_spec))) => {
2415 self.state
2418 .try_get_schema(database_spec, schema_spec, &self.conn_id)
2419 .map(|schema| schema.privileges())
2420 }
2421 SystemObjectId::Object(ObjectId::Item(id)) => Some(self.get_item(id).privileges()),
2422 SystemObjectId::Object(ObjectId::NetworkPolicy(id)) => {
2423 Some(self.get_network_policy(id).privileges())
2424 }
2425 SystemObjectId::Object(ObjectId::ClusterReplica(_))
2426 | SystemObjectId::Object(ObjectId::Role(_)) => None,
2427 }
2428 }
2429
2430 fn object_dependents(&self, ids: &Vec<ObjectId>) -> Vec<ObjectId> {
2431 let mut seen = BTreeSet::new();
2432 self.state.object_dependents(ids, &self.conn_id, &mut seen)
2433 }
2434
2435 fn item_dependents(&self, id: CatalogItemId) -> Vec<ObjectId> {
2436 let mut seen = BTreeSet::new();
2437 self.state.item_dependents(id, &mut seen)
2438 }
2439
2440 fn all_object_privileges(&self, object_type: mz_sql::catalog::SystemObjectType) -> AclMode {
2441 rbac::all_object_privileges(object_type)
2442 }
2443
2444 fn get_object_type(&self, object_id: &ObjectId) -> mz_sql::catalog::ObjectType {
2445 self.state.get_object_type(object_id)
2446 }
2447
2448 fn get_system_object_type(&self, id: &SystemObjectId) -> mz_sql::catalog::SystemObjectType {
2449 self.state.get_system_object_type(id)
2450 }
2451
2452 fn minimal_qualification(&self, qualified_name: &QualifiedItemName) -> PartialItemName {
2459 if qualified_name.qualifiers.schema_spec.is_temporary() {
2460 return qualified_name.item.clone().into();
2468 }
2469
2470 let database_id = match &qualified_name.qualifiers.database_spec {
2471 ResolvedDatabaseSpecifier::Ambient => None,
2472 ResolvedDatabaseSpecifier::Id(id)
2473 if self.database.is_some() && self.database == Some(*id) =>
2474 {
2475 None
2476 }
2477 ResolvedDatabaseSpecifier::Id(id) => Some(id.clone()),
2478 };
2479
2480 let schema_spec = if database_id.is_none()
2481 && self.resolve_item_name(&PartialItemName {
2482 database: None,
2483 schema: None,
2484 item: qualified_name.item.clone(),
2485 }) == Ok(qualified_name)
2486 || self.resolve_function_name(&PartialItemName {
2487 database: None,
2488 schema: None,
2489 item: qualified_name.item.clone(),
2490 }) == Ok(qualified_name)
2491 || self.resolve_type_name(&PartialItemName {
2492 database: None,
2493 schema: None,
2494 item: qualified_name.item.clone(),
2495 }) == Ok(qualified_name)
2496 {
2497 None
2498 } else {
2499 Some(qualified_name.qualifiers.schema_spec.clone())
2502 };
2503
2504 let res = PartialItemName {
2505 database: database_id.map(|id| self.get_database(&id).name().to_string()),
2506 schema: schema_spec.map(|spec| {
2507 self.get_schema(&qualified_name.qualifiers.database_spec, &spec)
2508 .name()
2509 .schema
2510 .clone()
2511 }),
2512 item: qualified_name.item.clone(),
2513 };
2514 assert!(
2515 self.resolve_item_name(&res) == Ok(qualified_name)
2516 || self.resolve_function_name(&res) == Ok(qualified_name)
2517 || self.resolve_type_name(&res) == Ok(qualified_name)
2518 );
2519 res
2520 }
2521
2522 fn add_notice(&self, notice: PlanNotice) {
2523 let _ = self.notices_tx.send(notice.into());
2524 }
2525
2526 fn get_item_comments(&self, id: &CatalogItemId) -> Option<&BTreeMap<Option<usize>, String>> {
2527 let comment_id = self.state.get_comment_id(ObjectId::Item(*id));
2528 self.state.comments.get_object_comments(comment_id)
2529 }
2530
2531 fn is_cluster_size_cc(&self, size: &str) -> bool {
2532 self.state
2533 .cluster_replica_sizes
2534 .0
2535 .get(size)
2536 .map_or(false, |a| a.is_cc)
2537 }
2538}
2539
2540#[cfg(test)]
2541mod source_preparation_tests;
2542
2543#[cfg(test)]
2544mod try_cast_tests;
2545
2546#[cfg(test)]
2547mod tests {
2548 use std::collections::{BTreeMap, BTreeSet};
2549 use std::sync::Arc;
2550 use std::{env, iter};
2551
2552 use itertools::Itertools;
2553 use mz_catalog::memory::objects::{
2554 CatalogItem, ClusterConfig, ClusterVariant, ClusterVariantManaged, ReconfigurationState,
2555 ReconfigurationStatus, ReconfigurationTarget,
2556 };
2557 use mz_controller::clusters::ReplicaLogging;
2558 use mz_postgres_util::{query, sql};
2559 use tokio_postgres::NoTls;
2560 use tokio_postgres::types::Type;
2561 use uuid::Uuid;
2562
2563 use mz_catalog::SYSTEM_CONN_ID;
2564 use mz_catalog::builtin::{BUILTINS, Builtin, BuiltinType};
2565 use mz_catalog::durable::{CatalogError, DurableCatalogError, FenceError, test_bootstrap_args};
2566 use mz_controller_types::{ClusterId, ReplicaId};
2567 use mz_expr::{Eval, MirScalarExpr};
2568 use mz_ore::now::to_datetime;
2569 use mz_ore::{assert_err, assert_ok, soft_assert_eq_or_log, task};
2570 use mz_persist_client::PersistClient;
2571 use mz_pgrepr::oid::{FIRST_MATERIALIZE_OID, FIRST_UNPINNED_OID, FIRST_USER_OID};
2572 use mz_repr::namespaces::{INFORMATION_SCHEMA, PG_CATALOG_SCHEMA};
2573 use mz_repr::role_id::RoleId;
2574 use mz_repr::{
2575 CatalogItemId, Datum, GlobalId, RelationVersionSelector, Row, RowArena, SqlRelationType,
2576 SqlScalarType, Timestamp,
2577 };
2578 use mz_sql::catalog::{CatalogSchema, CatalogType, SessionCatalog};
2579 use mz_sql::func::{Func, FuncImpl, OP_IMPLS, Operation};
2580 use mz_sql::names::{
2581 self, DatabaseId, ItemQualifiers, ObjectId, PartialItemName, QualifiedItemName,
2582 ResolvedDatabaseSpecifier, SchemaId, SchemaSpecifier, SystemObjectId,
2583 };
2584 use mz_sql::plan::{
2585 AutoScalingStrategy, CoercibleScalarExpr, ExprContext, HirScalarExpr, HirToMirConfig,
2586 OnHydration, OnTimeoutAction, PlanContext, QueryContext, QueryLifetime, Scope,
2587 StatementContext,
2588 };
2589 use mz_sql::session::user::MZ_SYSTEM_ROLE_ID;
2590 use mz_sql::session::vars::{SystemVars, VarInput};
2591
2592 use crate::catalog::state::LocalExpressionCache;
2593 use crate::catalog::{Catalog, DebugAwsContext, Op};
2594 use crate::optimize::dataflows::{EvalTime, ExprPrep, ExprPrepOneShot};
2595 use crate::session::Session;
2596
2597 #[mz_ore::test(tokio::test)]
2604 #[cfg_attr(miri, ignore)] async fn test_minimal_qualification() {
2606 Catalog::with_debug(|catalog| async move {
2607 struct TestCase {
2608 input: QualifiedItemName,
2609 system_output: PartialItemName,
2610 normal_output: PartialItemName,
2611 }
2612
2613 let test_cases = vec![
2614 TestCase {
2615 input: QualifiedItemName {
2616 qualifiers: ItemQualifiers {
2617 database_spec: ResolvedDatabaseSpecifier::Ambient,
2618 schema_spec: SchemaSpecifier::Id(catalog.get_pg_catalog_schema_id()),
2619 },
2620 item: "numeric".to_string(),
2621 },
2622 system_output: PartialItemName {
2623 database: None,
2624 schema: None,
2625 item: "numeric".to_string(),
2626 },
2627 normal_output: PartialItemName {
2628 database: None,
2629 schema: None,
2630 item: "numeric".to_string(),
2631 },
2632 },
2633 TestCase {
2634 input: QualifiedItemName {
2635 qualifiers: ItemQualifiers {
2636 database_spec: ResolvedDatabaseSpecifier::Ambient,
2637 schema_spec: SchemaSpecifier::Id(catalog.get_mz_catalog_schema_id()),
2638 },
2639 item: "mz_array_types".to_string(),
2640 },
2641 system_output: PartialItemName {
2642 database: None,
2643 schema: None,
2644 item: "mz_array_types".to_string(),
2645 },
2646 normal_output: PartialItemName {
2647 database: None,
2648 schema: None,
2649 item: "mz_array_types".to_string(),
2650 },
2651 },
2652 ];
2653
2654 for tc in test_cases {
2655 assert_eq!(
2656 catalog
2657 .for_system_session()
2658 .minimal_qualification(&tc.input),
2659 tc.system_output
2660 );
2661 assert_eq!(
2662 catalog
2663 .for_session(&Session::dummy())
2664 .minimal_qualification(&tc.input),
2665 tc.normal_output
2666 );
2667 }
2668 catalog.expire().await;
2669 })
2670 .await
2671 }
2672
2673 #[mz_ore::test(tokio::test)]
2674 #[cfg_attr(miri, ignore)] async fn test_catalog_revision() {
2676 let persist_client = PersistClient::new_for_tests().await;
2677 let organization_id = Uuid::new_v4();
2678 let bootstrap_args = test_bootstrap_args();
2679 {
2680 let mut catalog = Catalog::open_debug_catalog(
2681 persist_client.clone(),
2682 organization_id.clone(),
2683 &bootstrap_args,
2684 )
2685 .await
2686 .expect("unable to open debug catalog");
2687 assert_eq!(catalog.transient_revision(), 1);
2688 assert!(catalog.transient_revision_is_current());
2689 let snapshot = catalog.clone();
2690 let commit_ts = catalog.current_upper().await;
2691 catalog
2692 .transact(
2693 None,
2694 commit_ts,
2695 None,
2696 vec![Op::CreateDatabase {
2697 name: "test".to_string(),
2698 owner_id: MZ_SYSTEM_ROLE_ID,
2699 }],
2700 )
2701 .await
2702 .expect("failed to transact");
2703 assert_eq!(catalog.transient_revision(), 2);
2704 assert!(catalog.transient_revision_is_current());
2705 assert!(!snapshot.transient_revision_is_current());
2708 catalog.expire().await;
2709 }
2710 {
2711 let catalog =
2712 Catalog::open_debug_catalog(persist_client, organization_id, &bootstrap_args)
2713 .await
2714 .expect("unable to open debug catalog");
2715 assert_eq!(catalog.transient_revision(), 1);
2717 catalog.expire().await;
2718 }
2719 }
2720
2721 #[mz_ore::test(tokio::test)]
2722 #[cfg_attr(miri, ignore)] async fn test_builtin_cluster_replica_size_sync() {
2724 let persist_client = PersistClient::new_for_tests().await;
2725 let organization_id = Uuid::new_v4();
2726 let mut bootstrap_args = test_bootstrap_args();
2727 let in_use = bootstrap_args.default_cluster_replica_size.clone();
2729 let unused = "scale=1,workers=2".to_string();
2730 let changed = "scale=1,workers=4".to_string();
2731 let cluster_only = "scale=1,workers=8".to_string();
2732 let hydration = "scale=1,workers=16".to_string();
2733 let timed_out_target = "scale=1,workers=32".to_string();
2734
2735 let mut catalog =
2736 Catalog::open_debug_catalog(persist_client.clone(), organization_id, &bootstrap_args)
2737 .await
2738 .expect("unable to open debug catalog");
2739 assert_eq!(
2740 catalog.cluster_replica_sizes().0,
2741 bootstrap_args.cluster_replica_size_map.0
2742 );
2743 let commit_ts = catalog.current_upper().await;
2746 let cluster_id = catalog
2747 .allocate_user_cluster_id(commit_ts)
2748 .await
2749 .expect("allocate cluster id");
2750 let config = ClusterConfig {
2751 variant: ClusterVariant::Managed(ClusterVariantManaged {
2752 size: cluster_only.clone(),
2753 availability_zones: Vec::new(),
2754 logging: ReplicaLogging {
2755 log_logging: false,
2756 interval: None,
2757 },
2758 arrangement_compression: false,
2759 replication_factor: 0,
2760 optimizer_feature_overrides: Default::default(),
2761 schedule: Default::default(),
2762 auto_scaling_strategy: Some(AutoScalingStrategy {
2763 on_hydration: Some(OnHydration {
2764 hydration_size: hydration.clone(),
2765 linger_duration: None,
2766 }),
2767 }),
2768 reconfiguration: Some(ReconfigurationState {
2769 target: ReconfigurationTarget {
2770 size: timed_out_target.clone(),
2771 replication_factor: 1,
2772 availability_zones: Vec::new(),
2773 logging: ReplicaLogging {
2774 log_logging: false,
2775 interval: None,
2776 },
2777 arrangement_compression: false,
2778 },
2779 deadline: Timestamp::MIN,
2780 on_timeout: OnTimeoutAction::Rollback,
2781 status: ReconfigurationStatus::TimedOut,
2782 }),
2783 burst: None,
2784 }),
2785 workload_class: None,
2786 };
2787 let commit_ts = catalog.current_upper().await;
2788 catalog
2789 .transact(
2790 None,
2791 commit_ts,
2792 None,
2793 vec![Op::CreateCluster {
2794 id: cluster_id,
2795 name: "no_replicas".into(),
2796 introspection_sources: Vec::new(),
2797 owner_id: MZ_SYSTEM_ROLE_ID,
2798 config,
2799 }],
2800 )
2801 .await
2802 .expect("create cluster");
2803 catalog.expire().await;
2804
2805 let config_sizes = &mut bootstrap_args.cluster_replica_size_map.0;
2806 config_sizes.remove(&in_use);
2807 config_sizes.remove(&unused);
2808 config_sizes.remove(&cluster_only);
2809 config_sizes.remove(&hydration);
2810 config_sizes.remove(&timed_out_target);
2811 config_sizes
2812 .get_mut(&changed)
2813 .expect("test size")
2814 .cpu_exclusive = true;
2815 let catalog =
2816 Catalog::open_debug_catalog(persist_client.clone(), organization_id, &bootstrap_args)
2817 .await
2818 .expect("unable to open debug catalog");
2819 let sizes = &catalog.cluster_replica_sizes().0;
2820 assert!(sizes[&in_use].disabled);
2821 assert!(sizes[&cluster_only].disabled);
2822 assert!(sizes[&hydration].disabled);
2823 assert!(!sizes.contains_key(&unused));
2824 assert!(!sizes.contains_key(&timed_out_target));
2825 assert_eq!(
2826 sizes[&changed],
2827 bootstrap_args.cluster_replica_size_map.0[&changed]
2828 );
2829 let durable_sizes = catalog.cluster_replica_sizes().clone();
2830 catalog.expire().await;
2831
2832 bootstrap_args
2834 .cluster_replica_size_map
2835 .0
2836 .insert(unused.clone(), durable_sizes.0[&changed].clone());
2837 let catalog =
2838 Catalog::open_debug_read_only_catalog(persist_client, organization_id, &bootstrap_args)
2839 .await
2840 .expect("unable to open read-only debug catalog");
2841 assert_eq!(catalog.cluster_replica_sizes().0, durable_sizes.0);
2842 catalog.expire().await;
2843 }
2844
2845 #[mz_ore::test(tokio::test)]
2846 #[cfg_attr(miri, ignore)] async fn test_effective_search_path() {
2848 Catalog::with_debug(|catalog| async move {
2849 let mz_catalog_schema = (
2850 ResolvedDatabaseSpecifier::Ambient,
2851 SchemaSpecifier::Id(catalog.state().get_mz_catalog_schema_id()),
2852 );
2853 let pg_catalog_schema = (
2854 ResolvedDatabaseSpecifier::Ambient,
2855 SchemaSpecifier::Id(catalog.state().get_pg_catalog_schema_id()),
2856 );
2857 let mz_temp_schema = (
2858 ResolvedDatabaseSpecifier::Ambient,
2859 SchemaSpecifier::Temporary,
2860 );
2861
2862 let session = Session::dummy();
2864 let conn_catalog = catalog.for_session(&session);
2865 assert_ne!(
2866 conn_catalog.effective_search_path(false),
2867 conn_catalog.search_path
2868 );
2869 assert_ne!(
2870 conn_catalog.effective_search_path(true),
2871 conn_catalog.search_path
2872 );
2873 assert_eq!(
2874 conn_catalog.effective_search_path(false),
2875 vec![
2876 mz_catalog_schema.clone(),
2877 pg_catalog_schema.clone(),
2878 conn_catalog.search_path[0].clone()
2879 ]
2880 );
2881 assert_eq!(
2882 conn_catalog.effective_search_path(true),
2883 vec![
2884 mz_temp_schema.clone(),
2885 mz_catalog_schema.clone(),
2886 pg_catalog_schema.clone(),
2887 conn_catalog.search_path[0].clone()
2888 ]
2889 );
2890
2891 let mut session = Session::dummy();
2893 session
2894 .vars_mut()
2895 .set(
2896 &SystemVars::new(),
2897 "search_path",
2898 VarInput::Flat(mz_repr::namespaces::PG_CATALOG_SCHEMA),
2899 false,
2900 )
2901 .expect("failed to set search_path");
2902 let conn_catalog = catalog.for_session(&session);
2903 assert_ne!(
2904 conn_catalog.effective_search_path(false),
2905 conn_catalog.search_path
2906 );
2907 assert_ne!(
2908 conn_catalog.effective_search_path(true),
2909 conn_catalog.search_path
2910 );
2911 assert_eq!(
2912 conn_catalog.effective_search_path(false),
2913 vec![mz_catalog_schema.clone(), pg_catalog_schema.clone()]
2914 );
2915 assert_eq!(
2916 conn_catalog.effective_search_path(true),
2917 vec![
2918 mz_temp_schema.clone(),
2919 mz_catalog_schema.clone(),
2920 pg_catalog_schema.clone()
2921 ]
2922 );
2923
2924 let mut session = Session::dummy();
2925 session
2926 .vars_mut()
2927 .set(
2928 &SystemVars::new(),
2929 "search_path",
2930 VarInput::Flat(mz_repr::namespaces::MZ_CATALOG_SCHEMA),
2931 false,
2932 )
2933 .expect("failed to set search_path");
2934 let conn_catalog = catalog.for_session(&session);
2935 assert_ne!(
2936 conn_catalog.effective_search_path(false),
2937 conn_catalog.search_path
2938 );
2939 assert_ne!(
2940 conn_catalog.effective_search_path(true),
2941 conn_catalog.search_path
2942 );
2943 assert_eq!(
2944 conn_catalog.effective_search_path(false),
2945 vec![pg_catalog_schema.clone(), mz_catalog_schema.clone()]
2946 );
2947 assert_eq!(
2948 conn_catalog.effective_search_path(true),
2949 vec![
2950 mz_temp_schema.clone(),
2951 pg_catalog_schema.clone(),
2952 mz_catalog_schema.clone()
2953 ]
2954 );
2955
2956 let mut session = Session::dummy();
2957 session
2958 .vars_mut()
2959 .set(
2960 &SystemVars::new(),
2961 "search_path",
2962 VarInput::Flat(mz_repr::namespaces::MZ_TEMP_SCHEMA),
2963 false,
2964 )
2965 .expect("failed to set search_path");
2966 let conn_catalog = catalog.for_session(&session);
2967 assert_ne!(
2968 conn_catalog.effective_search_path(false),
2969 conn_catalog.search_path
2970 );
2971 assert_ne!(
2972 conn_catalog.effective_search_path(true),
2973 conn_catalog.search_path
2974 );
2975 assert_eq!(
2980 conn_catalog.effective_search_path(false),
2981 vec![mz_catalog_schema.clone(), pg_catalog_schema.clone(),]
2982 );
2983 assert_eq!(
2984 conn_catalog.effective_search_path(true),
2985 vec![mz_temp_schema, mz_catalog_schema, pg_catalog_schema]
2986 );
2987 catalog.expire().await;
2988 })
2989 .await
2990 }
2991
2992 #[mz_ore::test(tokio::test)]
2993 #[cfg_attr(miri, ignore)] async fn test_normalized_create() {
2995 use mz_ore::collections::CollectionExt;
2996 Catalog::with_debug(|catalog| async move {
2997 let conn_catalog = catalog.for_system_session();
2998 let scx = &mut StatementContext::new(None, &conn_catalog);
2999
3000 let parsed = mz_sql_parser::parser::parse_statements(
3001 "create view public.foo as select 1 as bar",
3002 )
3003 .expect("")
3004 .into_element()
3005 .ast;
3006
3007 let (stmt, _) = names::resolve(scx.catalog, parsed).expect("");
3008
3009 assert_eq!(
3011 r#"CREATE VIEW "materialize"."public"."foo" AS SELECT 1 AS "bar""#,
3012 mz_sql::normalize::create_statement(scx, stmt).expect(""),
3013 );
3014 catalog.expire().await;
3015 })
3016 .await;
3017 }
3018
3019 #[mz_ore::test(tokio::test)]
3026 #[cfg_attr(miri, ignore)] async fn test_resolved_ids_survive_create_sql_round_trip() {
3028 use mz_ore::collections::CollectionExt;
3029 Catalog::with_debug(|catalog| async move {
3030 let conn_catalog = catalog.for_system_session();
3031 let scx = &mut StatementContext::new(None, &conn_catalog);
3032
3033 let resolve_and_normalize = |scx: &mut StatementContext, sql: &str| {
3034 let parsed = mz_sql_parser::parser::parse_statements(sql)
3035 .expect("parses")
3036 .into_element()
3037 .ast;
3038 let (stmt, ids) = names::resolve(scx.catalog, parsed).expect("resolves");
3039 let normalized =
3040 mz_sql::normalize::create_statement(scx, stmt).expect("normalizes");
3041 (ids, normalized)
3042 };
3043
3044 const ARRAY_SUFFIX: &str = "create table public.t (a pg_catalog.int4[])";
3045 const ARRAY_TYPE: &str = "create table public.t (a pg_catalog._int4)";
3046 const ELEMENT_TYPE: &str = "create table public.t (a pg_catalog.int4)";
3047
3048 let mut ids_by_spelling: BTreeMap<&str, Vec<CatalogItemId>> = BTreeMap::new();
3049 for sql in [
3050 ARRAY_SUFFIX,
3051 ARRAY_TYPE,
3052 ELEMENT_TYPE,
3053 "create table public.t (a pg_catalog.int4 list)",
3054 "create view public.v as select null::pg_catalog.text[]",
3055 ] {
3056 let (ids, normalized) = resolve_and_normalize(scx, sql);
3057 let (round_tripped_ids, _) = resolve_and_normalize(scx, &normalized);
3058 assert_eq!(
3059 ids.items().collect::<Vec<_>>(),
3060 round_tripped_ids.items().collect::<Vec<_>>(),
3061 "resolving {normalized:?} produced different ids than {sql:?}",
3062 );
3063 ids_by_spelling.insert(sql, ids.items().copied().collect());
3064 }
3065
3066 assert_eq!(
3071 ids_by_spelling[ARRAY_SUFFIX], ids_by_spelling[ARRAY_TYPE],
3072 "{ARRAY_SUFFIX:?} and {ARRAY_TYPE:?} must resolve to the same ids",
3073 );
3074
3075 let element_ids = &ids_by_spelling[ELEMENT_TYPE];
3078 assert!(
3079 ids_by_spelling[ARRAY_SUFFIX]
3080 .iter()
3081 .all(|id| !element_ids.contains(id)),
3082 "{ARRAY_SUFFIX:?} recorded an element type id from {ELEMENT_TYPE:?}",
3083 );
3084
3085 catalog.expire().await;
3086 })
3087 .await;
3088 }
3089
3090 #[mz_ore::test(tokio::test)]
3092 #[cfg_attr(miri, ignore)] async fn test_large_catalog_item() {
3094 let view_def = "CREATE VIEW \"materialize\".\"public\".\"v\" AS SELECT 1 FROM (SELECT 1";
3095 let column = ", 1";
3096 let view_def_size = view_def.bytes().count();
3097 let column_size = column.bytes().count();
3098 let column_count =
3099 (mz_sql_parser::parser::MAX_STATEMENT_BATCH_SIZE - view_def_size) / column_size + 1;
3100 let columns = iter::repeat(column).take(column_count).join("");
3101 let create_sql = format!("{view_def}{columns})");
3102 let create_sql_check = create_sql.clone();
3103 assert_ok!(mz_sql_parser::parser::parse_statements(&create_sql));
3104 assert_err!(mz_sql_parser::parser::parse_statements_with_limit(
3105 &create_sql
3106 ));
3107
3108 let persist_client = PersistClient::new_for_tests().await;
3109 let organization_id = Uuid::new_v4();
3110 let id = CatalogItemId::User(1);
3111 let gid = GlobalId::User(1);
3112 let bootstrap_args = test_bootstrap_args();
3113 {
3114 let mut catalog = Catalog::open_debug_catalog(
3115 persist_client.clone(),
3116 organization_id.clone(),
3117 &bootstrap_args,
3118 )
3119 .await
3120 .expect("unable to open debug catalog");
3121 let item = catalog
3122 .state()
3123 .deserialize_item(
3124 gid,
3125 &create_sql,
3126 &BTreeMap::new(),
3127 &mut LocalExpressionCache::Closed,
3128 None,
3129 )
3130 .expect("unable to parse view");
3131 let commit_ts = catalog.current_upper().await;
3132 catalog
3133 .transact(
3134 None,
3135 commit_ts,
3136 None,
3137 vec![Op::CreateItem {
3138 item,
3139 name: QualifiedItemName {
3140 qualifiers: ItemQualifiers {
3141 database_spec: ResolvedDatabaseSpecifier::Id(DatabaseId::User(1)),
3142 schema_spec: SchemaSpecifier::Id(SchemaId::User(3)),
3143 },
3144 item: "v".to_string(),
3145 },
3146 id,
3147 owner_id: MZ_SYSTEM_ROLE_ID,
3148 }],
3149 )
3150 .await
3151 .expect("failed to transact");
3152 catalog.expire().await;
3153 }
3154 {
3155 let catalog =
3156 Catalog::open_debug_catalog(persist_client, organization_id, &bootstrap_args)
3157 .await
3158 .expect("unable to open debug catalog");
3159 let view = catalog.get_entry(&id);
3160 assert_eq!("v", view.name.item);
3161 match &view.item {
3162 CatalogItem::View(view) => assert_eq!(create_sql_check, view.create_sql),
3163 item => panic!("expected view, got {}", item.typ()),
3164 }
3165 catalog.expire().await;
3166 }
3167 }
3168
3169 #[mz_ore::test(tokio::test)]
3170 #[cfg_attr(miri, ignore)] async fn test_object_type() {
3172 Catalog::with_debug(|catalog| async move {
3173 let conn_catalog = catalog.for_system_session();
3174
3175 assert_eq!(
3176 mz_sql::catalog::ObjectType::ClusterReplica,
3177 conn_catalog.get_object_type(&ObjectId::ClusterReplica((
3178 ClusterId::user(1).expect("1 is a valid ID"),
3179 ReplicaId::User(1)
3180 )))
3181 );
3182 assert_eq!(
3183 mz_sql::catalog::ObjectType::Role,
3184 conn_catalog.get_object_type(&ObjectId::Role(RoleId::User(1)))
3185 );
3186 catalog.expire().await;
3187 })
3188 .await;
3189 }
3190
3191 #[mz_ore::test(tokio::test)]
3192 #[cfg_attr(miri, ignore)] async fn test_get_privileges() {
3194 Catalog::with_debug(|catalog| async move {
3195 let conn_catalog = catalog.for_system_session();
3196
3197 assert_eq!(
3198 None,
3199 conn_catalog.get_privileges(&SystemObjectId::Object(ObjectId::ClusterReplica((
3200 ClusterId::user(1).expect("1 is a valid ID"),
3201 ReplicaId::User(1),
3202 ))))
3203 );
3204 assert_eq!(
3205 None,
3206 conn_catalog
3207 .get_privileges(&SystemObjectId::Object(ObjectId::Role(RoleId::User(1))))
3208 );
3209 catalog.expire().await;
3210 })
3211 .await;
3212 }
3213
3214 #[mz_ore::test(tokio::test)]
3215 #[cfg_attr(miri, ignore)] async fn verify_builtin_descs() {
3217 let aws_context = DebugAwsContext {
3224 aws_account_id: Some("123456789000".to_string()),
3225 aws_external_id_prefix: Some("eb5cb59b-e2fe-41f3-87ca-d2176a495345".to_string()),
3226 aws_connection_role_arn: Some(
3227 "arn:aws:iam::123456789000:role/MaterializeConnection".to_string(),
3228 ),
3229 };
3230 Catalog::with_debug_aws_context(aws_context, |catalog| async move {
3231 let conn_catalog = catalog.for_system_session();
3232
3233 for builtin in BUILTINS::iter() {
3234 let (schema, name, expected_desc, shard_backed) = match builtin {
3235 Builtin::Table(t) => (&t.schema, &t.name, &t.desc, true),
3236 Builtin::View(v) => (&v.schema, &v.name, &v.desc, false),
3237 Builtin::MaterializedView(mv) => (&mv.schema, &mv.name, &mv.desc, true),
3238 Builtin::Source(s) => (&s.schema, &s.name, &s.desc, true),
3239 Builtin::Log(_)
3240 | Builtin::Type(_)
3241 | Builtin::Func(_)
3242 | Builtin::Index(_)
3243 | Builtin::Connection(_) => continue,
3244 };
3245 let migration_hint = if shard_backed {
3252 format!(
3253 "; {schema}.{name} is backed by a persist shard, so after updating its \
3254 `desc` also add a `MigrationStep` for it to `MIGRATIONS` in \
3255 `src/adapter/src/catalog/open/builtin_schema_migration.rs`"
3256 )
3257 } else {
3258 String::new()
3259 };
3260 let item = conn_catalog
3261 .resolve_item(&PartialItemName {
3262 database: None,
3263 schema: Some(schema.to_string()),
3264 item: name.to_string(),
3265 })
3266 .expect("unable to resolve item")
3267 .at_version(RelationVersionSelector::Latest);
3268
3269 let actual_desc = item.relation_desc().expect("invalid item type");
3270 assert_eq!(
3271 actual_desc.arity(),
3272 expected_desc.arity(),
3273 "item {schema}.{name} column count did not match its expected column count{migration_hint}"
3274 );
3275 for (index, ((actual_name, actual_typ), (expected_name, expected_typ))) in
3276 actual_desc.iter().zip_eq(expected_desc.iter()).enumerate()
3277 {
3278 assert_eq!(
3279 actual_name, expected_name,
3280 "item {schema}.{name} column {index} name did not match its expected name{migration_hint}"
3281 );
3282 assert_eq!(
3283 actual_typ, expected_typ,
3284 "item {schema}.{name} column {index} ('{actual_name}') type did not match its expected type{migration_hint}"
3285 );
3286 }
3287 assert_eq!(
3290 actual_desc.typ().keys,
3291 expected_desc.typ().keys,
3292 "item {schema}.{name} keys did not match its expected keys"
3293 );
3294 assert_eq!(
3295 &*actual_desc, expected_desc,
3296 "item {schema}.{name} did not match its expected RelationDesc"
3297 );
3298 }
3299 catalog.expire().await;
3300 })
3301 .await
3302 }
3303
3304 #[mz_ore::test(tokio::test)]
3307 #[cfg_attr(miri, ignore)] async fn test_compare_builtins_postgres() {
3309 async fn inner(catalog: Catalog) {
3310 let (client, connection) = tokio_postgres::connect(
3314 &env::var("POSTGRES_URL").unwrap_or_else(|_| "host=localhost user=postgres".into()),
3315 NoTls,
3316 )
3317 .await
3318 .expect("failed to connect to Postgres");
3319
3320 task::spawn(|| "compare_builtin_postgres", async move {
3321 if let Err(e) = connection.await {
3322 panic!("connection error: {}", e);
3323 }
3324 });
3325
3326 struct PgProc {
3327 name: String,
3328 arg_oids: Vec<u32>,
3329 ret_oid: Option<u32>,
3330 ret_set: bool,
3331 }
3332
3333 struct PgType {
3334 name: String,
3335 ty: String,
3336 elem: u32,
3337 array: u32,
3338 input: u32,
3339 receive: u32,
3340 send: u32,
3341 len: i16,
3342 }
3343
3344 struct PgOper {
3345 oprresult: u32,
3346 name: String,
3347 }
3348
3349 let pg_proc: BTreeMap<_, _> = query(
3350 &client,
3351 sql!(
3352 "SELECT
3353 p.oid,
3354 proname,
3355 proargtypes,
3356 prorettype,
3357 proretset
3358 FROM pg_proc p
3359 JOIN pg_namespace n ON p.pronamespace = n.oid"
3360 ),
3361 &[],
3362 )
3363 .await
3364 .expect("pg query failed")
3365 .into_iter()
3366 .map(|row| {
3367 let oid: u32 = row.get("oid");
3368 let pg_proc = PgProc {
3369 name: row.get("proname"),
3370 arg_oids: row.get("proargtypes"),
3371 ret_oid: row.get("prorettype"),
3372 ret_set: row.get("proretset"),
3373 };
3374 (oid, pg_proc)
3375 })
3376 .collect();
3377
3378 let pg_type: BTreeMap<_, _> = query(
3379 &client,
3380 sql!(
3381 "SELECT oid, typname, typtype::text, typelem, typarray, typinput::oid, typreceive::oid as typreceive, typsend::oid as typsend, typlen FROM pg_type"
3382 ),
3383 &[],
3384 )
3385 .await
3386 .expect("pg query failed")
3387 .into_iter()
3388 .map(|row| {
3389 let oid: u32 = row.get("oid");
3390 let pg_type = PgType {
3391 name: row.get("typname"),
3392 ty: row.get("typtype"),
3393 elem: row.get("typelem"),
3394 array: row.get("typarray"),
3395 input: row.get("typinput"),
3396 receive: row.get("typreceive"),
3397 send: row.get("typsend"),
3398 len: row.get("typlen"),
3399 };
3400 (oid, pg_type)
3401 })
3402 .collect();
3403
3404 let pg_oper: BTreeMap<_, _> = query(
3405 &client,
3406 sql!("SELECT oid, oprname, oprresult FROM pg_operator"),
3407 &[],
3408 )
3409 .await
3410 .expect("pg query failed")
3411 .into_iter()
3412 .map(|row| {
3413 let oid: u32 = row.get("oid");
3414 let pg_oper = PgOper {
3415 name: row.get("oprname"),
3416 oprresult: row.get("oprresult"),
3417 };
3418 (oid, pg_oper)
3419 })
3420 .collect();
3421
3422 let conn_catalog = catalog.for_system_session();
3423 let resolve_type_oid = |item: &str| {
3424 conn_catalog
3425 .resolve_type(&PartialItemName {
3426 database: None,
3427 schema: Some(PG_CATALOG_SCHEMA.into()),
3430 item: item.to_string(),
3431 })
3432 .expect("unable to resolve type")
3433 .oid()
3434 };
3435
3436 let func_oids: BTreeSet<_> = BUILTINS::funcs()
3437 .flat_map(|f| f.inner.func_impls().into_iter().map(|f| f.oid))
3438 .collect();
3439
3440 let mut all_oids = BTreeSet::new();
3441
3442 let equivalent_types: BTreeSet<(Option<u32>, Option<u32>)> = BTreeSet::from_iter(
3445 [
3446 (Type::NAME, Type::TEXT),
3448 (Type::NAME_ARRAY, Type::TEXT_ARRAY),
3449 (Type::TIME, Type::TIMETZ),
3451 (Type::TIME_ARRAY, Type::TIMETZ_ARRAY),
3452 ]
3453 .map(|(a, b)| (Some(a.oid()), Some(b.oid()))),
3454 );
3455 let ignore_return_types: BTreeSet<u32> = BTreeSet::from([
3456 1619, ]);
3458 let is_same_type = |fn_oid: u32, a: Option<u32>, b: Option<u32>| -> bool {
3459 if ignore_return_types.contains(&fn_oid) {
3460 return true;
3461 }
3462 if equivalent_types.contains(&(a, b)) || equivalent_types.contains(&(b, a)) {
3463 return true;
3464 }
3465 a == b
3466 };
3467
3468 for builtin in BUILTINS::iter() {
3469 match builtin {
3470 Builtin::Type(ty) => {
3471 assert!(all_oids.insert(ty.oid), "{} reused oid {}", ty.name, ty.oid);
3472
3473 if ty.oid >= FIRST_MATERIALIZE_OID {
3474 continue;
3477 }
3478
3479 let pg_ty = pg_type.get(&ty.oid).unwrap_or_else(|| {
3482 panic!("pg_proc missing type {}: oid {}", ty.name, ty.oid)
3483 });
3484 assert_eq!(
3485 ty.name, pg_ty.name,
3486 "oid {} has name {} in postgres; expected {}",
3487 ty.oid, pg_ty.name, ty.name,
3488 );
3489
3490 let (typinput_oid, typreceive_oid, typsend_oid) =
3491 match &ty.details.pg_metadata {
3492 None => (0, 0, 0),
3493 Some(pgmeta) => (
3494 pgmeta.typinput_oid,
3495 pgmeta.typreceive_oid,
3496 pgmeta.typsend_oid,
3497 ),
3498 };
3499 assert_eq!(
3500 typinput_oid, pg_ty.input,
3501 "type {} has typinput OID {:?} in mz but {:?} in pg",
3502 ty.name, typinput_oid, pg_ty.input,
3503 );
3504 assert_eq!(
3505 typreceive_oid, pg_ty.receive,
3506 "type {} has typreceive OID {:?} in mz but {:?} in pg",
3507 ty.name, typreceive_oid, pg_ty.receive,
3508 );
3509 assert_eq!(
3514 typsend_oid, pg_ty.send,
3515 "type {} has typsend OID {:?} in mz but {:?} in pg",
3516 ty.name, typsend_oid, pg_ty.send,
3517 );
3518 if typinput_oid != 0 {
3519 assert!(
3520 func_oids.contains(&typinput_oid),
3521 "type {} has typinput OID {} that does not exist in pg_proc",
3522 ty.name,
3523 typinput_oid,
3524 );
3525 }
3526 if typreceive_oid != 0 {
3527 assert!(
3528 func_oids.contains(&typreceive_oid),
3529 "type {} has typreceive OID {} that does not exist in pg_proc",
3530 ty.name,
3531 typreceive_oid,
3532 );
3533 }
3534 let id = conn_catalog
3535 .resolve_type(&PartialItemName {
3536 database: None,
3537 schema: Some(ty.schema.into()),
3538 item: ty.name.to_string(),
3539 })
3540 .expect("unable to resolve type")
3541 .id();
3542 if let Ok(scalar_type) =
3544 mz_sql::plan::scalar_type_from_catalog(&conn_catalog, id, &[])
3545 {
3546 let typlen = mz_pgrepr::Type::from(&scalar_type).typlen();
3547 assert_eq!(
3548 typlen, pg_ty.len,
3549 "type {} has typlen {} in mz but {} in pg",
3550 ty.name, typlen, pg_ty.len,
3551 );
3552 }
3553
3554 match &ty.details.typ {
3556 CatalogType::Array { element_reference } => {
3557 let elem_ty = BUILTINS::iter()
3558 .filter_map(|builtin| match builtin {
3559 Builtin::Type(ty @ BuiltinType { name, .. })
3560 if element_reference == name =>
3561 {
3562 Some(ty)
3563 }
3564 _ => None,
3565 })
3566 .next();
3567 let elem_ty = match elem_ty {
3568 Some(ty) => ty,
3569 None => {
3570 panic!("{} is unexpectedly not a type", element_reference)
3571 }
3572 };
3573 assert_eq!(
3574 pg_ty.elem, elem_ty.oid,
3575 "type {} has mismatched element OIDs",
3576 ty.name
3577 )
3578 }
3579 CatalogType::Pseudo => {
3580 assert_eq!(
3581 pg_ty.ty, "p",
3582 "type {} is not a pseudo type as expected",
3583 ty.name
3584 )
3585 }
3586 CatalogType::Range { .. } => {
3587 assert_eq!(
3588 pg_ty.ty, "r",
3589 "type {} is not a range type as expected",
3590 ty.name
3591 );
3592 }
3593 _ => {
3594 assert_eq!(
3595 pg_ty.ty, "b",
3596 "type {} is not a base type as expected",
3597 ty.name
3598 )
3599 }
3600 }
3601
3602 let schema = catalog
3604 .resolve_schema_in_database(
3605 &ResolvedDatabaseSpecifier::Ambient,
3606 ty.schema,
3607 &SYSTEM_CONN_ID,
3608 )
3609 .expect("unable to resolve schema");
3610 let allocated_type = catalog
3611 .resolve_type(
3612 None,
3613 &vec![(ResolvedDatabaseSpecifier::Ambient, schema.id().clone())],
3614 &PartialItemName {
3615 database: None,
3616 schema: Some(schema.name().schema.clone()),
3617 item: ty.name.to_string(),
3618 },
3619 &SYSTEM_CONN_ID,
3620 )
3621 .expect("unable to resolve type");
3622 let ty = if let CatalogItem::Type(ty) = &allocated_type.item {
3623 ty
3624 } else {
3625 panic!("unexpectedly not a type")
3626 };
3627 match ty.details.array_id {
3628 Some(array_id) => {
3629 let array_ty = catalog.get_entry(&array_id);
3630 assert_eq!(
3631 pg_ty.array, array_ty.oid,
3632 "type {} has mismatched array OIDs",
3633 allocated_type.name.item,
3634 );
3635 }
3636 None => assert_eq!(
3637 pg_ty.array, 0,
3638 "type {} does not have an array type in mz but does in pg",
3639 allocated_type.name.item,
3640 ),
3641 }
3642 }
3643 Builtin::Func(func) => {
3644 for imp in func.inner.func_impls() {
3645 assert!(
3646 all_oids.insert(imp.oid),
3647 "{} reused oid {}",
3648 func.name,
3649 imp.oid
3650 );
3651
3652 assert!(
3653 imp.oid < FIRST_USER_OID,
3654 "built-in function {} erroneously has OID in user space ({})",
3655 func.name,
3656 imp.oid,
3657 );
3658
3659 let pg_fn = if imp.oid >= FIRST_UNPINNED_OID {
3662 continue;
3663 } else {
3664 pg_proc.get(&imp.oid).unwrap_or_else(|| {
3665 panic!(
3666 "pg_proc missing function {}: oid {}",
3667 func.name, imp.oid
3668 )
3669 })
3670 };
3671 assert_eq!(
3672 func.name, pg_fn.name,
3673 "funcs with oid {} don't match names: {} in mz, {} in pg",
3674 imp.oid, func.name, pg_fn.name
3675 );
3676
3677 let imp_arg_oids = imp
3680 .arg_typs
3681 .iter()
3682 .map(|item| resolve_type_oid(item))
3683 .collect::<Vec<_>>();
3684
3685 if imp_arg_oids != pg_fn.arg_oids {
3686 println!(
3687 "funcs with oid {} ({}) don't match arguments: {:?} in mz, {:?} in pg",
3688 imp.oid, func.name, imp_arg_oids, pg_fn.arg_oids
3689 );
3690 }
3691
3692 let imp_return_oid = imp.return_typ.map(resolve_type_oid);
3693
3694 assert!(
3695 is_same_type(imp.oid, imp_return_oid, pg_fn.ret_oid),
3696 "funcs with oid {} ({}) don't match return types: {:?} in mz, {:?} in pg",
3697 imp.oid,
3698 func.name,
3699 imp_return_oid,
3700 pg_fn.ret_oid
3701 );
3702
3703 assert_eq!(
3704 imp.return_is_set, pg_fn.ret_set,
3705 "funcs with oid {} ({}) don't match set-returning value: {:?} in mz, {:?} in pg",
3706 imp.oid, func.name, imp.return_is_set, pg_fn.ret_set
3707 );
3708 }
3709 }
3710 _ => (),
3711 }
3712 }
3713
3714 for (op, func) in OP_IMPLS.iter() {
3715 for imp in func.func_impls() {
3716 assert!(all_oids.insert(imp.oid), "{} reused oid {}", op, imp.oid);
3717
3718 let pg_op = if imp.oid >= FIRST_UNPINNED_OID {
3720 continue;
3721 } else {
3722 pg_oper.get(&imp.oid).unwrap_or_else(|| {
3723 panic!("pg_operator missing operator {}: oid {}", op, imp.oid)
3724 })
3725 };
3726
3727 assert_eq!(*op, pg_op.name);
3728
3729 let imp_return_oid =
3730 imp.return_typ.map(resolve_type_oid).expect("must have oid");
3731 if imp_return_oid != pg_op.oprresult {
3732 panic!(
3733 "operators with oid {} ({}) don't match return typs: {} in mz, {} in pg",
3734 imp.oid, op, imp_return_oid, pg_op.oprresult
3735 );
3736 }
3737 }
3738 }
3739 catalog.expire().await;
3740 }
3741
3742 Catalog::with_debug(inner).await
3743 }
3744
3745 #[mz_ore::test(tokio::test)]
3747 #[cfg_attr(miri, ignore)] async fn test_smoketest_all_builtins() {
3749 fn inner(catalog: Catalog) -> Vec<mz_ore::task::JoinHandle<()>> {
3750 let catalog = Arc::new(catalog);
3751 let conn_catalog = catalog.for_system_session();
3752
3753 let resolve_type_oid = |item: &str| conn_catalog.state().get_system_type(item).oid();
3754 let mut handles = Vec::new();
3755
3756 let ignore_names = BTreeSet::from([
3758 "avg",
3759 "avg_internal_v1",
3760 "bool_and",
3761 "bool_or",
3762 "has_table_privilege", "has_type_privilege", "mod",
3765 "mz_panic",
3766 "mz_sleep",
3767 "pow",
3768 "stddev_pop",
3769 "stddev_samp",
3770 "stddev",
3771 "var_pop",
3772 "var_samp",
3773 "variance",
3774 ]);
3775
3776 let fns = BUILTINS::funcs()
3777 .map(|func| (&func.name, func.inner))
3778 .chain(OP_IMPLS.iter());
3779
3780 for (name, func) in fns {
3781 if ignore_names.contains(name) {
3782 continue;
3783 }
3784 let Func::Scalar(impls) = func else {
3785 continue;
3786 };
3787
3788 'outer: for imp in impls {
3789 let details = imp.details();
3790 let mut styps = Vec::new();
3791 for item in details.arg_typs.iter() {
3792 let oid = resolve_type_oid(item);
3793 let Ok(pgtyp) = mz_pgrepr::Type::from_oid(oid) else {
3794 continue 'outer;
3795 };
3796 styps.push(SqlScalarType::try_from(&pgtyp).expect("must exist"));
3797 }
3798 let datums = styps
3799 .iter()
3800 .map(|styp| {
3801 let mut datums = vec![Datum::Null];
3802 datums.extend(styp.interesting_datums());
3803 datums
3804 })
3805 .collect::<Vec<_>>();
3806 if datums.is_empty() {
3808 continue;
3809 }
3810
3811 let return_oid = details
3812 .return_typ
3813 .map(resolve_type_oid)
3814 .expect("must exist");
3815 let return_styp = mz_pgrepr::Type::from_oid(return_oid)
3816 .ok()
3817 .map(|typ| SqlScalarType::try_from(&typ).expect("must exist"));
3818
3819 let mut idxs = vec![0; datums.len()];
3820 while idxs[0] < datums[0].len() {
3821 let mut args = Vec::with_capacity(idxs.len());
3822 for i in 0..(datums.len()) {
3823 args.push(datums[i][idxs[i]]);
3824 }
3825
3826 let op = &imp.op;
3827 let scalars = args
3828 .iter()
3829 .enumerate()
3830 .map(|(i, datum)| {
3831 CoercibleScalarExpr::Coerced(HirScalarExpr::literal(
3832 datum.clone(),
3833 styps[i].clone(),
3834 ))
3835 })
3836 .collect();
3837
3838 let call_name = format!(
3839 "{name}({}) (oid: {})",
3840 args.iter()
3841 .map(|d| d.to_string())
3842 .collect::<Vec<_>>()
3843 .join(", "),
3844 imp.oid
3845 );
3846 let catalog = Arc::clone(&catalog);
3847 let call_name_fn = call_name.clone();
3848 let return_styp = return_styp.clone();
3849 let handle = task::spawn_blocking(
3850 || call_name,
3851 move || {
3852 smoketest_fn(
3853 name,
3854 call_name_fn,
3855 op,
3856 imp,
3857 args,
3858 catalog,
3859 scalars,
3860 return_styp,
3861 )
3862 },
3863 );
3864 handles.push(handle);
3865
3866 for i in (0..datums.len()).rev() {
3868 idxs[i] += 1;
3869 if idxs[i] >= datums[i].len() {
3870 if i == 0 {
3871 break;
3872 }
3873 idxs[i] = 0;
3874 continue;
3875 } else {
3876 break;
3877 }
3878 }
3879 }
3880 }
3881 }
3882 handles
3883 }
3884
3885 let handles = Catalog::with_debug(|catalog| async { inner(catalog) }).await;
3886 for handle in handles {
3887 handle.await;
3888 }
3889 }
3890
3891 fn smoketest_fn(
3892 name: &&str,
3893 call_name: String,
3894 op: &Operation<HirScalarExpr>,
3895 imp: &FuncImpl<HirScalarExpr>,
3896 args: Vec<Datum<'_>>,
3897 catalog: Arc<Catalog>,
3898 scalars: Vec<CoercibleScalarExpr>,
3899 return_styp: Option<SqlScalarType>,
3900 ) {
3901 let conn_catalog = catalog.for_system_session();
3902 let pcx = PlanContext::zero();
3903 let scx = StatementContext::new(Some(&pcx), &conn_catalog);
3904 let qcx = QueryContext::root(&scx, QueryLifetime::OneShot);
3905 let ecx = ExprContext {
3906 qcx: &qcx,
3907 name: "smoketest",
3908 scope: &Scope::empty(),
3909 relation_type: &SqlRelationType::empty(),
3910 allow_aggregates: false,
3911 allow_subqueries: false,
3912 allow_parameters: false,
3913 allow_windows: false,
3914 };
3915 let arena = RowArena::new();
3916 let mut session = Session::dummy();
3917 session
3918 .start_transaction(to_datetime(0), None, None)
3919 .expect("must succeed");
3920 let prep_style = ExprPrepOneShot {
3921 logical_time: EvalTime::Time(Timestamp::MIN),
3922 session: &session,
3923 catalog_state: &catalog.state,
3924 };
3925
3926 let res = (op.0)(&ecx, scalars, &imp.params, vec![]);
3929 if let Ok(hir) = res {
3930 let uneliminated_result_row = {
3931 if let HirScalarExpr::CallUnary { func, .. } = &hir
3932 && func.is_eliminable_cast()
3933 {
3934 let mut uneliminated_mir = hir
3935 .clone()
3936 .lower_uncorrelated(HirToMirConfig {
3937 enable_cast_elimination: false,
3938 ..catalog.system_config().into()
3939 })
3940 .expect("lowering eliminable cast should always succeed");
3941 prep_style
3942 .prep_scalar_expr(&mut uneliminated_mir)
3943 .expect("must succeed");
3944
3945 uneliminated_mir
3947 .eval(&[], &arena)
3948 .ok()
3949 .map(|datum| Row::pack([datum]))
3950 } else {
3951 None
3952 }
3953 };
3954
3955 if let Ok(mut mir) = hir.lower_uncorrelated(catalog.system_config()) {
3956 prep_style.prep_scalar_expr(&mut mir).expect("must succeed");
3958
3959 if let Ok(eval_result_datum) = mir.eval(&[], &arena) {
3960 if let Some(return_styp) = return_styp {
3961 let mir_typ = mir.typ(&[]);
3962 soft_assert_eq_or_log!(
3965 mir_typ.scalar_type,
3966 (&return_styp).into(),
3967 "MIR type did not match the catalog type (cast elimination/repr type error)"
3968 );
3969 if !eval_result_datum.is_instance_of(&mir_typ) {
3973 panic!(
3974 "{call_name}: expected return type of {return_styp:?}, got {eval_result_datum}"
3975 );
3976 }
3977 if let Some(row) = uneliminated_result_row {
3979 let uneliminated_result_datum = row.unpack_first();
3980 assert_eq!(
3981 uneliminated_result_datum, eval_result_datum,
3982 "datums should not change if cast is eliminable"
3983 );
3984 }
3985 if let Some((introduces_nulls, propagates_nulls)) =
3988 call_introduces_propagates_nulls(&mir)
3989 {
3990 if introduces_nulls {
3991 assert!(
3995 mir_typ.nullable,
3996 "fn named `{}` called on args `{:?}` (lowered to `{}`) yielded mir_typ.nullable: {}",
3997 name, args, mir, mir_typ.nullable
3998 );
3999 } else {
4000 let any_input_null = args.iter().any(|arg| arg.is_null());
4001 if !any_input_null {
4002 assert!(
4003 !mir_typ.nullable,
4004 "fn named `{}` called on args `{:?}` (lowered to `{}`) yielded mir_typ.nullable: {}",
4005 name, args, mir, mir_typ.nullable
4006 );
4007 } else if propagates_nulls {
4008 assert!(
4011 mir_typ.nullable,
4012 "fn named `{}` called on args `{:?}` (lowered to `{}`) yielded mir_typ.nullable: {}",
4013 name, args, mir, mir_typ.nullable
4014 );
4015 }
4016 }
4021 }
4022 let mut reduced = mir.clone();
4025 reduced.reduce(&[]);
4026 match reduced {
4027 MirScalarExpr::Literal(reduce_result, ctyp) => {
4028 match reduce_result {
4029 Ok(reduce_result_row) => {
4030 let reduce_result_datum = reduce_result_row.unpack_first();
4031 assert_eq!(
4032 reduce_result_datum,
4033 eval_result_datum,
4034 "eval/reduce datum mismatch: fn named `{}` called on args `{:?}` (lowered to `{}`) evaluated to `{}` with typ `{:?}`, but reduced to `{}` with typ `{:?}`",
4035 name,
4036 args,
4037 mir,
4038 eval_result_datum,
4039 mir_typ.scalar_type,
4040 reduce_result_datum,
4041 ctyp.scalar_type
4042 );
4043 assert_eq!(
4049 ctyp.scalar_type,
4050 mir_typ.scalar_type,
4051 "eval/reduce type mismatch: fn named `{}` called on args `{:?}` (lowered to `{}`) evaluated to `{}` with typ `{:?}`, but reduced to `{}` with typ `{:?}`",
4052 name,
4053 args,
4054 mir,
4055 eval_result_datum,
4056 mir_typ.scalar_type,
4057 reduce_result_datum,
4058 ctyp.scalar_type
4059 );
4060 }
4061 Err(..) => {} }
4063 }
4064 _ => unreachable!(
4065 "all args are literals, so should have reduced to a literal"
4066 ),
4067 }
4068 }
4069 }
4070 }
4071 }
4072 }
4073
4074 fn call_introduces_propagates_nulls(mir_func_call: &MirScalarExpr) -> Option<(bool, bool)> {
4079 match mir_func_call {
4080 MirScalarExpr::CallUnary { func, expr } => {
4081 if expr.is_literal() {
4082 Some((func.introduces_nulls(), func.propagates_nulls()))
4083 } else {
4084 None
4085 }
4086 }
4087 MirScalarExpr::CallBinary { func, expr1, expr2 } => {
4088 if expr1.is_literal() && expr2.is_literal() {
4089 Some((func.introduces_nulls(), func.propagates_nulls()))
4090 } else {
4091 None
4092 }
4093 }
4094 MirScalarExpr::CallVariadic { func, exprs } => {
4095 if exprs.iter().all(|arg| arg.is_literal()) {
4096 Some((func.introduces_nulls(), func.propagates_nulls()))
4097 } else {
4098 None
4099 }
4100 }
4101 _ => None,
4102 }
4103 }
4104
4105 #[mz_ore::test(tokio::test)]
4107 #[cfg_attr(miri, ignore)] async fn test_pg_views_forbidden_types() {
4109 Catalog::with_debug(|catalog| async move {
4110 let conn_catalog = catalog.for_system_session();
4111
4112 for view in BUILTINS::views().filter(|view| {
4113 view.schema == PG_CATALOG_SCHEMA || view.schema == INFORMATION_SCHEMA
4114 }) {
4115 let item = conn_catalog
4116 .resolve_item(&PartialItemName {
4117 database: None,
4118 schema: Some(view.schema.to_string()),
4119 item: view.name.to_string(),
4120 })
4121 .expect("unable to resolve view")
4122 .at_version(RelationVersionSelector::Latest);
4124 let full_name = conn_catalog.resolve_full_name(item.name());
4125 let desc = item.relation_desc().expect("invalid item type");
4126 for col_type in desc.iter_types() {
4127 match &col_type.scalar_type {
4128 typ @ SqlScalarType::UInt16
4129 | typ @ SqlScalarType::UInt32
4130 | typ @ SqlScalarType::UInt64
4131 | typ @ SqlScalarType::MzTimestamp
4132 | typ @ SqlScalarType::List { .. }
4133 | typ @ SqlScalarType::Map { .. }
4134 | typ @ SqlScalarType::MzAclItem => {
4135 panic!("{typ:?} type found in {full_name}");
4136 }
4137 SqlScalarType::AclItem
4138 | SqlScalarType::Bool
4139 | SqlScalarType::Int16
4140 | SqlScalarType::Int32
4141 | SqlScalarType::Int64
4142 | SqlScalarType::Float32
4143 | SqlScalarType::Float64
4144 | SqlScalarType::Numeric { .. }
4145 | SqlScalarType::Date
4146 | SqlScalarType::Time
4147 | SqlScalarType::Timestamp { .. }
4148 | SqlScalarType::TimestampTz { .. }
4149 | SqlScalarType::Interval
4150 | SqlScalarType::PgLegacyChar
4151 | SqlScalarType::Bytes
4152 | SqlScalarType::String
4153 | SqlScalarType::Char { .. }
4154 | SqlScalarType::VarChar { .. }
4155 | SqlScalarType::Jsonb
4156 | SqlScalarType::Uuid
4157 | SqlScalarType::Array(_)
4158 | SqlScalarType::Record { .. }
4159 | SqlScalarType::Oid
4160 | SqlScalarType::RegProc
4161 | SqlScalarType::RegType
4162 | SqlScalarType::RegClass
4163 | SqlScalarType::Int2Vector
4164 | SqlScalarType::Range { .. }
4165 | SqlScalarType::PgLegacyName => {}
4166 }
4167 }
4168 }
4169 catalog.expire().await;
4170 })
4171 .await
4172 }
4173
4174 #[mz_ore::test(tokio::test)]
4177 #[cfg_attr(miri, ignore)] async fn test_mz_introspection_builtins() {
4179 Catalog::with_debug(|catalog| async move {
4180 let conn_catalog = catalog.for_system_session();
4181
4182 let introspection_schema_id = catalog.get_mz_introspection_schema_id();
4183 let introspection_schema_spec = SchemaSpecifier::Id(introspection_schema_id);
4184
4185 for entry in catalog.entries() {
4186 let schema_spec = entry.name().qualifiers.schema_spec;
4187 let introspection_deps = catalog.introspection_dependencies(entry.id);
4188 if introspection_deps.is_empty() {
4189 assert!(
4190 schema_spec != introspection_schema_spec,
4191 "entry does not depend on introspection sources but is in \
4192 `mz_introspection`: {}",
4193 conn_catalog.resolve_full_name(entry.name()),
4194 );
4195 } else {
4196 assert!(
4197 schema_spec == introspection_schema_spec,
4198 "entry depends on introspection sources but is not in \
4199 `mz_introspection`: {}",
4200 conn_catalog.resolve_full_name(entry.name()),
4201 );
4202 }
4203 }
4204 })
4205 .await
4206 }
4207
4208 #[mz_ore::test(tokio::test)]
4209 #[cfg_attr(miri, ignore)] async fn test_multi_subscriber_catalog() {
4211 let persist_client = PersistClient::new_for_tests().await;
4212 let bootstrap_args = test_bootstrap_args();
4213 let organization_id = Uuid::new_v4();
4214 let db_name = "DB";
4215
4216 let mut writer_catalog = Catalog::open_debug_catalog(
4217 persist_client.clone(),
4218 organization_id.clone(),
4219 &bootstrap_args,
4220 )
4221 .await
4222 .expect("open_debug_catalog");
4223 let mut read_only_catalog = Catalog::open_debug_read_only_catalog(
4224 persist_client.clone(),
4225 organization_id.clone(),
4226 &bootstrap_args,
4227 )
4228 .await
4229 .expect("open_debug_read_only_catalog");
4230 assert_err!(writer_catalog.resolve_database(db_name));
4231 assert_err!(read_only_catalog.resolve_database(db_name));
4232
4233 let commit_ts = writer_catalog.current_upper().await;
4234 writer_catalog
4235 .transact(
4236 None,
4237 commit_ts,
4238 None,
4239 vec![Op::CreateDatabase {
4240 name: db_name.to_string(),
4241 owner_id: MZ_SYSTEM_ROLE_ID,
4242 }],
4243 )
4244 .await
4245 .expect("failed to transact");
4246
4247 let write_db = writer_catalog
4248 .resolve_database(db_name)
4249 .expect("resolve_database");
4250 read_only_catalog
4251 .sync_to_current_updates()
4252 .await
4253 .expect("sync_to_current_updates");
4254 let read_db = read_only_catalog
4255 .resolve_database(db_name)
4256 .expect("resolve_database");
4257
4258 assert_eq!(write_db, read_db);
4259
4260 let writer_catalog_fencer =
4261 Catalog::open_debug_catalog(persist_client, organization_id, &bootstrap_args)
4262 .await
4263 .expect("open_debug_catalog for fencer");
4264 let fencer_db = writer_catalog_fencer
4265 .resolve_database(db_name)
4266 .expect("resolve_database for fencer");
4267 assert_eq!(fencer_db, read_db);
4268
4269 let write_fence_err = writer_catalog
4270 .sync_to_current_updates()
4271 .await
4272 .expect_err("sync_to_current_updates for fencer");
4273 assert!(matches!(
4274 write_fence_err,
4275 CatalogError::Durable(DurableCatalogError::Fence(FenceError::Epoch { .. }))
4276 ));
4277 let read_fence_err = read_only_catalog
4278 .sync_to_current_updates()
4279 .await
4280 .expect_err("sync_to_current_updates after fencer");
4281 assert!(matches!(
4282 read_fence_err,
4283 CatalogError::Durable(DurableCatalogError::Fence(FenceError::Epoch { .. }))
4284 ));
4285
4286 writer_catalog.expire().await;
4287 read_only_catalog.expire().await;
4288 writer_catalog_fencer.expire().await;
4289 }
4290}