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