1use std::any::Any;
16use std::collections::hash_map::Entry;
17use std::collections::{HashMap, HashSet};
18use std::sync::{Arc, RwLock, Weak};
19
20use async_stream::{stream, try_stream};
21use common_catalog::build_db_string;
22use common_catalog::consts::{
23 DEFAULT_CATALOG_NAME, DEFAULT_PRIVATE_SCHEMA_NAME, DEFAULT_SCHEMA_NAME,
24 INFORMATION_SCHEMA_NAME, PG_CATALOG_NAME,
25};
26use common_meta::key::flow::FlowMetadataManager;
27use common_meta::kv_backend::memory::MemoryKvBackend;
28use futures_util::stream::BoxStream;
29use session::context::QueryContext;
30use snafu::OptionExt;
31use table::TableRef;
32use table::metadata::{TableId, TableInfoRef};
33
34use crate::error::{CatalogNotFoundSnafu, Result, SchemaNotFoundSnafu, TableExistsSnafu};
35use crate::information_schema::InformationSchemaProvider;
36#[cfg(any(test, feature = "testing"))]
37use crate::process_manager::ProcessManagerRef;
38use crate::system_schema::SystemSchemaProvider;
39use crate::{CatalogManager, DeregisterTableRequest, RegisterSchemaRequest, RegisterTableRequest};
40
41type SchemaEntries = HashMap<String, HashMap<String, TableRef>>;
42
43#[derive(Clone)]
45pub struct MemoryCatalogManager {
46 catalogs: Arc<RwLock<HashMap<String, SchemaEntries>>>,
48}
49
50#[async_trait::async_trait]
51impl CatalogManager for MemoryCatalogManager {
52 fn as_any(&self) -> &dyn Any {
53 self
54 }
55
56 async fn catalog_names(&self) -> Result<Vec<String>> {
57 Ok(self.catalogs.read().unwrap().keys().cloned().collect())
58 }
59
60 async fn schema_names(
61 &self,
62 catalog: &str,
63 _query_ctx: Option<&QueryContext>,
64 ) -> Result<Vec<String>> {
65 Ok(self
66 .catalogs
67 .read()
68 .unwrap()
69 .get(catalog)
70 .with_context(|| CatalogNotFoundSnafu {
71 catalog_name: catalog,
72 })?
73 .keys()
74 .cloned()
75 .collect())
76 }
77
78 async fn table_names(
79 &self,
80 catalog: &str,
81 schema: &str,
82 _query_ctx: Option<&QueryContext>,
83 ) -> Result<Vec<String>> {
84 Ok(self
85 .catalogs
86 .read()
87 .unwrap()
88 .get(catalog)
89 .with_context(|| CatalogNotFoundSnafu {
90 catalog_name: catalog,
91 })?
92 .get(schema)
93 .with_context(|| SchemaNotFoundSnafu { catalog, schema })?
94 .keys()
95 .cloned()
96 .collect())
97 }
98
99 async fn catalog_exists(&self, catalog: &str) -> Result<bool> {
100 self.catalog_exist_sync(catalog)
101 }
102
103 async fn schema_exists(
104 &self,
105 catalog: &str,
106 schema: &str,
107 _query_ctx: Option<&QueryContext>,
108 ) -> Result<bool> {
109 self.schema_exist_sync(catalog, schema)
110 }
111
112 async fn table_exists(
113 &self,
114 catalog: &str,
115 schema: &str,
116 table: &str,
117 _query_ctx: Option<&QueryContext>,
118 ) -> Result<bool> {
119 let catalogs = self.catalogs.read().unwrap();
120 Ok(catalogs
121 .get(catalog)
122 .with_context(|| CatalogNotFoundSnafu {
123 catalog_name: catalog,
124 })?
125 .get(schema)
126 .with_context(|| SchemaNotFoundSnafu { catalog, schema })?
127 .contains_key(table))
128 }
129
130 async fn table(
131 &self,
132 catalog: &str,
133 schema: &str,
134 table_name: &str,
135 _query_ctx: Option<&QueryContext>,
136 ) -> Result<Option<TableRef>> {
137 let catalogs = self.catalogs.read().unwrap();
138 let result = catalogs
139 .get(catalog)
140 .and_then(|c| c.get(schema))
141 .and_then(|s| s.get(table_name))
142 .cloned();
143
144 Ok(result)
145 }
146
147 async fn table_info_by_id(&self, table_id: TableId) -> Result<Option<TableInfoRef>> {
148 Ok(self
149 .catalogs
150 .read()
151 .unwrap()
152 .values()
153 .flat_map(|schema_entries| schema_entries.values())
154 .flat_map(|tables| tables.values())
155 .find(|t| t.table_info().ident.table_id == table_id)
156 .map(|t| t.table_info()))
157 }
158
159 async fn tables_by_ids(
160 &self,
161 catalog: &str,
162 schema: &str,
163 table_ids: &[TableId],
164 ) -> Result<Vec<TableRef>> {
165 let catalogs = self.catalogs.read().unwrap();
166
167 let schemas = catalogs.get(catalog).context(CatalogNotFoundSnafu {
168 catalog_name: catalog,
169 })?;
170
171 let tables = schemas
172 .get(schema)
173 .context(SchemaNotFoundSnafu { catalog, schema })?;
174
175 let filter_ids: HashSet<_> = table_ids.iter().collect();
176 let tables = tables
178 .values()
179 .filter(|t| filter_ids.contains(&t.table_info().table_id()))
180 .cloned()
181 .collect::<Vec<_>>();
182
183 Ok(tables)
184 }
185
186 fn tables<'a>(
187 &'a self,
188 catalog: &'a str,
189 schema: &'a str,
190 _query_ctx: Option<&QueryContext>,
191 ) -> BoxStream<'a, Result<TableRef>> {
192 let catalogs = self.catalogs.read().unwrap();
193
194 let Some(schemas) = catalogs.get(catalog) else {
195 return Box::pin(stream!({
196 yield CatalogNotFoundSnafu {
197 catalog_name: catalog,
198 }
199 .fail();
200 }));
201 };
202
203 let Some(tables) = schemas.get(schema) else {
204 return Box::pin(stream!({
205 yield SchemaNotFoundSnafu { catalog, schema }.fail();
206 }));
207 };
208
209 let tables = tables.values().cloned().collect::<Vec<_>>();
210
211 Box::pin(try_stream!({
212 for table in tables {
213 yield table;
214 }
215 }))
216 }
217}
218
219impl MemoryCatalogManager {
220 pub fn new() -> Arc<Self> {
221 Arc::new(Self {
222 catalogs: Default::default(),
223 })
224 }
225
226 pub fn with_default_setup() -> Arc<Self> {
229 let manager = Arc::new(Self {
230 catalogs: Default::default(),
231 });
232
233 manager.register_catalog_sync(DEFAULT_CATALOG_NAME).unwrap();
235 manager
236 .register_schema_sync(RegisterSchemaRequest {
237 catalog: DEFAULT_CATALOG_NAME.to_string(),
238 schema: DEFAULT_SCHEMA_NAME.to_string(),
239 })
240 .unwrap();
241 manager
242 .register_schema_sync(RegisterSchemaRequest {
243 catalog: DEFAULT_CATALOG_NAME.to_string(),
244 schema: DEFAULT_PRIVATE_SCHEMA_NAME.to_string(),
245 })
246 .unwrap();
247 manager
248 .register_schema_sync(RegisterSchemaRequest {
249 catalog: DEFAULT_CATALOG_NAME.to_string(),
250 schema: PG_CATALOG_NAME.to_string(),
251 })
252 .unwrap();
253 manager
254 .register_schema_sync(RegisterSchemaRequest {
255 catalog: DEFAULT_CATALOG_NAME.to_string(),
256 schema: INFORMATION_SCHEMA_NAME.to_string(),
257 })
258 .unwrap();
259
260 manager
261 }
262
263 #[cfg(any(test, feature = "testing"))]
264 pub fn register_process_list_table(&self, process_manager: ProcessManagerRef) {
265 let backend = Arc::new(MemoryKvBackend::new());
266 let manager = Arc::new(self.clone());
267 let information_schema_provider = InformationSchemaProvider::new(
268 DEFAULT_CATALOG_NAME.to_string(),
269 Arc::downgrade(&manager) as Weak<dyn CatalogManager>,
270 Arc::new(FlowMetadataManager::new(backend.clone())),
271 Some(process_manager),
272 backend,
273 );
274 let process_list = information_schema_provider
275 .table(crate::information_schema::PROCESS_LIST)
276 .unwrap();
277 self.catalogs
278 .write()
279 .unwrap()
280 .get_mut(DEFAULT_CATALOG_NAME)
281 .unwrap()
282 .get_mut(INFORMATION_SCHEMA_NAME)
283 .unwrap()
284 .insert(
285 crate::information_schema::PROCESS_LIST.to_string(),
286 process_list,
287 );
288 }
289
290 fn schema_exist_sync(&self, catalog: &str, schema: &str) -> Result<bool> {
291 Ok(self
292 .catalogs
293 .read()
294 .unwrap()
295 .get(catalog)
296 .with_context(|| CatalogNotFoundSnafu {
297 catalog_name: catalog,
298 })?
299 .contains_key(schema))
300 }
301
302 fn catalog_exist_sync(&self, catalog: &str) -> Result<bool> {
303 Ok(self.catalogs.read().unwrap().contains_key(catalog))
304 }
305
306 pub fn register_catalog_sync(&self, name: &str) -> Result<bool> {
308 let name = name.to_string();
309
310 let mut catalogs = self.catalogs.write().unwrap();
311
312 match catalogs.entry(name.clone()) {
313 Entry::Vacant(e) => {
314 let arc_self = Arc::new(self.clone());
315 let catalog = arc_self.create_catalog_entry(name);
316 e.insert(catalog);
317 crate::metrics::METRIC_CATALOG_MANAGER_CATALOG_COUNT.inc();
318 Ok(true)
319 }
320 Entry::Occupied(_) => Ok(false),
321 }
322 }
323
324 pub fn deregister_table_sync(&self, request: DeregisterTableRequest) -> Result<()> {
325 let mut catalogs = self.catalogs.write().unwrap();
326 let schema = catalogs
327 .get_mut(&request.catalog)
328 .with_context(|| CatalogNotFoundSnafu {
329 catalog_name: &request.catalog,
330 })?
331 .get_mut(&request.schema)
332 .with_context(|| SchemaNotFoundSnafu {
333 catalog: &request.catalog,
334 schema: &request.schema,
335 })?;
336 let result = schema.remove(&request.table_name);
337 if result.is_some() {
338 crate::metrics::METRIC_CATALOG_MANAGER_TABLE_COUNT
339 .with_label_values(&[build_db_string(&request.catalog, &request.schema).as_str()])
340 .dec();
341 }
342 Ok(())
343 }
344
345 pub fn register_schema_sync(&self, request: RegisterSchemaRequest) -> Result<bool> {
349 let mut catalogs = self.catalogs.write().unwrap();
350 let catalog = catalogs
351 .get_mut(&request.catalog)
352 .with_context(|| CatalogNotFoundSnafu {
353 catalog_name: &request.catalog,
354 })?;
355
356 match catalog.entry(request.schema) {
357 Entry::Vacant(e) => {
358 e.insert(HashMap::new());
359 crate::metrics::METRIC_CATALOG_MANAGER_SCHEMA_COUNT.inc();
360 Ok(true)
361 }
362 Entry::Occupied(_) => Ok(false),
363 }
364 }
365
366 pub fn register_table_sync(&self, request: RegisterTableRequest) -> Result<bool> {
368 let mut catalogs = self.catalogs.write().unwrap();
369 let schema = catalogs
370 .get_mut(&request.catalog)
371 .with_context(|| CatalogNotFoundSnafu {
372 catalog_name: &request.catalog,
373 })?
374 .get_mut(&request.schema)
375 .with_context(|| SchemaNotFoundSnafu {
376 catalog: &request.catalog,
377 schema: &request.schema,
378 })?;
379
380 if schema.contains_key(&request.table_name) {
381 return TableExistsSnafu {
382 table: &request.table_name,
383 }
384 .fail();
385 }
386 schema.insert(request.table_name, request.table);
387 crate::metrics::METRIC_CATALOG_MANAGER_TABLE_COUNT
388 .with_label_values(&[build_db_string(&request.catalog, &request.schema).as_str()])
389 .inc();
390 Ok(true)
391 }
392
393 fn create_catalog_entry(self: &Arc<Self>, catalog: String) -> SchemaEntries {
394 let backend = Arc::new(MemoryKvBackend::new());
395 let information_schema_provider = InformationSchemaProvider::new(
396 catalog,
397 Arc::downgrade(self) as Weak<dyn CatalogManager>,
398 Arc::new(FlowMetadataManager::new(backend.clone())),
399 None, backend,
401 );
402 let information_schema = information_schema_provider.tables().clone();
403
404 let mut catalog = HashMap::new();
405 catalog.insert(INFORMATION_SCHEMA_NAME.to_string(), information_schema);
406 catalog
407 }
408
409 #[cfg(any(test, feature = "testing"))]
410 pub fn new_with_table(table: TableRef) -> Arc<Self> {
411 let manager = Self::with_default_setup();
412 let catalog = &table.table_info().catalog_name;
413 let schema = &table.table_info().schema_name;
414
415 if !manager.catalog_exist_sync(catalog).unwrap() {
416 manager.register_catalog_sync(catalog).unwrap();
417 }
418
419 if !manager.schema_exist_sync(catalog, schema).unwrap() {
420 manager
421 .register_schema_sync(RegisterSchemaRequest {
422 catalog: catalog.clone(),
423 schema: schema.clone(),
424 })
425 .unwrap();
426 }
427
428 let request = RegisterTableRequest {
429 catalog: catalog.clone(),
430 schema: schema.clone(),
431 table_name: table.table_info().name.clone(),
432 table_id: table.table_info().ident.table_id,
433 table,
434 };
435 let _ = manager.register_table_sync(request).unwrap();
436 manager
437 }
438}
439
440pub fn new_memory_catalog_manager() -> Result<Arc<MemoryCatalogManager>> {
442 Ok(MemoryCatalogManager::with_default_setup())
443}
444
445#[cfg(test)]
446mod tests {
447 use common_catalog::consts::*;
448 use futures_util::TryStreamExt;
449 use table::table::numbers::{NUMBERS_TABLE_NAME, NumbersTable};
450
451 use super::*;
452
453 #[tokio::test]
454 async fn test_new_memory_catalog_list() {
455 let catalog_list = new_memory_catalog_manager().unwrap();
456
457 let register_request = RegisterTableRequest {
458 catalog: DEFAULT_CATALOG_NAME.to_string(),
459 schema: DEFAULT_SCHEMA_NAME.to_string(),
460 table_name: NUMBERS_TABLE_NAME.to_string(),
461 table_id: NUMBERS_TABLE_ID,
462 table: NumbersTable::table(NUMBERS_TABLE_ID),
463 };
464
465 catalog_list.register_table_sync(register_request).unwrap();
466 let table = catalog_list
467 .table(
468 DEFAULT_CATALOG_NAME,
469 DEFAULT_SCHEMA_NAME,
470 NUMBERS_TABLE_NAME,
471 None,
472 )
473 .await
474 .unwrap()
475 .unwrap();
476 let stream = catalog_list.tables(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, None);
477 let tables = stream.try_collect::<Vec<_>>().await.unwrap();
478 assert_eq!(tables.len(), 1);
479 assert_eq!(
480 table.table_info().table_id(),
481 tables[0].table_info().table_id()
482 );
483
484 assert!(
485 catalog_list
486 .table(
487 DEFAULT_CATALOG_NAME,
488 DEFAULT_SCHEMA_NAME,
489 "not_exists",
490 None
491 )
492 .await
493 .unwrap()
494 .is_none()
495 );
496 }
497
498 #[test]
499 pub fn test_register_catalog_sync() {
500 let list = MemoryCatalogManager::with_default_setup();
501 assert!(list.register_catalog_sync("test_catalog").unwrap());
502 assert!(!list.register_catalog_sync("test_catalog").unwrap());
503 }
504
505 #[tokio::test]
506 pub async fn test_catalog_deregister_table() {
507 let catalog = MemoryCatalogManager::with_default_setup();
508 let table_name = "foo_table";
509
510 let register_table_req = RegisterTableRequest {
511 catalog: DEFAULT_CATALOG_NAME.to_string(),
512 schema: DEFAULT_SCHEMA_NAME.to_string(),
513 table_name: table_name.to_string(),
514 table_id: 2333,
515 table: NumbersTable::table(2333),
516 };
517 catalog.register_table_sync(register_table_req).unwrap();
518 assert!(
519 catalog
520 .table(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, table_name, None)
521 .await
522 .unwrap()
523 .is_some()
524 );
525
526 let deregister_table_req = DeregisterTableRequest {
527 catalog: DEFAULT_CATALOG_NAME.to_string(),
528 schema: DEFAULT_SCHEMA_NAME.to_string(),
529 table_name: table_name.to_string(),
530 };
531 catalog.deregister_table_sync(deregister_table_req).unwrap();
532 assert!(
533 catalog
534 .table(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, table_name, None)
535 .await
536 .unwrap()
537 .is_none()
538 );
539 }
540}