1mod cluster_info;
16pub mod columns;
17pub mod flow_statistics;
18pub mod flows;
19mod information_memory_table;
20pub mod key_column_usage;
21mod partitions;
22mod procedure_info;
23pub mod process_list;
24#[cfg(feature = "enterprise")]
25mod recycle_bin;
26mod region_info;
27pub mod region_peers;
28mod region_statistics;
29pub mod schemata;
30mod ssts;
31pub mod statistics;
32mod table_constraints;
33mod table_names;
34mod table_semantics;
35pub mod tables;
36mod views;
37
38#[cfg(all(test, feature = "enterprise"))]
39mod recycle_bin_test;
40
41use std::collections::{HashMap, HashSet};
42use std::sync::{Arc, Weak};
43
44use common_catalog::consts::{self, DEFAULT_CATALOG_NAME, INFORMATION_SCHEMA_NAME};
45use common_error::ext::ErrorExt;
46use common_meta::cluster::NodeInfo;
47use common_meta::datanode::RegionStat;
48use common_meta::key::flow::FlowMetadataManager;
49use common_meta::key::flow::flow_state::FlowStat;
50use common_meta::kv_backend::KvBackendRef;
51use common_procedure::ProcedureInfo;
52use common_recordbatch::SendableRecordBatchStream;
53use datafusion::error::DataFusionError;
54use datafusion::logical_expr::LogicalPlan;
55use datafusion::physical_plan::ExecutionPlan;
56use datatypes::schema::SchemaRef;
57use lazy_static::lazy_static;
58use paste::paste;
59use process_list::InformationSchemaProcessList;
60use region_info::InformationSchemaRegionInfo;
61use store_api::metric_engine_consts::{
62 MEMTABLE_PARTITION_TREE_PRIMARY_KEY_ENCODING, PRIMARY_KEY_ENCODING,
63};
64use store_api::region_info::RegionInfoEntry;
65use store_api::sst_entry::{ManifestSstEntry, PuffinIndexMetaEntry, StorageSstEntry};
66use store_api::storage::ScanRequest;
67use table::TableRef;
68use table::requests::{SEMANTIC_TABLE_SCOPE, TableOptions};
69pub use table_names::*;
70use views::InformationSchemaViews;
71
72use self::columns::InformationSchemaColumns;
73use crate::CatalogManager;
74use crate::error::Error;
75use crate::process_manager::ProcessManagerRef;
76use crate::system_schema::information_schema::cluster_info::InformationSchemaClusterInfo;
77use crate::system_schema::information_schema::flow_statistics::InformationSchemaFlowStatistics;
78use crate::system_schema::information_schema::flows::InformationSchemaFlows;
79use crate::system_schema::information_schema::information_memory_table::get_schema_columns;
80use crate::system_schema::information_schema::key_column_usage::InformationSchemaKeyColumnUsage;
81use crate::system_schema::information_schema::partitions::InformationSchemaPartitions;
82#[cfg(feature = "enterprise")]
83use crate::system_schema::information_schema::recycle_bin::InformationSchemaRecycleBin;
84use crate::system_schema::information_schema::region_peers::InformationSchemaRegionPeers;
85use crate::system_schema::information_schema::schemata::InformationSchemaSchemata;
86use crate::system_schema::information_schema::ssts::{
87 InformationSchemaSstsIndexMeta, InformationSchemaSstsManifest, InformationSchemaSstsStorage,
88};
89use crate::system_schema::information_schema::statistics::InformationSchemaStatistics;
90use crate::system_schema::information_schema::table_constraints::InformationSchemaTableConstraints;
91use crate::system_schema::information_schema::table_semantics::InformationSchemaTableSemantics;
92use crate::system_schema::information_schema::tables::InformationSchemaTables;
93use crate::system_schema::memory_table::MemoryTable;
94pub(crate) use crate::system_schema::predicate::Predicates;
95use crate::system_schema::{SystemSchemaProvider, SystemTable, build_system_table};
96
97const DENSE_PRIMARY_KEY_ENCODING: &str = "dense";
98const SPARSE_PRIMARY_KEY_ENCODING: &str = "sparse";
99
100pub(crate) fn primary_key_encoding_index_type(options: &HashMap<String, String>) -> &'static str {
101 options
102 .get(PRIMARY_KEY_ENCODING)
103 .or_else(|| options.get(MEMTABLE_PARTITION_TREE_PRIMARY_KEY_ENCODING))
104 .map(|value| {
105 if value.eq_ignore_ascii_case(SPARSE_PRIMARY_KEY_ENCODING) {
106 SPARSE_PRIMARY_KEY_ENCODING
107 } else {
108 DENSE_PRIMARY_KEY_ENCODING
109 }
110 })
111 .unwrap_or(DENSE_PRIMARY_KEY_ENCODING)
112}
113
114lazy_static! {
115 static ref MEMORY_TABLES: &'static [&'static str] = &[
117 ENGINES,
118 COLUMN_PRIVILEGES,
119 COLUMN_STATISTICS,
120 CHARACTER_SETS,
121 COLLATIONS,
122 COLLATION_CHARACTER_SET_APPLICABILITY,
123 CHECK_CONSTRAINTS,
124 EVENTS,
125 FILES,
126 OPTIMIZER_TRACE,
127 PARAMETERS,
128 PROFILING,
129 REFERENTIAL_CONSTRAINTS,
130 ROUTINES,
131 SCHEMA_PRIVILEGES,
132 TABLE_PRIVILEGES,
133 GLOBAL_STATUS,
134 SESSION_STATUS,
135 PLUGINS,
136 USER_PRIVILEGES,
137 PROCESSLIST,
138 ];
139}
140
141macro_rules! setup_memory_table {
142 ($name: expr) => {
143 paste! {
144 {
145 let (schema, columns) = get_schema_columns($name);
146 Some(Arc::new(MemoryTable::new(
147 consts::[<INFORMATION_SCHEMA_ $name _TABLE_ID>],
148 $name,
149 schema,
150 columns
151 )) as _)
152 }
153 }
154 };
155}
156
157pub struct MakeInformationTableRequest {
158 pub catalog_name: String,
159 pub catalog_manager: Weak<dyn CatalogManager>,
160 pub kv_backend: KvBackendRef,
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum InformationSchemaTableScope {
166 Catalog,
168 Global,
170}
171
172impl InformationSchemaTableScope {
173 pub const fn as_str(self) -> &'static str {
174 match self {
175 Self::Catalog => "catalog",
176 Self::Global => "global",
177 }
178 }
179}
180
181pub trait InformationSchemaTableFactory {
186 fn make_information_table(&self, req: MakeInformationTableRequest) -> InformationTableRef;
188}
189
190pub type InformationSchemaTableFactoryRef = Arc<dyn InformationSchemaTableFactory + Send + Sync>;
191
192pub struct InformationSchemaProvider {
194 catalog_name: String,
195 catalog_manager: Weak<dyn CatalogManager>,
196 process_manager: Option<ProcessManagerRef>,
197 flow_metadata_manager: Arc<FlowMetadataManager>,
198 tables: HashMap<String, TableRef>,
199 kv_backend: KvBackendRef,
200 extra_table_factories: HashMap<String, InformationSchemaTableFactoryRef>,
201}
202
203impl SystemSchemaProvider for InformationSchemaProvider {
204 fn tables(&self) -> &HashMap<String, TableRef> {
205 assert!(!self.tables.is_empty());
206
207 &self.tables
208 }
209}
210
211impl InformationSchemaProvider {
212 fn information_table(&self, name: &str) -> Option<InformationTableRef> {
213 if let Some(factory) = self.extra_table_factories.get(name) {
214 let req = MakeInformationTableRequest {
215 catalog_name: self.catalog_name.clone(),
216 catalog_manager: self.catalog_manager.clone(),
217 kv_backend: self.kv_backend.clone(),
218 };
219 return Some(factory.make_information_table(req));
220 }
221
222 match name.to_ascii_lowercase().as_str() {
223 TABLES => Some(Arc::new(InformationSchemaTables::new(
224 self.catalog_name.clone(),
225 self.catalog_manager.clone(),
226 )) as _),
227 COLUMNS => Some(Arc::new(InformationSchemaColumns::new(
228 self.catalog_name.clone(),
229 self.catalog_manager.clone(),
230 )) as _),
231 ENGINES => setup_memory_table!(ENGINES),
232 COLUMN_PRIVILEGES => setup_memory_table!(COLUMN_PRIVILEGES),
233 COLUMN_STATISTICS => setup_memory_table!(COLUMN_STATISTICS),
234 BUILD_INFO => setup_memory_table!(BUILD_INFO),
235 CHARACTER_SETS => setup_memory_table!(CHARACTER_SETS),
236 COLLATIONS => setup_memory_table!(COLLATIONS),
237 COLLATION_CHARACTER_SET_APPLICABILITY => {
238 setup_memory_table!(COLLATION_CHARACTER_SET_APPLICABILITY)
239 }
240 CHECK_CONSTRAINTS => setup_memory_table!(CHECK_CONSTRAINTS),
241 EVENTS => setup_memory_table!(EVENTS),
242 FILES => setup_memory_table!(FILES),
243 OPTIMIZER_TRACE => setup_memory_table!(OPTIMIZER_TRACE),
244 PARAMETERS => setup_memory_table!(PARAMETERS),
245 PROFILING => setup_memory_table!(PROFILING),
246 REFERENTIAL_CONSTRAINTS => setup_memory_table!(REFERENTIAL_CONSTRAINTS),
247 ROUTINES => setup_memory_table!(ROUTINES),
248 SCHEMA_PRIVILEGES => setup_memory_table!(SCHEMA_PRIVILEGES),
249 TABLE_PRIVILEGES => setup_memory_table!(TABLE_PRIVILEGES),
250 GLOBAL_STATUS => setup_memory_table!(GLOBAL_STATUS),
251 SESSION_STATUS => setup_memory_table!(SESSION_STATUS),
252 PLUGINS => setup_memory_table!(PLUGINS),
253 USER_PRIVILEGES => setup_memory_table!(USER_PRIVILEGES),
254 PROCESSLIST => setup_memory_table!(PROCESSLIST),
255 KEY_COLUMN_USAGE => Some(Arc::new(InformationSchemaKeyColumnUsage::new(
256 self.catalog_name.clone(),
257 self.catalog_manager.clone(),
258 )) as _),
259 SCHEMATA => Some(Arc::new(InformationSchemaSchemata::new(
260 self.catalog_name.clone(),
261 self.catalog_manager.clone(),
262 )) as _),
263 PARTITIONS => Some(Arc::new(InformationSchemaPartitions::new(
264 self.catalog_name.clone(),
265 self.catalog_manager.clone(),
266 )) as _),
267 REGION_PEERS => Some(Arc::new(InformationSchemaRegionPeers::new(
268 self.catalog_name.clone(),
269 self.catalog_manager.clone(),
270 )) as _),
271 TABLE_CONSTRAINTS => Some(Arc::new(InformationSchemaTableConstraints::new(
272 self.catalog_name.clone(),
273 self.catalog_manager.clone(),
274 )) as _),
275 STATISTICS => Some(Arc::new(InformationSchemaStatistics::new(
276 self.catalog_name.clone(),
277 self.catalog_manager.clone(),
278 )) as _),
279 CLUSTER_INFO => Some(Arc::new(InformationSchemaClusterInfo::new(
280 self.catalog_manager.clone(),
281 )) as _),
282 VIEWS => Some(Arc::new(InformationSchemaViews::new(
283 self.catalog_name.clone(),
284 self.catalog_manager.clone(),
285 )) as _),
286 FLOWS => Some(Arc::new(InformationSchemaFlows::new(
287 self.catalog_name.clone(),
288 self.catalog_manager.clone(),
289 self.flow_metadata_manager.clone(),
290 )) as _),
291 FLOW_STATISTICS => Some(Arc::new(InformationSchemaFlowStatistics::new(
292 self.catalog_name.clone(),
293 self.catalog_manager.clone(),
294 self.flow_metadata_manager.clone(),
295 )) as _),
296 PROCEDURE_INFO => Some(
297 Arc::new(procedure_info::InformationSchemaProcedureInfo::new(
298 self.catalog_manager.clone(),
299 )) as _,
300 ),
301 #[cfg(feature = "enterprise")]
302 RECYCLE_BIN => Some(Arc::new(InformationSchemaRecycleBin::new(
303 self.catalog_name.clone(),
304 self.catalog_manager.clone(),
305 )) as _),
306 REGION_STATISTICS => Some(Arc::new(
307 region_statistics::InformationSchemaRegionStatistics::new(
308 self.catalog_manager.clone(),
309 ),
310 ) as _),
311 REGION_INFO => Some(Arc::new(InformationSchemaRegionInfo::new(
312 self.catalog_manager.clone(),
313 )) as _),
314 PROCESS_LIST => self
315 .process_manager
316 .as_ref()
317 .map(|p| Arc::new(InformationSchemaProcessList::new(p.clone())) as _),
318 SSTS_MANIFEST => Some(Arc::new(InformationSchemaSstsManifest::new(
319 self.catalog_manager.clone(),
320 )) as _),
321 SSTS_STORAGE => Some(Arc::new(InformationSchemaSstsStorage::new(
322 self.catalog_manager.clone(),
323 )) as _),
324 SSTS_INDEX_META => Some(Arc::new(InformationSchemaSstsIndexMeta::new(
325 self.catalog_manager.clone(),
326 )) as _),
327 TABLE_SEMANTICS => Some(Arc::new(InformationSchemaTableSemantics::new(
328 self.catalog_name.clone(),
329 self.catalog_manager.clone(),
330 )) as _),
331 _ => None,
332 }
333 }
334}
335
336impl InformationSchemaProvider {
337 pub fn new(
338 catalog_name: String,
339 catalog_manager: Weak<dyn CatalogManager>,
340 flow_metadata_manager: Arc<FlowMetadataManager>,
341 process_manager: Option<ProcessManagerRef>,
342 kv_backend: KvBackendRef,
343 ) -> Self {
344 let mut provider = Self {
345 catalog_name,
346 catalog_manager,
347 flow_metadata_manager,
348 process_manager,
349 tables: HashMap::new(),
350 kv_backend,
351 extra_table_factories: HashMap::new(),
352 };
353
354 provider.build_tables();
355
356 provider
357 }
358
359 pub(crate) fn with_extra_table_factories(
360 mut self,
361 factories: HashMap<String, InformationSchemaTableFactoryRef>,
362 ) -> Self {
363 self.extra_table_factories = factories;
364 self.build_tables();
365 self
366 }
367
368 fn build_tables(&mut self) {
369 let mut table_names = HashSet::new();
370
371 if self.catalog_name == DEFAULT_CATALOG_NAME {
376 table_names.extend([
377 BUILD_INFO,
378 REGION_PEERS,
379 CLUSTER_INFO,
380 PROCEDURE_INFO,
381 REGION_STATISTICS,
382 REGION_INFO,
383 SSTS_MANIFEST,
384 SSTS_STORAGE,
385 SSTS_INDEX_META,
386 ]);
387 }
388
389 table_names.extend([
390 TABLES,
391 VIEWS,
392 SCHEMATA,
393 COLUMNS,
394 KEY_COLUMN_USAGE,
395 TABLE_CONSTRAINTS,
396 STATISTICS,
397 FLOWS,
398 FLOW_STATISTICS,
399 #[cfg(feature = "enterprise")]
400 RECYCLE_BIN,
401 TABLE_SEMANTICS,
402 PARTITIONS,
403 ]);
404 if self.process_manager.is_some() {
405 table_names.insert(PROCESS_LIST);
406 }
407 table_names.extend(MEMORY_TABLES.iter().copied());
408 table_names.extend(self.extra_table_factories.keys().map(String::as_str));
409 self.tables = table_names
410 .into_iter()
411 .map(|name| {
412 let table = self.information_table(name).expect(name);
413 let options = TableOptions {
414 extra_options: HashMap::from([(
415 SEMANTIC_TABLE_SCOPE.to_string(),
416 table.scope().as_str().to_string(),
417 )]),
418 ..Default::default()
419 };
420 let table = build_system_table(
421 self.catalog_name.clone(),
422 INFORMATION_SCHEMA_NAME,
423 table,
424 options,
425 );
426 (name.to_string(), table)
427 })
428 .collect();
429 }
430}
431
432pub trait InformationTable: SystemTable {
434 fn scope(&self) -> InformationSchemaTableScope;
436}
437
438pub type InformationTableRef = Arc<dyn InformationTable + Send + Sync>;
439
440pub type InformationExtensionRef = Arc<dyn InformationExtension<Error = Error> + Send + Sync>;
441
442#[async_trait::async_trait]
444pub trait InformationExtension {
445 type Error: ErrorExt;
446
447 async fn nodes(&self) -> std::result::Result<Vec<NodeInfo>, Self::Error>;
449
450 async fn procedures(&self) -> std::result::Result<Vec<(String, ProcedureInfo)>, Self::Error>;
452
453 async fn region_stats(&self) -> std::result::Result<Vec<RegionStat>, Self::Error>;
455
456 async fn flow_stats(&self) -> std::result::Result<Option<FlowStat>, Self::Error>;
458
459 async fn inspect_datanode(
461 &self,
462 request: DatanodeInspectRequest,
463 ) -> std::result::Result<SendableRecordBatchStream, Self::Error>;
464
465 fn inspect_datanode_plan(
468 &self,
469 _request: DatanodeInspectRequest,
470 _schema: SchemaRef,
471 ) -> std::result::Result<Option<Arc<dyn ExecutionPlan>>, Self::Error> {
472 Ok(None)
473 }
474}
475
476#[derive(Debug, Clone, PartialEq)]
478pub struct DatanodeInspectRequest {
479 pub kind: DatanodeInspectKind,
481
482 pub scan: ScanRequest,
485}
486
487#[derive(Debug, Clone, Copy, PartialEq, Eq)]
489pub enum DatanodeInspectKind {
490 SstManifest,
492 SstStorage,
494 SstIndexMeta,
496 RegionInfo,
498}
499
500impl DatanodeInspectRequest {
501 pub fn build_plan(self) -> std::result::Result<LogicalPlan, DataFusionError> {
503 match self.kind {
504 DatanodeInspectKind::SstManifest => ManifestSstEntry::build_plan(self.scan),
505 DatanodeInspectKind::SstStorage => StorageSstEntry::build_plan(self.scan),
506 DatanodeInspectKind::SstIndexMeta => PuffinIndexMetaEntry::build_plan(self.scan),
507 DatanodeInspectKind::RegionInfo => RegionInfoEntry::build_plan(self.scan),
508 }
509 }
510}
511pub struct NoopInformationExtension;
512
513#[async_trait::async_trait]
514impl InformationExtension for NoopInformationExtension {
515 type Error = Error;
516
517 async fn nodes(&self) -> std::result::Result<Vec<NodeInfo>, Self::Error> {
518 Ok(vec![])
519 }
520
521 async fn procedures(&self) -> std::result::Result<Vec<(String, ProcedureInfo)>, Self::Error> {
522 Ok(vec![])
523 }
524
525 async fn region_stats(&self) -> std::result::Result<Vec<RegionStat>, Self::Error> {
526 Ok(vec![])
527 }
528
529 async fn flow_stats(&self) -> std::result::Result<Option<FlowStat>, Self::Error> {
530 Ok(None)
531 }
532
533 async fn inspect_datanode(
534 &self,
535 _request: DatanodeInspectRequest,
536 ) -> std::result::Result<SendableRecordBatchStream, Self::Error> {
537 Ok(common_recordbatch::RecordBatches::empty().as_stream())
538 }
539}
540
541#[cfg(test)]
542mod tests {
543 use cache::{build_fundamental_cache_registry, with_default_composite_cache_registry};
544 use common_meta::cache::{CacheRegistryBuilder, LayeredCacheRegistryBuilder};
545 use common_meta::ddl::test_util::create_table::test_create_table_task;
546 use common_meta::key::schema_name::SchemaNameKey;
547 use common_meta::key::table_route::TableRouteValue;
548 use common_meta::kv_backend::memory::MemoryKvBackend;
549 use common_meta::kv_backend::txn::TxnService;
550 use common_meta::wal_provider::RegionWalOptions;
551 use datatypes::schema::Schema;
552 use futures_util::TryStreamExt;
553 use store_api::region_info::RegionInfoEntry;
554 use store_api::storage::TableId;
555
556 use super::*;
557 use crate::error::Result;
558 use crate::information_schema::NoopInformationExtension;
559 use crate::kvbackend::KvBackendCatalogManagerBuilder;
560 use crate::memory::MemoryCatalogManager;
561 use crate::process_manager::ProcessManager;
562
563 struct ScopedFactory {
564 name: &'static str,
565 scope: InformationSchemaTableScope,
566 }
567
568 impl InformationSchemaTableFactory for ScopedFactory {
569 fn make_information_table(&self, _req: MakeInformationTableRequest) -> InformationTableRef {
570 Arc::new(ScopedTable {
571 name: self.name,
572 scope: self.scope,
573 })
574 }
575 }
576
577 struct ScopedTable {
578 name: &'static str,
579 scope: InformationSchemaTableScope,
580 }
581
582 impl InformationTable for ScopedTable {
583 fn scope(&self) -> InformationSchemaTableScope {
584 self.scope
585 }
586 }
587
588 impl SystemTable for ScopedTable {
589 fn table_id(&self) -> TableId {
590 999
591 }
592
593 fn table_name(&self) -> &'static str {
594 self.name
595 }
596
597 fn schema(&self) -> SchemaRef {
598 Arc::new(Schema::new(vec![]))
599 }
600
601 fn to_stream(&self, _request: ScanRequest) -> Result<SendableRecordBatchStream> {
602 Ok(common_recordbatch::RecordBatches::empty().as_stream())
603 }
604 }
605
606 #[test]
607 fn every_registered_information_schema_table_has_scope() {
608 use InformationSchemaTableScope::{Catalog, Global};
609
610 let manager: Arc<dyn CatalogManager> = MemoryCatalogManager::new();
611 let backend = Arc::new(MemoryKvBackend::default());
612 let factories: HashMap<_, InformationSchemaTableFactoryRef> = [
613 ("future_catalog_table", Catalog),
614 ("future_global_table", Global),
615 (ENGINES, Catalog),
616 ]
617 .into_iter()
618 .map(|(name, scope)| {
619 (
620 name.to_string(),
621 Arc::new(ScopedFactory { name, scope }) as _,
622 )
623 })
624 .collect();
625 let catalog_tables = [
626 TABLES,
627 VIEWS,
628 SCHEMATA,
629 COLUMNS,
630 KEY_COLUMN_USAGE,
631 TABLE_CONSTRAINTS,
632 STATISTICS,
633 FLOWS,
634 FLOW_STATISTICS,
635 TABLE_SEMANTICS,
636 PARTITIONS,
637 REGION_PEERS,
638 #[cfg(feature = "enterprise")]
639 RECYCLE_BIN,
640 "future_catalog_table",
641 ENGINES,
642 ];
643
644 for (catalog, with_process_manager) in [
645 (DEFAULT_CATALOG_NAME, true),
646 (DEFAULT_CATALOG_NAME, false),
647 ("tenant", true),
648 ("tenant", false),
649 ] {
650 let provider = InformationSchemaProvider::new(
651 catalog.to_string(),
652 Arc::downgrade(&manager),
653 Arc::new(FlowMetadataManager::new(backend.clone())),
654 with_process_manager.then(|| Arc::new(ProcessManager::new(String::new(), None))),
655 backend.clone(),
656 );
657 assert_eq!(
658 provider
659 .table(ENGINES)
660 .unwrap()
661 .table_info()
662 .meta
663 .options
664 .extra_options
665 .get(SEMANTIC_TABLE_SCOPE)
666 .map(String::as_str),
667 Some(Global.as_str()),
668 );
669 let provider = provider.with_extra_table_factories(factories.clone());
671 let expected_count =
672 43 + usize::from(with_process_manager) + usize::from(cfg!(feature = "enterprise"))
673 - if catalog == DEFAULT_CATALOG_NAME {
674 0
675 } else {
676 9
677 };
678 assert_eq!(provider.tables().len(), expected_count);
679 for (name, table) in provider.tables() {
680 let scope = if catalog_tables.contains(&name.as_str()) {
681 Catalog
682 } else {
683 Global
684 };
685 assert_eq!(
686 table
687 .table_info()
688 .meta
689 .options
690 .extra_options
691 .get(SEMANTIC_TABLE_SCOPE)
692 .map(String::as_str),
693 Some(scope.as_str()),
694 "{catalog}.{name}"
695 );
696 }
697 assert_eq!(
698 provider.table(REGION_PEERS).is_some(),
699 catalog == DEFAULT_CATALOG_NAME
700 );
701 assert_eq!(provider.table(PROCESS_LIST).is_some(), with_process_manager);
702 if let Some(table) = provider.table(PROCESS_LIST) {
703 assert!(table.schema().column_schema_by_name("catalog").is_some());
704 }
705 }
706 }
707
708 #[tokio::test]
709 async fn newly_accessible_information_schema_tables_filter_catalogs() {
710 let backend = Arc::new(MemoryKvBackend::default());
711 let caches = LayeredCacheRegistryBuilder::default()
712 .add_cache_registry(CacheRegistryBuilder::default().build())
713 .add_cache_registry(build_fundamental_cache_registry(backend.clone()));
714 let manager = KvBackendCatalogManagerBuilder::new(
715 Arc::new(NoopInformationExtension),
716 backend.clone(),
717 Arc::new(
718 with_default_composite_cache_registry(caches)
719 .unwrap()
720 .build(),
721 ),
722 )
723 .build();
724 let metadata = manager.table_metadata_manager_ref();
725 let flow_metadata = FlowMetadataManager::new(backend.clone());
726 for (catalog, name, id) in [
727 ("tenant_a", "visible_a", 1024),
728 ("tenant_b", "visible_b", 1025),
729 ] {
730 metadata
731 .schema_manager()
732 .create(SchemaNameKey::new(catalog, "public"), None, false)
733 .await
734 .unwrap();
735 let mut info = test_create_table_task(name, id).table_info;
736 info.catalog_name = catalog.to_string();
737 info.meta
738 .options
739 .extra_options
740 .insert(SEMANTIC_TABLE_SCOPE.to_string(), "catalog".to_string());
741 metadata
742 .create_table_metadata(
743 info,
744 TableRouteValue::physical(vec![]),
745 RegionWalOptions::default(),
746 )
747 .await
748 .unwrap();
749 let (txn, _) = flow_metadata
750 .flow_name_manager()
751 .build_create_txn(catalog, name, id)
752 .unwrap();
753 backend.txn(txn).await.unwrap();
754 }
755
756 for (catalog, visible, hidden) in [
757 ("tenant_a", "visible_a", "visible_b"),
758 ("tenant_b", "visible_b", "visible_a"),
759 ] {
760 for name in [TABLE_SEMANTICS, STATISTICS, FLOW_STATISTICS] {
761 let table = manager
762 .table(catalog, INFORMATION_SCHEMA_NAME, name, None)
763 .await
764 .unwrap()
765 .unwrap();
766 let batches = table
767 .scan_to_stream(ScanRequest::default())
768 .await
769 .unwrap()
770 .try_collect::<Vec<_>>()
771 .await
772 .unwrap();
773 let output = batches
774 .iter()
775 .map(|batch| batch.pretty_print())
776 .collect::<Vec<_>>()
777 .join("\n");
778 assert!(output.contains(visible), "{catalog}.{name}: {output}");
779 assert!(!output.contains(hidden), "{catalog}.{name}: {output}");
780 }
781 }
782 assert!(
783 !manager
784 .information_schema_provider()
785 .tables()
786 .contains_key("visible_a")
787 );
788 }
789
790 #[test]
791 fn test_datanode_inspect_region_info_build_plan() {
792 let plan = DatanodeInspectRequest {
793 kind: DatanodeInspectKind::RegionInfo,
794 scan: ScanRequest::default(),
795 }
796 .build_plan()
797 .unwrap();
798
799 let LogicalPlan::TableScan(scan) = plan else {
800 panic!("expected table scan");
801 };
802 assert_eq!(
803 scan.table_name.to_string(),
804 RegionInfoEntry::reserved_table_name_for_inspection()
805 );
806 }
807}