Skip to main content

catalog/kvbackend/
manager.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::any::Any;
16use std::collections::BTreeSet;
17use std::sync::{Arc, OnceLock, Weak};
18
19use async_stream::try_stream;
20use common_catalog::consts::{
21    DEFAULT_CATALOG_NAME, DEFAULT_PRIVATE_SCHEMA_NAME, DEFAULT_SCHEMA_NAME,
22    INFORMATION_SCHEMA_NAME, PG_CATALOG_NAME,
23};
24use common_error::ext::BoxedError;
25use common_meta::cache::{
26    LayeredCacheRegistryRef, TableInfoCacheRef, TableNameCacheRef, TableRoute, TableRouteCacheRef,
27    ViewInfoCacheRef,
28};
29use common_meta::key::TableMetadataManagerRef;
30use common_meta::key::catalog_name::CatalogNameKey;
31use common_meta::key::flow::FlowMetadataManager;
32use common_meta::key::schema_name::SchemaNameKey;
33use common_meta::key::table_info::TableInfoValue;
34use common_meta::key::table_name::TableNameKey;
35use common_meta::kv_backend::KvBackendRef;
36use common_procedure::ProcedureManagerRef;
37use futures_util::stream::BoxStream;
38use futures_util::{StreamExt, TryStreamExt};
39use moka::sync::Cache;
40use partition::manager::PartitionRuleManagerRef;
41use session::context::{Channel, QueryContext};
42use snafu::prelude::*;
43use store_api::metric_engine_consts::METRIC_ENGINE_NAME;
44use table::TableRef;
45use table::dist_table::DistTable;
46use table::metadata::{TableId, TableInfoRef};
47use table::table::PartitionRules;
48use table::table_name::TableName;
49use tokio::sync::Semaphore;
50use tokio_stream::wrappers::ReceiverStream;
51
52use crate::CatalogManager;
53use crate::error::{
54    CacheNotFoundSnafu, GetTableCacheSnafu, ListCatalogsSnafu, ListSchemasSnafu, ListTablesSnafu,
55    Result, TableMetadataManagerSnafu,
56};
57use crate::information_schema::{
58    InformationExtensionRef, InformationSchemaProvider, InformationSchemaTableFactoryRef,
59};
60use crate::kvbackend::TableCacheRef;
61use crate::process_manager::ProcessManagerRef;
62use crate::system_schema::SystemSchemaProvider;
63use crate::system_schema::numbers_table_provider::NumbersTableProvider;
64use crate::system_schema::pg_catalog::PGCatalogProvider;
65use crate::system_schema::semantic_graph::{EntityGraphProviderRef, SemanticGraphTableProvider};
66
67/// Access all existing catalog, schema and tables.
68///
69/// The result comes from two source, all the user tables are presented in
70/// a kv-backend which persists the metadata of a table. And system tables
71/// comes from `SystemCatalog`, which is static and read-only.
72#[derive(Clone)]
73pub struct KvBackendCatalogManager {
74    /// Provides the extension methods for the `information_schema` tables
75    pub(super) information_extension: InformationExtensionRef,
76    /// Backs the computed entity-graph tables (`semantic_entities` /
77    /// `semantic_relationships`). Set once, after the query engine is built, to
78    /// break the `catalog -> query` cycle; `None` until then. `Arc` so the cell
79    /// stays shared across `Clone`s of the manager (a plain `OnceLock` would
80    /// fork on clone).
81    pub(super) entity_graph_provider: Arc<OnceLock<EntityGraphProviderRef>>,
82    /// Manages partition rules.
83    pub(super) partition_manager: PartitionRuleManagerRef,
84    /// Manages table metadata.
85    pub(super) table_metadata_manager: TableMetadataManagerRef,
86    /// A sub-CatalogManager that handles system tables
87    pub(super) system_catalog: SystemCatalog,
88    /// Cache registry for all caches.
89    pub(super) cache_registry: LayeredCacheRegistryRef,
90    /// Only available in `Standalone` mode.
91    pub(super) procedure_manager: Option<ProcedureManagerRef>,
92}
93
94pub(super) const CATALOG_CACHE_MAX_CAPACITY: u64 = 128;
95
96impl KvBackendCatalogManager {
97    pub fn view_info_cache(&self) -> Result<ViewInfoCacheRef> {
98        self.cache_registry.get().context(CacheNotFoundSnafu {
99            name: "view_info_cache",
100        })
101    }
102
103    /// Returns the [`InformationExtension`].
104    pub fn information_extension(&self) -> InformationExtensionRef {
105        self.information_extension.clone()
106    }
107
108    /// Returns the entity-graph provider, or `None` if it has not been injected
109    /// yet (before the query engine is built). The computed graph tables stream
110    /// empty until it is set.
111    pub fn entity_graph_provider(&self) -> Option<EntityGraphProviderRef> {
112        self.entity_graph_provider.get().cloned()
113    }
114
115    /// Injects the entity-graph provider once the query engine exists. A second
116    /// call is a no-op (the first binding wins).
117    pub fn set_entity_graph_provider(&self, provider: EntityGraphProviderRef) {
118        let _ = self.entity_graph_provider.set(provider);
119    }
120
121    pub fn partition_manager(&self) -> PartitionRuleManagerRef {
122        self.partition_manager.clone()
123    }
124
125    pub fn table_metadata_manager_ref(&self) -> &TableMetadataManagerRef {
126        &self.table_metadata_manager
127    }
128
129    pub fn procedure_manager(&self) -> Option<ProcedureManagerRef> {
130        self.procedure_manager.clone()
131    }
132
133    // Override logical table's partition key indices with physical table's.
134    async fn override_logical_table_partition_key_indices(
135        table_route_cache: &TableRouteCacheRef,
136        table_info_cache: &TableInfoCacheRef,
137        table: TableRef,
138    ) -> Result<TableRef> {
139        // If the table is not a metric table, return the table directly.
140        if table.table_info().meta.engine != METRIC_ENGINE_NAME {
141            return Ok(table);
142        }
143
144        if let Some(table_route_value) = table_route_cache
145            .get(table.table_info().table_id())
146            .await
147            .context(TableMetadataManagerSnafu)?
148            && let TableRoute::Logical(logical_route) = &*table_route_value
149            && let Some(physical_table_info) = table_info_cache
150                .get(logical_route.physical_table_id())
151                .await
152                .context(TableMetadataManagerSnafu)?
153        {
154            let mut new_table_info = (*table.table_info()).clone();
155
156            let mut phy_part_cols_not_in_logical_table = vec![];
157
158            // Remap partition key indices from physical table to logical table
159            new_table_info.meta.partition_key_indices = physical_table_info
160                .meta
161                .partition_key_indices
162                .iter()
163                .filter_map(|&physical_index| {
164                    // Get the column name from the physical table using the physical index
165                    physical_table_info
166                        .meta
167                        .schema
168                        .column_schemas()
169                        .get(physical_index)
170                        .and_then(|physical_column| {
171                            // Find the corresponding index in the logical table schema
172                            let idx = new_table_info
173                                .meta
174                                .schema
175                                .column_index_by_name(physical_column.name.as_str());
176                            if idx.is_none() {
177                                // not all part columns in physical table that are also in logical table
178                                phy_part_cols_not_in_logical_table
179                                    .push(physical_column.name.clone());
180                            }
181
182                            idx
183                        })
184                })
185                .collect();
186
187            let partition_rules = if !phy_part_cols_not_in_logical_table.is_empty() {
188                Some(PartitionRules {
189                    extra_phy_cols_not_in_logical_table: phy_part_cols_not_in_logical_table,
190                })
191            } else {
192                None
193            };
194
195            let new_table = DistTable::table_partitioned(Arc::new(new_table_info), partition_rules);
196
197            return Ok(new_table);
198        }
199
200        Ok(table)
201    }
202}
203
204#[async_trait::async_trait]
205impl CatalogManager for KvBackendCatalogManager {
206    fn as_any(&self) -> &dyn Any {
207        self
208    }
209
210    async fn catalog_names(&self) -> Result<Vec<String>> {
211        let stream = self
212            .table_metadata_manager
213            .catalog_manager()
214            .catalog_names();
215
216        let keys = stream
217            .try_collect::<Vec<_>>()
218            .await
219            .map_err(BoxedError::new)
220            .context(ListCatalogsSnafu)?;
221
222        Ok(keys)
223    }
224
225    async fn schema_names(
226        &self,
227        catalog: &str,
228        query_ctx: Option<&QueryContext>,
229    ) -> Result<Vec<String>> {
230        let stream = self
231            .table_metadata_manager
232            .schema_manager()
233            .schema_names(catalog);
234        let mut keys = stream
235            .try_collect::<BTreeSet<_>>()
236            .await
237            .map_err(BoxedError::new)
238            .context(ListSchemasSnafu { catalog })?;
239
240        keys.extend(self.system_catalog.schema_names(query_ctx));
241
242        Ok(keys.into_iter().collect())
243    }
244
245    async fn table_names(
246        &self,
247        catalog: &str,
248        schema: &str,
249        query_ctx: Option<&QueryContext>,
250    ) -> Result<Vec<String>> {
251        let mut tables = self
252            .table_metadata_manager
253            .table_name_manager()
254            .tables(catalog, schema)
255            .map_ok(|(table_name, _)| table_name)
256            .try_collect::<Vec<_>>()
257            .await
258            .map_err(BoxedError::new)
259            .context(ListTablesSnafu { catalog, schema })?;
260
261        tables.extend(self.system_catalog.table_names(schema, query_ctx));
262        Ok(tables)
263    }
264
265    async fn catalog_exists(&self, catalog: &str) -> Result<bool> {
266        self.table_metadata_manager
267            .catalog_manager()
268            .exists(CatalogNameKey::new(catalog))
269            .await
270            .context(TableMetadataManagerSnafu)
271    }
272
273    async fn schema_exists(
274        &self,
275        catalog: &str,
276        schema: &str,
277        query_ctx: Option<&QueryContext>,
278    ) -> Result<bool> {
279        if self.system_catalog.schema_exists(schema, query_ctx) {
280            return Ok(true);
281        }
282
283        self.table_metadata_manager
284            .schema_manager()
285            .exists(SchemaNameKey::new(catalog, schema))
286            .await
287            .context(TableMetadataManagerSnafu)
288    }
289
290    async fn table_exists(
291        &self,
292        catalog: &str,
293        schema: &str,
294        table: &str,
295        query_ctx: Option<&QueryContext>,
296    ) -> Result<bool> {
297        if self.system_catalog.table_exists(schema, table, query_ctx) {
298            return Ok(true);
299        }
300
301        let key = TableNameKey::new(catalog, schema, table);
302        self.table_metadata_manager
303            .table_name_manager()
304            .get(key)
305            .await
306            .context(TableMetadataManagerSnafu)
307            .map(|x| x.is_some())
308    }
309
310    async fn table(
311        &self,
312        catalog_name: &str,
313        schema_name: &str,
314        table_name: &str,
315        query_ctx: Option<&QueryContext>,
316    ) -> Result<Option<TableRef>> {
317        let channel = query_ctx.map_or(Channel::Unknown, |ctx| ctx.channel());
318        if let Some(table) =
319            self.system_catalog
320                .table(catalog_name, schema_name, table_name, query_ctx)
321        {
322            return Ok(Some(table));
323        }
324
325        let table_cache: TableCacheRef = self.cache_registry.get().context(CacheNotFoundSnafu {
326            name: "table_cache",
327        })?;
328
329        let table = table_cache
330            .get_by_ref(&TableName {
331                catalog_name: catalog_name.to_string(),
332                schema_name: schema_name.to_string(),
333                table_name: table_name.to_string(),
334            })
335            .await
336            .context(GetTableCacheSnafu)?;
337
338        if let Some(table) = table {
339            let table_route_cache: TableRouteCacheRef =
340                self.cache_registry.get().context(CacheNotFoundSnafu {
341                    name: "table_route_cache",
342                })?;
343            let table_info_cache: TableInfoCacheRef =
344                self.cache_registry.get().context(CacheNotFoundSnafu {
345                    name: "table_info_cache",
346                })?;
347            return Self::override_logical_table_partition_key_indices(
348                &table_route_cache,
349                &table_info_cache,
350                table,
351            )
352            .await
353            .map(Some);
354        }
355
356        if channel == Channel::Postgres {
357            // falldown to pg_catalog
358            if let Some(table) =
359                self.system_catalog
360                    .table(catalog_name, PG_CATALOG_NAME, table_name, query_ctx)
361            {
362                return Ok(Some(table));
363            }
364        }
365
366        Ok(None)
367    }
368
369    async fn table_id(
370        &self,
371        catalog_name: &str,
372        schema_name: &str,
373        table_name: &str,
374        query_ctx: Option<&QueryContext>,
375    ) -> Result<Option<TableId>> {
376        let channel = query_ctx.map_or(Channel::Unknown, |ctx| ctx.channel());
377        if let Some(table) =
378            self.system_catalog
379                .table(catalog_name, schema_name, table_name, query_ctx)
380        {
381            return Ok(Some(table.table_info().table_id()));
382        }
383
384        let table_cache: TableNameCacheRef =
385            self.cache_registry.get().context(CacheNotFoundSnafu {
386                name: "table_name_cache",
387            })?;
388
389        let table = table_cache
390            .get_by_ref(&TableName {
391                catalog_name: catalog_name.to_string(),
392                schema_name: schema_name.to_string(),
393                table_name: table_name.to_string(),
394            })
395            .await
396            .context(GetTableCacheSnafu)?;
397
398        if let Some(table) = table {
399            return Ok(Some(table));
400        }
401
402        if channel == Channel::Postgres {
403            // falldown to pg_catalog
404            if let Some(table) =
405                self.system_catalog
406                    .table(catalog_name, PG_CATALOG_NAME, table_name, query_ctx)
407            {
408                return Ok(Some(table.table_info().table_id()));
409            }
410        }
411
412        Ok(None)
413    }
414
415    async fn table_info_by_id(&self, table_id: TableId) -> Result<Option<TableInfoRef>> {
416        let table_info_cache: TableInfoCacheRef =
417            self.cache_registry.get().context(CacheNotFoundSnafu {
418                name: "table_info_cache",
419            })?;
420        table_info_cache
421            .get_by_ref(&table_id)
422            .await
423            .context(GetTableCacheSnafu)
424    }
425
426    async fn tables_by_ids(
427        &self,
428        catalog: &str,
429        schema: &str,
430        table_ids: &[TableId],
431    ) -> Result<Vec<TableRef>> {
432        let table_info_values = self
433            .table_metadata_manager
434            .table_info_manager()
435            .batch_get(table_ids)
436            .await
437            .context(TableMetadataManagerSnafu)?;
438
439        let tables = table_info_values
440            .into_values()
441            .filter(|t| t.table_info.catalog_name == catalog && t.table_info.schema_name == schema)
442            .map(build_table)
443            .collect::<Vec<_>>();
444
445        Ok(tables)
446    }
447
448    fn tables<'a>(
449        &'a self,
450        catalog: &'a str,
451        schema: &'a str,
452        query_ctx: Option<&'a QueryContext>,
453    ) -> BoxStream<'a, Result<TableRef>> {
454        let sys_tables = try_stream!({
455            // System tables
456            let sys_table_names = self.system_catalog.table_names(schema, query_ctx);
457            for table_name in sys_table_names {
458                if let Some(table) =
459                    self.system_catalog
460                        .table(catalog, schema, &table_name, query_ctx)
461                {
462                    yield table;
463                }
464            }
465        });
466
467        const BATCH_SIZE: usize = 128;
468        const CONCURRENCY: usize = 8;
469
470        let (tx, rx) = tokio::sync::mpsc::channel(64);
471        let metadata_manager = self.table_metadata_manager.clone();
472        let catalog = catalog.to_string();
473        let schema = schema.to_string();
474        let semaphore = Arc::new(Semaphore::new(CONCURRENCY));
475        let table_route_cache: Result<TableRouteCacheRef> =
476            self.cache_registry.get().context(CacheNotFoundSnafu {
477                name: "table_route_cache",
478            });
479        let table_info_cache: Result<TableInfoCacheRef> =
480            self.cache_registry.get().context(CacheNotFoundSnafu {
481                name: "table_info_cache",
482            });
483
484        common_runtime::spawn_global(async move {
485            let table_route_cache = match table_route_cache {
486                Ok(table_route_cache) => table_route_cache,
487                Err(e) => {
488                    let _ = tx.send(Err(e)).await;
489                    return;
490                }
491            };
492            let table_info_cache = match table_info_cache {
493                Ok(table_info_cache) => table_info_cache,
494                Err(e) => {
495                    let _ = tx.send(Err(e)).await;
496                    return;
497                }
498            };
499
500            let table_id_stream = metadata_manager
501                .table_name_manager()
502                .tables(&catalog, &schema)
503                .map_ok(|(_, v)| v.table_id());
504            // Split table ids into chunks
505            let mut table_id_chunks = table_id_stream.ready_chunks(BATCH_SIZE);
506
507            while let Some(table_ids) = table_id_chunks.next().await {
508                let table_ids = match table_ids
509                    .into_iter()
510                    .collect::<std::result::Result<Vec<_>, _>>()
511                    .map_err(BoxedError::new)
512                    .context(ListTablesSnafu {
513                        catalog: &catalog,
514                        schema: &schema,
515                    }) {
516                    Ok(table_ids) => table_ids,
517                    Err(e) => {
518                        let _ = tx.send(Err(e)).await;
519                        return;
520                    }
521                };
522
523                let metadata_manager = metadata_manager.clone();
524                let tx = tx.clone();
525                let semaphore = semaphore.clone();
526                let table_route_cache = table_route_cache.clone();
527                let table_info_cache = table_info_cache.clone();
528                common_runtime::spawn_global(async move {
529                    // we don't explicitly close the semaphore so just ignore the potential error.
530                    let _ = semaphore.acquire().await;
531                    let table_info_values = match metadata_manager
532                        .table_info_manager()
533                        .batch_get(&table_ids)
534                        .await
535                        .context(TableMetadataManagerSnafu)
536                    {
537                        Ok(table_info_values) => table_info_values,
538                        Err(e) => {
539                            let _ = tx.send(Err(e)).await;
540                            return;
541                        }
542                    };
543
544                    for table in table_info_values.into_values().map(build_table) {
545                        let table = Self::override_logical_table_partition_key_indices(
546                            &table_route_cache,
547                            &table_info_cache,
548                            table,
549                        )
550                        .await;
551                        if tx.send(table).await.is_err() {
552                            return;
553                        }
554                    }
555                });
556            }
557        });
558
559        let user_tables = ReceiverStream::new(rx);
560        Box::pin(sys_tables.chain(user_tables))
561    }
562}
563
564fn build_table(table_info_value: TableInfoValue) -> TableRef {
565    let table_info = table_info_value.table_info;
566    DistTable::table(Arc::new(table_info))
567}
568
569// TODO: This struct can hold a static map of all system tables when
570// the upper layer (e.g., procedure) can inform the catalog manager
571// a new catalog is created.
572/// Existing system tables:
573/// - public.numbers
574/// - information_schema.{tables}
575/// - pg_catalog.{tables}
576#[derive(Clone)]
577pub(super) struct SystemCatalog {
578    pub(super) catalog_manager: Weak<KvBackendCatalogManager>,
579    pub(super) catalog_cache: Cache<String, Arc<InformationSchemaProvider>>,
580    pub(super) pg_catalog_cache: Cache<String, Arc<PGCatalogProvider>>,
581
582    // system_schema_provider for default catalog
583    pub(super) information_schema_provider: Arc<InformationSchemaProvider>,
584    pub(super) pg_catalog_provider: Arc<PGCatalogProvider>,
585    pub(super) numbers_table_provider: NumbersTableProvider,
586    pub(super) backend: KvBackendRef,
587    pub(super) process_manager: Option<ProcessManagerRef>,
588    pub(super) extra_information_table_factories:
589        std::collections::HashMap<String, InformationSchemaTableFactoryRef>,
590}
591
592impl SystemCatalog {
593    fn schema_names(&self, query_ctx: Option<&QueryContext>) -> Vec<String> {
594        let channel = query_ctx.map_or(Channel::Unknown, |ctx| ctx.channel());
595        match channel {
596            // pg_catalog only visible under postgres protocol
597            Channel::Postgres => vec![
598                INFORMATION_SCHEMA_NAME.to_string(),
599                PG_CATALOG_NAME.to_string(),
600            ],
601            _ => {
602                vec![INFORMATION_SCHEMA_NAME.to_string()]
603            }
604        }
605    }
606
607    fn table_names(&self, schema: &str, query_ctx: Option<&QueryContext>) -> Vec<String> {
608        let channel = query_ctx.map_or(Channel::Unknown, |ctx| ctx.channel());
609        match schema {
610            INFORMATION_SCHEMA_NAME => self.information_schema_provider.table_names(),
611            PG_CATALOG_NAME if channel == Channel::Postgres => {
612                self.pg_catalog_provider.table_names()
613            }
614            DEFAULT_SCHEMA_NAME => self.numbers_table_provider.table_names(),
615            // Computed entity-graph tables overlay the physical tables of
616            // `greptime_private` (the caller appends these to the physical list).
617            DEFAULT_PRIVATE_SCHEMA_NAME => SemanticGraphTableProvider::table_names(),
618            _ => vec![],
619        }
620    }
621
622    fn schema_exists(&self, schema: &str, query_ctx: Option<&QueryContext>) -> bool {
623        let channel = query_ctx.map_or(Channel::Unknown, |ctx| ctx.channel());
624        match channel {
625            Channel::Postgres => schema == PG_CATALOG_NAME || schema == INFORMATION_SCHEMA_NAME,
626            _ => schema == INFORMATION_SCHEMA_NAME,
627        }
628    }
629
630    fn table_exists(&self, schema: &str, table: &str, query_ctx: Option<&QueryContext>) -> bool {
631        let channel = query_ctx.map_or(Channel::Unknown, |ctx| ctx.channel());
632        if schema == INFORMATION_SCHEMA_NAME {
633            self.information_schema_provider.table(table).is_some()
634        } else if schema == DEFAULT_SCHEMA_NAME {
635            self.numbers_table_provider.table_exists(table)
636        } else if schema == PG_CATALOG_NAME && channel == Channel::Postgres {
637            self.pg_catalog_provider.table(table).is_some()
638        } else if schema == DEFAULT_PRIVATE_SCHEMA_NAME {
639            SemanticGraphTableProvider::table_exists(table)
640        } else {
641            false
642        }
643    }
644
645    fn table(
646        &self,
647        catalog: &str,
648        schema: &str,
649        table_name: &str,
650        query_ctx: Option<&QueryContext>,
651    ) -> Option<TableRef> {
652        let channel = query_ctx.map_or(Channel::Unknown, |ctx| ctx.channel());
653        if schema == INFORMATION_SCHEMA_NAME {
654            let information_schema_provider =
655                self.catalog_cache.get_with_by_ref(catalog, move || {
656                    let provider = InformationSchemaProvider::new(
657                        catalog.to_string(),
658                        self.catalog_manager.clone(),
659                        Arc::new(FlowMetadataManager::new(self.backend.clone())),
660                        self.process_manager.clone(),
661                        self.backend.clone(),
662                    );
663                    let provider = provider
664                        .with_extra_table_factories(self.extra_information_table_factories.clone());
665                    Arc::new(provider)
666                });
667            information_schema_provider.table(table_name)
668        } else if schema == PG_CATALOG_NAME && channel == Channel::Postgres {
669            if catalog == DEFAULT_CATALOG_NAME {
670                self.pg_catalog_provider.table(table_name)
671            } else {
672                let pg_catalog_provider =
673                    self.pg_catalog_cache.get_with_by_ref(catalog, move || {
674                        Arc::new(PGCatalogProvider::new(
675                            catalog.to_string(),
676                            self.catalog_manager.clone(),
677                        ))
678                    });
679                pg_catalog_provider.table(table_name)
680            }
681        } else if schema == DEFAULT_SCHEMA_NAME {
682            self.numbers_table_provider.table(table_name)
683        } else if schema == DEFAULT_PRIVATE_SCHEMA_NAME
684            && SemanticGraphTableProvider::table_exists(table_name)
685        {
686            // Constructed on demand (the provider is a name + a weak ref); the
687            // system catalog is consulted before physical resolution, so the
688            // computed tables shadow same-named physical tables by design.
689            SemanticGraphTableProvider::new(
690                catalog.to_string(),
691                self.catalog_manager.clone(),
692                query_ctx.cloned().map(Arc::new),
693            )
694            .table(table_name)
695        } else {
696            None
697        }
698    }
699}
700
701#[cfg(test)]
702mod tests {
703    use std::sync::atomic::{AtomicUsize, Ordering};
704
705    use common_meta::cache::{CacheContainer, Initializer};
706    use common_meta::key::table_route::LogicalTableRouteValue;
707    use moka::future::Cache as FutureCache;
708    use table::metadata::TableInfo;
709
710    use super::*;
711
712    fn metric_table_info(
713        table_id: TableId,
714        table_name: &str,
715        partition_key_indices: Vec<usize>,
716    ) -> TableInfo {
717        let mut table_info =
718            common_meta::ddl::test_util::create_table::test_create_table_task(table_name, table_id)
719                .table_info;
720        table_info.meta.engine = METRIC_ENGINE_NAME.to_string();
721        table_info.meta.partition_key_indices = partition_key_indices;
722        table_info
723    }
724
725    #[tokio::test]
726    async fn test_logical_table_uses_cached_physical_table_info() {
727        const LOGICAL_TABLE_ID: TableId = 1;
728        const PHYSICAL_TABLE_ID: TableId = 2;
729
730        let route = Arc::new(TableRoute::Logical(Arc::new(LogicalTableRouteValue::new(
731            PHYSICAL_TABLE_ID,
732        ))));
733        let route_initializer: Initializer<TableId, Arc<TableRoute>> = Arc::new(move |_| {
734            let route = route.clone();
735            Box::pin(async move { Ok(Some(route)) })
736        });
737        let table_route_cache = Arc::new(CacheContainer::new(
738            "test_table_route_cache".to_string(),
739            FutureCache::new(16),
740            Box::new(|_, _| Box::pin(async { Ok(()) })),
741            route_initializer,
742            |_| true,
743        ));
744
745        let load_count = Arc::new(AtomicUsize::new(0));
746        let physical_table_info =
747            Arc::new(metric_table_info(PHYSICAL_TABLE_ID, "physical", vec![1]));
748        let table_info_initializer: Initializer<TableId, Arc<TableInfo>> = {
749            let load_count = load_count.clone();
750            let table_info = physical_table_info.clone();
751            Arc::new(move |_| {
752                load_count.fetch_add(1, Ordering::Relaxed);
753                let table_info = table_info.clone();
754                Box::pin(async move { Ok(Some(table_info)) })
755            })
756        };
757        let table_info_cache = Arc::new(CacheContainer::new(
758            "test_table_info_cache".to_string(),
759            FutureCache::new(16),
760            Box::new(|_, _| Box::pin(async { Ok(()) })),
761            table_info_initializer,
762            |_| true,
763        ));
764
765        let logical_table = DistTable::table(Arc::new(metric_table_info(
766            LOGICAL_TABLE_ID,
767            "logical",
768            vec![],
769        )));
770        for _ in 0..2 {
771            let table = KvBackendCatalogManager::override_logical_table_partition_key_indices(
772                &table_route_cache,
773                &table_info_cache,
774                logical_table.clone(),
775            )
776            .await
777            .unwrap();
778            assert_eq!(table.table_info().meta.partition_key_indices, vec![1]);
779        }
780        assert_eq!(load_count.load(Ordering::Relaxed), 1);
781    }
782}