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