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: mz_compute_types::dataflows::LirDataflowDescription,
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<&mz_compute_types::dataflows::LirDataflowDescription> {
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: mz_compute_types::dataflows::LirDataflowDescription,
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 try_cast_tests;
2502
2503#[cfg(test)]
2504mod tests {
2505 use std::collections::{BTreeMap, BTreeSet};
2506 use std::sync::Arc;
2507 use std::{env, iter};
2508
2509 use itertools::Itertools;
2510 use mz_catalog::memory::objects::CatalogItem;
2511 use mz_postgres_util::{query, sql};
2512 use tokio_postgres::NoTls;
2513 use tokio_postgres::types::Type;
2514 use uuid::Uuid;
2515
2516 use mz_catalog::SYSTEM_CONN_ID;
2517 use mz_catalog::builtin::{BUILTINS, Builtin, BuiltinType};
2518 use mz_catalog::durable::{CatalogError, DurableCatalogError, FenceError, test_bootstrap_args};
2519 use mz_controller_types::{ClusterId, ReplicaId};
2520 use mz_expr::{Eval, MirScalarExpr};
2521 use mz_ore::now::to_datetime;
2522 use mz_ore::{assert_err, assert_ok, soft_assert_eq_or_log, task};
2523 use mz_persist_client::PersistClient;
2524 use mz_pgrepr::oid::{FIRST_MATERIALIZE_OID, FIRST_UNPINNED_OID, FIRST_USER_OID};
2525 use mz_repr::namespaces::{INFORMATION_SCHEMA, PG_CATALOG_SCHEMA};
2526 use mz_repr::role_id::RoleId;
2527 use mz_repr::{
2528 CatalogItemId, Datum, GlobalId, RelationVersionSelector, Row, RowArena, SqlRelationType,
2529 SqlScalarType, Timestamp,
2530 };
2531 use mz_sql::catalog::{CatalogSchema, CatalogType, SessionCatalog};
2532 use mz_sql::func::{Func, FuncImpl, OP_IMPLS, Operation};
2533 use mz_sql::names::{
2534 self, DatabaseId, ItemQualifiers, ObjectId, PartialItemName, QualifiedItemName,
2535 ResolvedDatabaseSpecifier, SchemaId, SchemaSpecifier, SystemObjectId,
2536 };
2537 use mz_sql::plan::{
2538 CoercibleScalarExpr, ExprContext, HirScalarExpr, HirToMirConfig, PlanContext, QueryContext,
2539 QueryLifetime, Scope, StatementContext,
2540 };
2541 use mz_sql::session::user::MZ_SYSTEM_ROLE_ID;
2542 use mz_sql::session::vars::{SystemVars, VarInput};
2543
2544 use crate::catalog::state::LocalExpressionCache;
2545 use crate::catalog::{Catalog, DebugAwsContext, Op};
2546 use crate::optimize::dataflows::{EvalTime, ExprPrep, ExprPrepOneShot};
2547 use crate::session::Session;
2548
2549 #[mz_ore::test(tokio::test)]
2556 #[cfg_attr(miri, ignore)] async fn test_minimal_qualification() {
2558 Catalog::with_debug(|catalog| async move {
2559 struct TestCase {
2560 input: QualifiedItemName,
2561 system_output: PartialItemName,
2562 normal_output: PartialItemName,
2563 }
2564
2565 let test_cases = vec![
2566 TestCase {
2567 input: QualifiedItemName {
2568 qualifiers: ItemQualifiers {
2569 database_spec: ResolvedDatabaseSpecifier::Ambient,
2570 schema_spec: SchemaSpecifier::Id(catalog.get_pg_catalog_schema_id()),
2571 },
2572 item: "numeric".to_string(),
2573 },
2574 system_output: PartialItemName {
2575 database: None,
2576 schema: None,
2577 item: "numeric".to_string(),
2578 },
2579 normal_output: PartialItemName {
2580 database: None,
2581 schema: None,
2582 item: "numeric".to_string(),
2583 },
2584 },
2585 TestCase {
2586 input: QualifiedItemName {
2587 qualifiers: ItemQualifiers {
2588 database_spec: ResolvedDatabaseSpecifier::Ambient,
2589 schema_spec: SchemaSpecifier::Id(catalog.get_mz_catalog_schema_id()),
2590 },
2591 item: "mz_array_types".to_string(),
2592 },
2593 system_output: PartialItemName {
2594 database: None,
2595 schema: None,
2596 item: "mz_array_types".to_string(),
2597 },
2598 normal_output: PartialItemName {
2599 database: None,
2600 schema: None,
2601 item: "mz_array_types".to_string(),
2602 },
2603 },
2604 ];
2605
2606 for tc in test_cases {
2607 assert_eq!(
2608 catalog
2609 .for_system_session()
2610 .minimal_qualification(&tc.input),
2611 tc.system_output
2612 );
2613 assert_eq!(
2614 catalog
2615 .for_session(&Session::dummy())
2616 .minimal_qualification(&tc.input),
2617 tc.normal_output
2618 );
2619 }
2620 catalog.expire().await;
2621 })
2622 .await
2623 }
2624
2625 #[mz_ore::test(tokio::test)]
2626 #[cfg_attr(miri, ignore)] async fn test_catalog_revision() {
2628 let persist_client = PersistClient::new_for_tests().await;
2629 let organization_id = Uuid::new_v4();
2630 let bootstrap_args = test_bootstrap_args();
2631 {
2632 let mut catalog = Catalog::open_debug_catalog(
2633 persist_client.clone(),
2634 organization_id.clone(),
2635 &bootstrap_args,
2636 )
2637 .await
2638 .expect("unable to open debug catalog");
2639 assert_eq!(catalog.transient_revision(), 1);
2640 assert!(catalog.transient_revision_is_current());
2641 let snapshot = catalog.clone();
2642 let commit_ts = catalog.current_upper().await;
2643 catalog
2644 .transact(
2645 None,
2646 commit_ts,
2647 None,
2648 vec![Op::CreateDatabase {
2649 name: "test".to_string(),
2650 owner_id: MZ_SYSTEM_ROLE_ID,
2651 }],
2652 )
2653 .await
2654 .expect("failed to transact");
2655 assert_eq!(catalog.transient_revision(), 2);
2656 assert!(catalog.transient_revision_is_current());
2657 assert!(!snapshot.transient_revision_is_current());
2660 catalog.expire().await;
2661 }
2662 {
2663 let catalog =
2664 Catalog::open_debug_catalog(persist_client, organization_id, &bootstrap_args)
2665 .await
2666 .expect("unable to open debug catalog");
2667 assert_eq!(catalog.transient_revision(), 1);
2669 catalog.expire().await;
2670 }
2671 }
2672
2673 #[mz_ore::test(tokio::test)]
2674 #[cfg_attr(miri, ignore)] async fn test_effective_search_path() {
2676 Catalog::with_debug(|catalog| async move {
2677 let mz_catalog_schema = (
2678 ResolvedDatabaseSpecifier::Ambient,
2679 SchemaSpecifier::Id(catalog.state().get_mz_catalog_schema_id()),
2680 );
2681 let pg_catalog_schema = (
2682 ResolvedDatabaseSpecifier::Ambient,
2683 SchemaSpecifier::Id(catalog.state().get_pg_catalog_schema_id()),
2684 );
2685 let mz_temp_schema = (
2686 ResolvedDatabaseSpecifier::Ambient,
2687 SchemaSpecifier::Temporary,
2688 );
2689
2690 let session = Session::dummy();
2692 let conn_catalog = catalog.for_session(&session);
2693 assert_ne!(
2694 conn_catalog.effective_search_path(false),
2695 conn_catalog.search_path
2696 );
2697 assert_ne!(
2698 conn_catalog.effective_search_path(true),
2699 conn_catalog.search_path
2700 );
2701 assert_eq!(
2702 conn_catalog.effective_search_path(false),
2703 vec![
2704 mz_catalog_schema.clone(),
2705 pg_catalog_schema.clone(),
2706 conn_catalog.search_path[0].clone()
2707 ]
2708 );
2709 assert_eq!(
2710 conn_catalog.effective_search_path(true),
2711 vec![
2712 mz_temp_schema.clone(),
2713 mz_catalog_schema.clone(),
2714 pg_catalog_schema.clone(),
2715 conn_catalog.search_path[0].clone()
2716 ]
2717 );
2718
2719 let mut session = Session::dummy();
2721 session
2722 .vars_mut()
2723 .set(
2724 &SystemVars::new(),
2725 "search_path",
2726 VarInput::Flat(mz_repr::namespaces::PG_CATALOG_SCHEMA),
2727 false,
2728 )
2729 .expect("failed to set search_path");
2730 let conn_catalog = catalog.for_session(&session);
2731 assert_ne!(
2732 conn_catalog.effective_search_path(false),
2733 conn_catalog.search_path
2734 );
2735 assert_ne!(
2736 conn_catalog.effective_search_path(true),
2737 conn_catalog.search_path
2738 );
2739 assert_eq!(
2740 conn_catalog.effective_search_path(false),
2741 vec![mz_catalog_schema.clone(), pg_catalog_schema.clone()]
2742 );
2743 assert_eq!(
2744 conn_catalog.effective_search_path(true),
2745 vec![
2746 mz_temp_schema.clone(),
2747 mz_catalog_schema.clone(),
2748 pg_catalog_schema.clone()
2749 ]
2750 );
2751
2752 let mut session = Session::dummy();
2753 session
2754 .vars_mut()
2755 .set(
2756 &SystemVars::new(),
2757 "search_path",
2758 VarInput::Flat(mz_repr::namespaces::MZ_CATALOG_SCHEMA),
2759 false,
2760 )
2761 .expect("failed to set search_path");
2762 let conn_catalog = catalog.for_session(&session);
2763 assert_ne!(
2764 conn_catalog.effective_search_path(false),
2765 conn_catalog.search_path
2766 );
2767 assert_ne!(
2768 conn_catalog.effective_search_path(true),
2769 conn_catalog.search_path
2770 );
2771 assert_eq!(
2772 conn_catalog.effective_search_path(false),
2773 vec![pg_catalog_schema.clone(), mz_catalog_schema.clone()]
2774 );
2775 assert_eq!(
2776 conn_catalog.effective_search_path(true),
2777 vec![
2778 mz_temp_schema.clone(),
2779 pg_catalog_schema.clone(),
2780 mz_catalog_schema.clone()
2781 ]
2782 );
2783
2784 let mut session = Session::dummy();
2785 session
2786 .vars_mut()
2787 .set(
2788 &SystemVars::new(),
2789 "search_path",
2790 VarInput::Flat(mz_repr::namespaces::MZ_TEMP_SCHEMA),
2791 false,
2792 )
2793 .expect("failed to set search_path");
2794 let conn_catalog = catalog.for_session(&session);
2795 assert_ne!(
2796 conn_catalog.effective_search_path(false),
2797 conn_catalog.search_path
2798 );
2799 assert_ne!(
2800 conn_catalog.effective_search_path(true),
2801 conn_catalog.search_path
2802 );
2803 assert_eq!(
2808 conn_catalog.effective_search_path(false),
2809 vec![mz_catalog_schema.clone(), pg_catalog_schema.clone(),]
2810 );
2811 assert_eq!(
2812 conn_catalog.effective_search_path(true),
2813 vec![mz_temp_schema, mz_catalog_schema, pg_catalog_schema]
2814 );
2815 catalog.expire().await;
2816 })
2817 .await
2818 }
2819
2820 #[mz_ore::test(tokio::test)]
2821 #[cfg_attr(miri, ignore)] async fn test_normalized_create() {
2823 use mz_ore::collections::CollectionExt;
2824 Catalog::with_debug(|catalog| async move {
2825 let conn_catalog = catalog.for_system_session();
2826 let scx = &mut StatementContext::new(None, &conn_catalog);
2827
2828 let parsed = mz_sql_parser::parser::parse_statements(
2829 "create view public.foo as select 1 as bar",
2830 )
2831 .expect("")
2832 .into_element()
2833 .ast;
2834
2835 let (stmt, _) = names::resolve(scx.catalog, parsed).expect("");
2836
2837 assert_eq!(
2839 r#"CREATE VIEW "materialize"."public"."foo" AS SELECT 1 AS "bar""#,
2840 mz_sql::normalize::create_statement(scx, stmt).expect(""),
2841 );
2842 catalog.expire().await;
2843 })
2844 .await;
2845 }
2846
2847 #[mz_ore::test(tokio::test)]
2854 #[cfg_attr(miri, ignore)] async fn test_resolved_ids_survive_create_sql_round_trip() {
2856 use mz_ore::collections::CollectionExt;
2857 Catalog::with_debug(|catalog| async move {
2858 let conn_catalog = catalog.for_system_session();
2859 let scx = &mut StatementContext::new(None, &conn_catalog);
2860
2861 let resolve_and_normalize = |scx: &mut StatementContext, sql: &str| {
2862 let parsed = mz_sql_parser::parser::parse_statements(sql)
2863 .expect("parses")
2864 .into_element()
2865 .ast;
2866 let (stmt, ids) = names::resolve(scx.catalog, parsed).expect("resolves");
2867 let normalized =
2868 mz_sql::normalize::create_statement(scx, stmt).expect("normalizes");
2869 (ids, normalized)
2870 };
2871
2872 const ARRAY_SUFFIX: &str = "create table public.t (a pg_catalog.int4[])";
2873 const ARRAY_TYPE: &str = "create table public.t (a pg_catalog._int4)";
2874 const ELEMENT_TYPE: &str = "create table public.t (a pg_catalog.int4)";
2875
2876 let mut ids_by_spelling: BTreeMap<&str, Vec<CatalogItemId>> = BTreeMap::new();
2877 for sql in [
2878 ARRAY_SUFFIX,
2879 ARRAY_TYPE,
2880 ELEMENT_TYPE,
2881 "create table public.t (a pg_catalog.int4 list)",
2882 "create view public.v as select null::pg_catalog.text[]",
2883 ] {
2884 let (ids, normalized) = resolve_and_normalize(scx, sql);
2885 let (round_tripped_ids, _) = resolve_and_normalize(scx, &normalized);
2886 assert_eq!(
2887 ids.items().collect::<Vec<_>>(),
2888 round_tripped_ids.items().collect::<Vec<_>>(),
2889 "resolving {normalized:?} produced different ids than {sql:?}",
2890 );
2891 ids_by_spelling.insert(sql, ids.items().copied().collect());
2892 }
2893
2894 assert_eq!(
2899 ids_by_spelling[ARRAY_SUFFIX], ids_by_spelling[ARRAY_TYPE],
2900 "{ARRAY_SUFFIX:?} and {ARRAY_TYPE:?} must resolve to the same ids",
2901 );
2902
2903 let element_ids = &ids_by_spelling[ELEMENT_TYPE];
2906 assert!(
2907 ids_by_spelling[ARRAY_SUFFIX]
2908 .iter()
2909 .all(|id| !element_ids.contains(id)),
2910 "{ARRAY_SUFFIX:?} recorded an element type id from {ELEMENT_TYPE:?}",
2911 );
2912
2913 catalog.expire().await;
2914 })
2915 .await;
2916 }
2917
2918 #[mz_ore::test(tokio::test)]
2920 #[cfg_attr(miri, ignore)] async fn test_large_catalog_item() {
2922 let view_def = "CREATE VIEW \"materialize\".\"public\".\"v\" AS SELECT 1 FROM (SELECT 1";
2923 let column = ", 1";
2924 let view_def_size = view_def.bytes().count();
2925 let column_size = column.bytes().count();
2926 let column_count =
2927 (mz_sql_parser::parser::MAX_STATEMENT_BATCH_SIZE - view_def_size) / column_size + 1;
2928 let columns = iter::repeat(column).take(column_count).join("");
2929 let create_sql = format!("{view_def}{columns})");
2930 let create_sql_check = create_sql.clone();
2931 assert_ok!(mz_sql_parser::parser::parse_statements(&create_sql));
2932 assert_err!(mz_sql_parser::parser::parse_statements_with_limit(
2933 &create_sql
2934 ));
2935
2936 let persist_client = PersistClient::new_for_tests().await;
2937 let organization_id = Uuid::new_v4();
2938 let id = CatalogItemId::User(1);
2939 let gid = GlobalId::User(1);
2940 let bootstrap_args = test_bootstrap_args();
2941 {
2942 let mut catalog = Catalog::open_debug_catalog(
2943 persist_client.clone(),
2944 organization_id.clone(),
2945 &bootstrap_args,
2946 )
2947 .await
2948 .expect("unable to open debug catalog");
2949 let item = catalog
2950 .state()
2951 .deserialize_item(
2952 gid,
2953 &create_sql,
2954 &BTreeMap::new(),
2955 &mut LocalExpressionCache::Closed,
2956 None,
2957 )
2958 .expect("unable to parse view");
2959 let commit_ts = catalog.current_upper().await;
2960 catalog
2961 .transact(
2962 None,
2963 commit_ts,
2964 None,
2965 vec![Op::CreateItem {
2966 item,
2967 name: QualifiedItemName {
2968 qualifiers: ItemQualifiers {
2969 database_spec: ResolvedDatabaseSpecifier::Id(DatabaseId::User(1)),
2970 schema_spec: SchemaSpecifier::Id(SchemaId::User(3)),
2971 },
2972 item: "v".to_string(),
2973 },
2974 id,
2975 owner_id: MZ_SYSTEM_ROLE_ID,
2976 }],
2977 )
2978 .await
2979 .expect("failed to transact");
2980 catalog.expire().await;
2981 }
2982 {
2983 let catalog =
2984 Catalog::open_debug_catalog(persist_client, organization_id, &bootstrap_args)
2985 .await
2986 .expect("unable to open debug catalog");
2987 let view = catalog.get_entry(&id);
2988 assert_eq!("v", view.name.item);
2989 match &view.item {
2990 CatalogItem::View(view) => assert_eq!(create_sql_check, view.create_sql),
2991 item => panic!("expected view, got {}", item.typ()),
2992 }
2993 catalog.expire().await;
2994 }
2995 }
2996
2997 #[mz_ore::test(tokio::test)]
2998 #[cfg_attr(miri, ignore)] async fn test_object_type() {
3000 Catalog::with_debug(|catalog| async move {
3001 let conn_catalog = catalog.for_system_session();
3002
3003 assert_eq!(
3004 mz_sql::catalog::ObjectType::ClusterReplica,
3005 conn_catalog.get_object_type(&ObjectId::ClusterReplica((
3006 ClusterId::user(1).expect("1 is a valid ID"),
3007 ReplicaId::User(1)
3008 )))
3009 );
3010 assert_eq!(
3011 mz_sql::catalog::ObjectType::Role,
3012 conn_catalog.get_object_type(&ObjectId::Role(RoleId::User(1)))
3013 );
3014 catalog.expire().await;
3015 })
3016 .await;
3017 }
3018
3019 #[mz_ore::test(tokio::test)]
3020 #[cfg_attr(miri, ignore)] async fn test_get_privileges() {
3022 Catalog::with_debug(|catalog| async move {
3023 let conn_catalog = catalog.for_system_session();
3024
3025 assert_eq!(
3026 None,
3027 conn_catalog.get_privileges(&SystemObjectId::Object(ObjectId::ClusterReplica((
3028 ClusterId::user(1).expect("1 is a valid ID"),
3029 ReplicaId::User(1),
3030 ))))
3031 );
3032 assert_eq!(
3033 None,
3034 conn_catalog
3035 .get_privileges(&SystemObjectId::Object(ObjectId::Role(RoleId::User(1))))
3036 );
3037 catalog.expire().await;
3038 })
3039 .await;
3040 }
3041
3042 #[mz_ore::test(tokio::test)]
3043 #[cfg_attr(miri, ignore)] async fn verify_builtin_descs() {
3045 let aws_context = DebugAwsContext {
3052 aws_account_id: Some("123456789000".to_string()),
3053 aws_external_id_prefix: Some("eb5cb59b-e2fe-41f3-87ca-d2176a495345".to_string()),
3054 aws_connection_role_arn: Some(
3055 "arn:aws:iam::123456789000:role/MaterializeConnection".to_string(),
3056 ),
3057 };
3058 Catalog::with_debug_aws_context(aws_context, |catalog| async move {
3059 let conn_catalog = catalog.for_system_session();
3060
3061 for builtin in BUILTINS::iter() {
3062 let (schema, name, expected_desc, shard_backed) = match builtin {
3063 Builtin::Table(t) => (&t.schema, &t.name, &t.desc, true),
3064 Builtin::View(v) => (&v.schema, &v.name, &v.desc, false),
3065 Builtin::MaterializedView(mv) => (&mv.schema, &mv.name, &mv.desc, true),
3066 Builtin::Source(s) => (&s.schema, &s.name, &s.desc, true),
3067 Builtin::Log(_)
3068 | Builtin::Type(_)
3069 | Builtin::Func(_)
3070 | Builtin::Index(_)
3071 | Builtin::Connection(_) => continue,
3072 };
3073 let migration_hint = if shard_backed {
3080 format!(
3081 "; {schema}.{name} is backed by a persist shard, so after updating its \
3082 `desc` also add a `MigrationStep` for it to `MIGRATIONS` in \
3083 `src/adapter/src/catalog/open/builtin_schema_migration.rs`"
3084 )
3085 } else {
3086 String::new()
3087 };
3088 let item = conn_catalog
3089 .resolve_item(&PartialItemName {
3090 database: None,
3091 schema: Some(schema.to_string()),
3092 item: name.to_string(),
3093 })
3094 .expect("unable to resolve item")
3095 .at_version(RelationVersionSelector::Latest);
3096
3097 let actual_desc = item.relation_desc().expect("invalid item type");
3098 assert_eq!(
3099 actual_desc.arity(),
3100 expected_desc.arity(),
3101 "item {schema}.{name} column count did not match its expected column count{migration_hint}"
3102 );
3103 for (index, ((actual_name, actual_typ), (expected_name, expected_typ))) in
3104 actual_desc.iter().zip_eq(expected_desc.iter()).enumerate()
3105 {
3106 assert_eq!(
3107 actual_name, expected_name,
3108 "item {schema}.{name} column {index} name did not match its expected name{migration_hint}"
3109 );
3110 assert_eq!(
3111 actual_typ, expected_typ,
3112 "item {schema}.{name} column {index} ('{actual_name}') type did not match its expected type{migration_hint}"
3113 );
3114 }
3115 assert_eq!(
3118 actual_desc.typ().keys,
3119 expected_desc.typ().keys,
3120 "item {schema}.{name} keys did not match its expected keys"
3121 );
3122 assert_eq!(
3123 &*actual_desc, expected_desc,
3124 "item {schema}.{name} did not match its expected RelationDesc"
3125 );
3126 }
3127 catalog.expire().await;
3128 })
3129 .await
3130 }
3131
3132 #[mz_ore::test(tokio::test)]
3135 #[cfg_attr(miri, ignore)] async fn test_compare_builtins_postgres() {
3137 async fn inner(catalog: Catalog) {
3138 let (client, connection) = tokio_postgres::connect(
3142 &env::var("POSTGRES_URL").unwrap_or_else(|_| "host=localhost user=postgres".into()),
3143 NoTls,
3144 )
3145 .await
3146 .expect("failed to connect to Postgres");
3147
3148 task::spawn(|| "compare_builtin_postgres", async move {
3149 if let Err(e) = connection.await {
3150 panic!("connection error: {}", e);
3151 }
3152 });
3153
3154 struct PgProc {
3155 name: String,
3156 arg_oids: Vec<u32>,
3157 ret_oid: Option<u32>,
3158 ret_set: bool,
3159 }
3160
3161 struct PgType {
3162 name: String,
3163 ty: String,
3164 elem: u32,
3165 array: u32,
3166 input: u32,
3167 receive: u32,
3168 send: u32,
3169 len: i16,
3170 }
3171
3172 struct PgOper {
3173 oprresult: u32,
3174 name: String,
3175 }
3176
3177 let pg_proc: BTreeMap<_, _> = query(
3178 &client,
3179 sql!(
3180 "SELECT
3181 p.oid,
3182 proname,
3183 proargtypes,
3184 prorettype,
3185 proretset
3186 FROM pg_proc p
3187 JOIN pg_namespace n ON p.pronamespace = n.oid"
3188 ),
3189 &[],
3190 )
3191 .await
3192 .expect("pg query failed")
3193 .into_iter()
3194 .map(|row| {
3195 let oid: u32 = row.get("oid");
3196 let pg_proc = PgProc {
3197 name: row.get("proname"),
3198 arg_oids: row.get("proargtypes"),
3199 ret_oid: row.get("prorettype"),
3200 ret_set: row.get("proretset"),
3201 };
3202 (oid, pg_proc)
3203 })
3204 .collect();
3205
3206 let pg_type: BTreeMap<_, _> = query(
3207 &client,
3208 sql!(
3209 "SELECT oid, typname, typtype::text, typelem, typarray, typinput::oid, typreceive::oid as typreceive, typsend::oid as typsend, typlen FROM pg_type"
3210 ),
3211 &[],
3212 )
3213 .await
3214 .expect("pg query failed")
3215 .into_iter()
3216 .map(|row| {
3217 let oid: u32 = row.get("oid");
3218 let pg_type = PgType {
3219 name: row.get("typname"),
3220 ty: row.get("typtype"),
3221 elem: row.get("typelem"),
3222 array: row.get("typarray"),
3223 input: row.get("typinput"),
3224 receive: row.get("typreceive"),
3225 send: row.get("typsend"),
3226 len: row.get("typlen"),
3227 };
3228 (oid, pg_type)
3229 })
3230 .collect();
3231
3232 let pg_oper: BTreeMap<_, _> = query(
3233 &client,
3234 sql!("SELECT oid, oprname, oprresult FROM pg_operator"),
3235 &[],
3236 )
3237 .await
3238 .expect("pg query failed")
3239 .into_iter()
3240 .map(|row| {
3241 let oid: u32 = row.get("oid");
3242 let pg_oper = PgOper {
3243 name: row.get("oprname"),
3244 oprresult: row.get("oprresult"),
3245 };
3246 (oid, pg_oper)
3247 })
3248 .collect();
3249
3250 let conn_catalog = catalog.for_system_session();
3251 let resolve_type_oid = |item: &str| {
3252 conn_catalog
3253 .resolve_type(&PartialItemName {
3254 database: None,
3255 schema: Some(PG_CATALOG_SCHEMA.into()),
3258 item: item.to_string(),
3259 })
3260 .expect("unable to resolve type")
3261 .oid()
3262 };
3263
3264 let func_oids: BTreeSet<_> = BUILTINS::funcs()
3265 .flat_map(|f| f.inner.func_impls().into_iter().map(|f| f.oid))
3266 .collect();
3267
3268 let mut all_oids = BTreeSet::new();
3269
3270 let equivalent_types: BTreeSet<(Option<u32>, Option<u32>)> = BTreeSet::from_iter(
3273 [
3274 (Type::NAME, Type::TEXT),
3276 (Type::NAME_ARRAY, Type::TEXT_ARRAY),
3277 (Type::TIME, Type::TIMETZ),
3279 (Type::TIME_ARRAY, Type::TIMETZ_ARRAY),
3280 ]
3281 .map(|(a, b)| (Some(a.oid()), Some(b.oid()))),
3282 );
3283 let ignore_return_types: BTreeSet<u32> = BTreeSet::from([
3284 1619, ]);
3286 let is_same_type = |fn_oid: u32, a: Option<u32>, b: Option<u32>| -> bool {
3287 if ignore_return_types.contains(&fn_oid) {
3288 return true;
3289 }
3290 if equivalent_types.contains(&(a, b)) || equivalent_types.contains(&(b, a)) {
3291 return true;
3292 }
3293 a == b
3294 };
3295
3296 for builtin in BUILTINS::iter() {
3297 match builtin {
3298 Builtin::Type(ty) => {
3299 assert!(all_oids.insert(ty.oid), "{} reused oid {}", ty.name, ty.oid);
3300
3301 if ty.oid >= FIRST_MATERIALIZE_OID {
3302 continue;
3305 }
3306
3307 let pg_ty = pg_type.get(&ty.oid).unwrap_or_else(|| {
3310 panic!("pg_proc missing type {}: oid {}", ty.name, ty.oid)
3311 });
3312 assert_eq!(
3313 ty.name, pg_ty.name,
3314 "oid {} has name {} in postgres; expected {}",
3315 ty.oid, pg_ty.name, ty.name,
3316 );
3317
3318 let (typinput_oid, typreceive_oid, typsend_oid) =
3319 match &ty.details.pg_metadata {
3320 None => (0, 0, 0),
3321 Some(pgmeta) => (
3322 pgmeta.typinput_oid,
3323 pgmeta.typreceive_oid,
3324 pgmeta.typsend_oid,
3325 ),
3326 };
3327 assert_eq!(
3328 typinput_oid, pg_ty.input,
3329 "type {} has typinput OID {:?} in mz but {:?} in pg",
3330 ty.name, typinput_oid, pg_ty.input,
3331 );
3332 assert_eq!(
3333 typreceive_oid, pg_ty.receive,
3334 "type {} has typreceive OID {:?} in mz but {:?} in pg",
3335 ty.name, typreceive_oid, pg_ty.receive,
3336 );
3337 assert_eq!(
3342 typsend_oid, pg_ty.send,
3343 "type {} has typsend OID {:?} in mz but {:?} in pg",
3344 ty.name, typsend_oid, pg_ty.send,
3345 );
3346 if typinput_oid != 0 {
3347 assert!(
3348 func_oids.contains(&typinput_oid),
3349 "type {} has typinput OID {} that does not exist in pg_proc",
3350 ty.name,
3351 typinput_oid,
3352 );
3353 }
3354 if typreceive_oid != 0 {
3355 assert!(
3356 func_oids.contains(&typreceive_oid),
3357 "type {} has typreceive OID {} that does not exist in pg_proc",
3358 ty.name,
3359 typreceive_oid,
3360 );
3361 }
3362 let id = conn_catalog
3363 .resolve_type(&PartialItemName {
3364 database: None,
3365 schema: Some(ty.schema.into()),
3366 item: ty.name.to_string(),
3367 })
3368 .expect("unable to resolve type")
3369 .id();
3370 if let Ok(scalar_type) =
3372 mz_sql::plan::scalar_type_from_catalog(&conn_catalog, id, &[])
3373 {
3374 let typlen = mz_pgrepr::Type::from(&scalar_type).typlen();
3375 assert_eq!(
3376 typlen, pg_ty.len,
3377 "type {} has typlen {} in mz but {} in pg",
3378 ty.name, typlen, pg_ty.len,
3379 );
3380 }
3381
3382 match &ty.details.typ {
3384 CatalogType::Array { element_reference } => {
3385 let elem_ty = BUILTINS::iter()
3386 .filter_map(|builtin| match builtin {
3387 Builtin::Type(ty @ BuiltinType { name, .. })
3388 if element_reference == name =>
3389 {
3390 Some(ty)
3391 }
3392 _ => None,
3393 })
3394 .next();
3395 let elem_ty = match elem_ty {
3396 Some(ty) => ty,
3397 None => {
3398 panic!("{} is unexpectedly not a type", element_reference)
3399 }
3400 };
3401 assert_eq!(
3402 pg_ty.elem, elem_ty.oid,
3403 "type {} has mismatched element OIDs",
3404 ty.name
3405 )
3406 }
3407 CatalogType::Pseudo => {
3408 assert_eq!(
3409 pg_ty.ty, "p",
3410 "type {} is not a pseudo type as expected",
3411 ty.name
3412 )
3413 }
3414 CatalogType::Range { .. } => {
3415 assert_eq!(
3416 pg_ty.ty, "r",
3417 "type {} is not a range type as expected",
3418 ty.name
3419 );
3420 }
3421 _ => {
3422 assert_eq!(
3423 pg_ty.ty, "b",
3424 "type {} is not a base type as expected",
3425 ty.name
3426 )
3427 }
3428 }
3429
3430 let schema = catalog
3432 .resolve_schema_in_database(
3433 &ResolvedDatabaseSpecifier::Ambient,
3434 ty.schema,
3435 &SYSTEM_CONN_ID,
3436 )
3437 .expect("unable to resolve schema");
3438 let allocated_type = catalog
3439 .resolve_type(
3440 None,
3441 &vec![(ResolvedDatabaseSpecifier::Ambient, schema.id().clone())],
3442 &PartialItemName {
3443 database: None,
3444 schema: Some(schema.name().schema.clone()),
3445 item: ty.name.to_string(),
3446 },
3447 &SYSTEM_CONN_ID,
3448 )
3449 .expect("unable to resolve type");
3450 let ty = if let CatalogItem::Type(ty) = &allocated_type.item {
3451 ty
3452 } else {
3453 panic!("unexpectedly not a type")
3454 };
3455 match ty.details.array_id {
3456 Some(array_id) => {
3457 let array_ty = catalog.get_entry(&array_id);
3458 assert_eq!(
3459 pg_ty.array, array_ty.oid,
3460 "type {} has mismatched array OIDs",
3461 allocated_type.name.item,
3462 );
3463 }
3464 None => assert_eq!(
3465 pg_ty.array, 0,
3466 "type {} does not have an array type in mz but does in pg",
3467 allocated_type.name.item,
3468 ),
3469 }
3470 }
3471 Builtin::Func(func) => {
3472 for imp in func.inner.func_impls() {
3473 assert!(
3474 all_oids.insert(imp.oid),
3475 "{} reused oid {}",
3476 func.name,
3477 imp.oid
3478 );
3479
3480 assert!(
3481 imp.oid < FIRST_USER_OID,
3482 "built-in function {} erroneously has OID in user space ({})",
3483 func.name,
3484 imp.oid,
3485 );
3486
3487 let pg_fn = if imp.oid >= FIRST_UNPINNED_OID {
3490 continue;
3491 } else {
3492 pg_proc.get(&imp.oid).unwrap_or_else(|| {
3493 panic!(
3494 "pg_proc missing function {}: oid {}",
3495 func.name, imp.oid
3496 )
3497 })
3498 };
3499 assert_eq!(
3500 func.name, pg_fn.name,
3501 "funcs with oid {} don't match names: {} in mz, {} in pg",
3502 imp.oid, func.name, pg_fn.name
3503 );
3504
3505 let imp_arg_oids = imp
3508 .arg_typs
3509 .iter()
3510 .map(|item| resolve_type_oid(item))
3511 .collect::<Vec<_>>();
3512
3513 if imp_arg_oids != pg_fn.arg_oids {
3514 println!(
3515 "funcs with oid {} ({}) don't match arguments: {:?} in mz, {:?} in pg",
3516 imp.oid, func.name, imp_arg_oids, pg_fn.arg_oids
3517 );
3518 }
3519
3520 let imp_return_oid = imp.return_typ.map(resolve_type_oid);
3521
3522 assert!(
3523 is_same_type(imp.oid, imp_return_oid, pg_fn.ret_oid),
3524 "funcs with oid {} ({}) don't match return types: {:?} in mz, {:?} in pg",
3525 imp.oid,
3526 func.name,
3527 imp_return_oid,
3528 pg_fn.ret_oid
3529 );
3530
3531 assert_eq!(
3532 imp.return_is_set, pg_fn.ret_set,
3533 "funcs with oid {} ({}) don't match set-returning value: {:?} in mz, {:?} in pg",
3534 imp.oid, func.name, imp.return_is_set, pg_fn.ret_set
3535 );
3536 }
3537 }
3538 _ => (),
3539 }
3540 }
3541
3542 for (op, func) in OP_IMPLS.iter() {
3543 for imp in func.func_impls() {
3544 assert!(all_oids.insert(imp.oid), "{} reused oid {}", op, imp.oid);
3545
3546 let pg_op = if imp.oid >= FIRST_UNPINNED_OID {
3548 continue;
3549 } else {
3550 pg_oper.get(&imp.oid).unwrap_or_else(|| {
3551 panic!("pg_operator missing operator {}: oid {}", op, imp.oid)
3552 })
3553 };
3554
3555 assert_eq!(*op, pg_op.name);
3556
3557 let imp_return_oid =
3558 imp.return_typ.map(resolve_type_oid).expect("must have oid");
3559 if imp_return_oid != pg_op.oprresult {
3560 panic!(
3561 "operators with oid {} ({}) don't match return typs: {} in mz, {} in pg",
3562 imp.oid, op, imp_return_oid, pg_op.oprresult
3563 );
3564 }
3565 }
3566 }
3567 catalog.expire().await;
3568 }
3569
3570 Catalog::with_debug(inner).await
3571 }
3572
3573 #[mz_ore::test(tokio::test)]
3575 #[cfg_attr(miri, ignore)] async fn test_smoketest_all_builtins() {
3577 fn inner(catalog: Catalog) -> Vec<mz_ore::task::JoinHandle<()>> {
3578 let catalog = Arc::new(catalog);
3579 let conn_catalog = catalog.for_system_session();
3580
3581 let resolve_type_oid = |item: &str| conn_catalog.state().get_system_type(item).oid();
3582 let mut handles = Vec::new();
3583
3584 let ignore_names = BTreeSet::from([
3586 "avg",
3587 "avg_internal_v1",
3588 "bool_and",
3589 "bool_or",
3590 "has_table_privilege", "has_type_privilege", "mod",
3593 "mz_panic",
3594 "mz_sleep",
3595 "pow",
3596 "stddev_pop",
3597 "stddev_samp",
3598 "stddev",
3599 "var_pop",
3600 "var_samp",
3601 "variance",
3602 ]);
3603
3604 let fns = BUILTINS::funcs()
3605 .map(|func| (&func.name, func.inner))
3606 .chain(OP_IMPLS.iter());
3607
3608 for (name, func) in fns {
3609 if ignore_names.contains(name) {
3610 continue;
3611 }
3612 let Func::Scalar(impls) = func else {
3613 continue;
3614 };
3615
3616 'outer: for imp in impls {
3617 let details = imp.details();
3618 let mut styps = Vec::new();
3619 for item in details.arg_typs.iter() {
3620 let oid = resolve_type_oid(item);
3621 let Ok(pgtyp) = mz_pgrepr::Type::from_oid(oid) else {
3622 continue 'outer;
3623 };
3624 styps.push(SqlScalarType::try_from(&pgtyp).expect("must exist"));
3625 }
3626 let datums = styps
3627 .iter()
3628 .map(|styp| {
3629 let mut datums = vec![Datum::Null];
3630 datums.extend(styp.interesting_datums());
3631 datums
3632 })
3633 .collect::<Vec<_>>();
3634 if datums.is_empty() {
3636 continue;
3637 }
3638
3639 let return_oid = details
3640 .return_typ
3641 .map(resolve_type_oid)
3642 .expect("must exist");
3643 let return_styp = mz_pgrepr::Type::from_oid(return_oid)
3644 .ok()
3645 .map(|typ| SqlScalarType::try_from(&typ).expect("must exist"));
3646
3647 let mut idxs = vec![0; datums.len()];
3648 while idxs[0] < datums[0].len() {
3649 let mut args = Vec::with_capacity(idxs.len());
3650 for i in 0..(datums.len()) {
3651 args.push(datums[i][idxs[i]]);
3652 }
3653
3654 let op = &imp.op;
3655 let scalars = args
3656 .iter()
3657 .enumerate()
3658 .map(|(i, datum)| {
3659 CoercibleScalarExpr::Coerced(HirScalarExpr::literal(
3660 datum.clone(),
3661 styps[i].clone(),
3662 ))
3663 })
3664 .collect();
3665
3666 let call_name = format!(
3667 "{name}({}) (oid: {})",
3668 args.iter()
3669 .map(|d| d.to_string())
3670 .collect::<Vec<_>>()
3671 .join(", "),
3672 imp.oid
3673 );
3674 let catalog = Arc::clone(&catalog);
3675 let call_name_fn = call_name.clone();
3676 let return_styp = return_styp.clone();
3677 let handle = task::spawn_blocking(
3678 || call_name,
3679 move || {
3680 smoketest_fn(
3681 name,
3682 call_name_fn,
3683 op,
3684 imp,
3685 args,
3686 catalog,
3687 scalars,
3688 return_styp,
3689 )
3690 },
3691 );
3692 handles.push(handle);
3693
3694 for i in (0..datums.len()).rev() {
3696 idxs[i] += 1;
3697 if idxs[i] >= datums[i].len() {
3698 if i == 0 {
3699 break;
3700 }
3701 idxs[i] = 0;
3702 continue;
3703 } else {
3704 break;
3705 }
3706 }
3707 }
3708 }
3709 }
3710 handles
3711 }
3712
3713 let handles = Catalog::with_debug(|catalog| async { inner(catalog) }).await;
3714 for handle in handles {
3715 handle.await;
3716 }
3717 }
3718
3719 fn smoketest_fn(
3720 name: &&str,
3721 call_name: String,
3722 op: &Operation<HirScalarExpr>,
3723 imp: &FuncImpl<HirScalarExpr>,
3724 args: Vec<Datum<'_>>,
3725 catalog: Arc<Catalog>,
3726 scalars: Vec<CoercibleScalarExpr>,
3727 return_styp: Option<SqlScalarType>,
3728 ) {
3729 let conn_catalog = catalog.for_system_session();
3730 let pcx = PlanContext::zero();
3731 let scx = StatementContext::new(Some(&pcx), &conn_catalog);
3732 let qcx = QueryContext::root(&scx, QueryLifetime::OneShot);
3733 let ecx = ExprContext {
3734 qcx: &qcx,
3735 name: "smoketest",
3736 scope: &Scope::empty(),
3737 relation_type: &SqlRelationType::empty(),
3738 allow_aggregates: false,
3739 allow_subqueries: false,
3740 allow_parameters: false,
3741 allow_windows: false,
3742 };
3743 let arena = RowArena::new();
3744 let mut session = Session::dummy();
3745 session
3746 .start_transaction(to_datetime(0), None, None)
3747 .expect("must succeed");
3748 let prep_style = ExprPrepOneShot {
3749 logical_time: EvalTime::Time(Timestamp::MIN),
3750 session: &session,
3751 catalog_state: &catalog.state,
3752 };
3753
3754 let res = (op.0)(&ecx, scalars, &imp.params, vec![]);
3757 if let Ok(hir) = res {
3758 let uneliminated_result_row = {
3759 if let HirScalarExpr::CallUnary { func, .. } = &hir
3760 && func.is_eliminable_cast()
3761 {
3762 let mut uneliminated_mir = hir
3763 .clone()
3764 .lower_uncorrelated(HirToMirConfig {
3765 enable_cast_elimination: false,
3766 ..catalog.system_config().into()
3767 })
3768 .expect("lowering eliminable cast should always succeed");
3769 prep_style
3770 .prep_scalar_expr(&mut uneliminated_mir)
3771 .expect("must succeed");
3772
3773 uneliminated_mir
3775 .eval(&[], &arena)
3776 .ok()
3777 .map(|datum| Row::pack([datum]))
3778 } else {
3779 None
3780 }
3781 };
3782
3783 if let Ok(mut mir) = hir.lower_uncorrelated(catalog.system_config()) {
3784 prep_style.prep_scalar_expr(&mut mir).expect("must succeed");
3786
3787 if let Ok(eval_result_datum) = mir.eval(&[], &arena) {
3788 if let Some(return_styp) = return_styp {
3789 let mir_typ = mir.typ(&[]);
3790 soft_assert_eq_or_log!(
3793 mir_typ.scalar_type,
3794 (&return_styp).into(),
3795 "MIR type did not match the catalog type (cast elimination/repr type error)"
3796 );
3797 if !eval_result_datum.is_instance_of(&mir_typ) {
3801 panic!(
3802 "{call_name}: expected return type of {return_styp:?}, got {eval_result_datum}"
3803 );
3804 }
3805 if let Some(row) = uneliminated_result_row {
3807 let uneliminated_result_datum = row.unpack_first();
3808 assert_eq!(
3809 uneliminated_result_datum, eval_result_datum,
3810 "datums should not change if cast is eliminable"
3811 );
3812 }
3813 if let Some((introduces_nulls, propagates_nulls)) =
3816 call_introduces_propagates_nulls(&mir)
3817 {
3818 if introduces_nulls {
3819 assert!(
3823 mir_typ.nullable,
3824 "fn named `{}` called on args `{:?}` (lowered to `{}`) yielded mir_typ.nullable: {}",
3825 name, args, mir, mir_typ.nullable
3826 );
3827 } else {
3828 let any_input_null = args.iter().any(|arg| arg.is_null());
3829 if !any_input_null {
3830 assert!(
3831 !mir_typ.nullable,
3832 "fn named `{}` called on args `{:?}` (lowered to `{}`) yielded mir_typ.nullable: {}",
3833 name, args, mir, mir_typ.nullable
3834 );
3835 } else if propagates_nulls {
3836 assert!(
3839 mir_typ.nullable,
3840 "fn named `{}` called on args `{:?}` (lowered to `{}`) yielded mir_typ.nullable: {}",
3841 name, args, mir, mir_typ.nullable
3842 );
3843 }
3844 }
3849 }
3850 let mut reduced = mir.clone();
3853 reduced.reduce(&[]);
3854 match reduced {
3855 MirScalarExpr::Literal(reduce_result, ctyp) => {
3856 match reduce_result {
3857 Ok(reduce_result_row) => {
3858 let reduce_result_datum = reduce_result_row.unpack_first();
3859 assert_eq!(
3860 reduce_result_datum,
3861 eval_result_datum,
3862 "eval/reduce datum mismatch: fn named `{}` called on args `{:?}` (lowered to `{}`) evaluated to `{}` with typ `{:?}`, but reduced to `{}` with typ `{:?}`",
3863 name,
3864 args,
3865 mir,
3866 eval_result_datum,
3867 mir_typ.scalar_type,
3868 reduce_result_datum,
3869 ctyp.scalar_type
3870 );
3871 assert_eq!(
3877 ctyp.scalar_type,
3878 mir_typ.scalar_type,
3879 "eval/reduce type mismatch: fn named `{}` called on args `{:?}` (lowered to `{}`) evaluated to `{}` with typ `{:?}`, but reduced to `{}` with typ `{:?}`",
3880 name,
3881 args,
3882 mir,
3883 eval_result_datum,
3884 mir_typ.scalar_type,
3885 reduce_result_datum,
3886 ctyp.scalar_type
3887 );
3888 }
3889 Err(..) => {} }
3891 }
3892 _ => unreachable!(
3893 "all args are literals, so should have reduced to a literal"
3894 ),
3895 }
3896 }
3897 }
3898 }
3899 }
3900 }
3901
3902 fn call_introduces_propagates_nulls(mir_func_call: &MirScalarExpr) -> Option<(bool, bool)> {
3907 match mir_func_call {
3908 MirScalarExpr::CallUnary { func, expr } => {
3909 if expr.is_literal() {
3910 Some((func.introduces_nulls(), func.propagates_nulls()))
3911 } else {
3912 None
3913 }
3914 }
3915 MirScalarExpr::CallBinary { func, expr1, expr2 } => {
3916 if expr1.is_literal() && expr2.is_literal() {
3917 Some((func.introduces_nulls(), func.propagates_nulls()))
3918 } else {
3919 None
3920 }
3921 }
3922 MirScalarExpr::CallVariadic { func, exprs } => {
3923 if exprs.iter().all(|arg| arg.is_literal()) {
3924 Some((func.introduces_nulls(), func.propagates_nulls()))
3925 } else {
3926 None
3927 }
3928 }
3929 _ => None,
3930 }
3931 }
3932
3933 #[mz_ore::test(tokio::test)]
3935 #[cfg_attr(miri, ignore)] async fn test_pg_views_forbidden_types() {
3937 Catalog::with_debug(|catalog| async move {
3938 let conn_catalog = catalog.for_system_session();
3939
3940 for view in BUILTINS::views().filter(|view| {
3941 view.schema == PG_CATALOG_SCHEMA || view.schema == INFORMATION_SCHEMA
3942 }) {
3943 let item = conn_catalog
3944 .resolve_item(&PartialItemName {
3945 database: None,
3946 schema: Some(view.schema.to_string()),
3947 item: view.name.to_string(),
3948 })
3949 .expect("unable to resolve view")
3950 .at_version(RelationVersionSelector::Latest);
3952 let full_name = conn_catalog.resolve_full_name(item.name());
3953 let desc = item.relation_desc().expect("invalid item type");
3954 for col_type in desc.iter_types() {
3955 match &col_type.scalar_type {
3956 typ @ SqlScalarType::UInt16
3957 | typ @ SqlScalarType::UInt32
3958 | typ @ SqlScalarType::UInt64
3959 | typ @ SqlScalarType::MzTimestamp
3960 | typ @ SqlScalarType::List { .. }
3961 | typ @ SqlScalarType::Map { .. }
3962 | typ @ SqlScalarType::MzAclItem => {
3963 panic!("{typ:?} type found in {full_name}");
3964 }
3965 SqlScalarType::AclItem
3966 | SqlScalarType::Bool
3967 | SqlScalarType::Int16
3968 | SqlScalarType::Int32
3969 | SqlScalarType::Int64
3970 | SqlScalarType::Float32
3971 | SqlScalarType::Float64
3972 | SqlScalarType::Numeric { .. }
3973 | SqlScalarType::Date
3974 | SqlScalarType::Time
3975 | SqlScalarType::Timestamp { .. }
3976 | SqlScalarType::TimestampTz { .. }
3977 | SqlScalarType::Interval
3978 | SqlScalarType::PgLegacyChar
3979 | SqlScalarType::Bytes
3980 | SqlScalarType::String
3981 | SqlScalarType::Char { .. }
3982 | SqlScalarType::VarChar { .. }
3983 | SqlScalarType::Jsonb
3984 | SqlScalarType::Uuid
3985 | SqlScalarType::Array(_)
3986 | SqlScalarType::Record { .. }
3987 | SqlScalarType::Oid
3988 | SqlScalarType::RegProc
3989 | SqlScalarType::RegType
3990 | SqlScalarType::RegClass
3991 | SqlScalarType::Int2Vector
3992 | SqlScalarType::Range { .. }
3993 | SqlScalarType::PgLegacyName => {}
3994 }
3995 }
3996 }
3997 catalog.expire().await;
3998 })
3999 .await
4000 }
4001
4002 #[mz_ore::test(tokio::test)]
4005 #[cfg_attr(miri, ignore)] async fn test_mz_introspection_builtins() {
4007 Catalog::with_debug(|catalog| async move {
4008 let conn_catalog = catalog.for_system_session();
4009
4010 let introspection_schema_id = catalog.get_mz_introspection_schema_id();
4011 let introspection_schema_spec = SchemaSpecifier::Id(introspection_schema_id);
4012
4013 for entry in catalog.entries() {
4014 let schema_spec = entry.name().qualifiers.schema_spec;
4015 let introspection_deps = catalog.introspection_dependencies(entry.id);
4016 if introspection_deps.is_empty() {
4017 assert!(
4018 schema_spec != introspection_schema_spec,
4019 "entry does not depend on introspection sources but is in \
4020 `mz_introspection`: {}",
4021 conn_catalog.resolve_full_name(entry.name()),
4022 );
4023 } else {
4024 assert!(
4025 schema_spec == introspection_schema_spec,
4026 "entry depends on introspection sources but is not in \
4027 `mz_introspection`: {}",
4028 conn_catalog.resolve_full_name(entry.name()),
4029 );
4030 }
4031 }
4032 })
4033 .await
4034 }
4035
4036 #[mz_ore::test(tokio::test)]
4037 #[cfg_attr(miri, ignore)] async fn test_multi_subscriber_catalog() {
4039 let persist_client = PersistClient::new_for_tests().await;
4040 let bootstrap_args = test_bootstrap_args();
4041 let organization_id = Uuid::new_v4();
4042 let db_name = "DB";
4043
4044 let mut writer_catalog = Catalog::open_debug_catalog(
4045 persist_client.clone(),
4046 organization_id.clone(),
4047 &bootstrap_args,
4048 )
4049 .await
4050 .expect("open_debug_catalog");
4051 let mut read_only_catalog = Catalog::open_debug_read_only_catalog(
4052 persist_client.clone(),
4053 organization_id.clone(),
4054 &bootstrap_args,
4055 )
4056 .await
4057 .expect("open_debug_read_only_catalog");
4058 assert_err!(writer_catalog.resolve_database(db_name));
4059 assert_err!(read_only_catalog.resolve_database(db_name));
4060
4061 let commit_ts = writer_catalog.current_upper().await;
4062 writer_catalog
4063 .transact(
4064 None,
4065 commit_ts,
4066 None,
4067 vec![Op::CreateDatabase {
4068 name: db_name.to_string(),
4069 owner_id: MZ_SYSTEM_ROLE_ID,
4070 }],
4071 )
4072 .await
4073 .expect("failed to transact");
4074
4075 let write_db = writer_catalog
4076 .resolve_database(db_name)
4077 .expect("resolve_database");
4078 read_only_catalog
4079 .sync_to_current_updates()
4080 .await
4081 .expect("sync_to_current_updates");
4082 let read_db = read_only_catalog
4083 .resolve_database(db_name)
4084 .expect("resolve_database");
4085
4086 assert_eq!(write_db, read_db);
4087
4088 let writer_catalog_fencer =
4089 Catalog::open_debug_catalog(persist_client, organization_id, &bootstrap_args)
4090 .await
4091 .expect("open_debug_catalog for fencer");
4092 let fencer_db = writer_catalog_fencer
4093 .resolve_database(db_name)
4094 .expect("resolve_database for fencer");
4095 assert_eq!(fencer_db, read_db);
4096
4097 let write_fence_err = writer_catalog
4098 .sync_to_current_updates()
4099 .await
4100 .expect_err("sync_to_current_updates for fencer");
4101 assert!(matches!(
4102 write_fence_err,
4103 CatalogError::Durable(DurableCatalogError::Fence(FenceError::Epoch { .. }))
4104 ));
4105 let read_fence_err = read_only_catalog
4106 .sync_to_current_updates()
4107 .await
4108 .expect_err("sync_to_current_updates after fencer");
4109 assert!(matches!(
4110 read_fence_err,
4111 CatalogError::Durable(DurableCatalogError::Fence(FenceError::Epoch { .. }))
4112 ));
4113
4114 writer_catalog.expire().await;
4115 read_only_catalog.expire().await;
4116 writer_catalog_fencer.expire().await;
4117 }
4118}