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