Skip to main content

catalog/
system_schema.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
15pub mod information_schema;
16mod memory_table;
17pub mod numbers_table_provider;
18pub mod pg_catalog;
19pub mod predicate;
20pub mod semantic_graph;
21mod utils;
22
23use std::collections::HashMap;
24use std::sync::Arc;
25
26use common_error::ext::BoxedError;
27use common_recordbatch::{RecordBatchStreamWrapper, SendableRecordBatchStream};
28use common_telemetry::tracing::Span;
29use datafusion::physical_plan::ExecutionPlan;
30use datatypes::schema::SchemaRef;
31use futures_util::StreamExt;
32use snafu::ResultExt;
33use store_api::data_source::DataSource;
34use store_api::storage::ScanRequest;
35use table::error::{SchemaConversionSnafu, TablesRecordBatchSnafu};
36use table::metadata::{FilterPushDownType, TableId, TableInfoBuilder, TableMetaBuilder, TableType};
37use table::requests::TableOptions;
38use table::{Table, TableRef};
39
40use crate::error::Result;
41
42pub trait SystemSchemaProvider {
43    /// Returns a map of [TableRef] in information schema.
44    fn tables(&self) -> &HashMap<String, TableRef>;
45
46    /// Returns the [TableRef] by table name.
47    fn table(&self, name: &str) -> Option<TableRef> {
48        self.tables().get(name).cloned()
49    }
50
51    /// Returns table names in the order of table id.
52    fn table_names(&self) -> Vec<String> {
53        let mut tables = self.tables().values().clone().collect::<Vec<_>>();
54
55        tables.sort_by(|t1, t2| {
56            t1.table_info()
57                .table_id()
58                .partial_cmp(&t2.table_info().table_id())
59                .unwrap()
60        });
61        tables
62            .into_iter()
63            .map(|t| t.table_info().name.clone())
64            .collect()
65    }
66}
67
68trait SystemSchemaProviderInner {
69    fn catalog_name(&self) -> &str;
70    fn schema_name() -> &'static str;
71    fn build_table(&self, name: &str) -> Option<TableRef> {
72        self.system_table(name).map(|table| {
73            build_system_table(
74                self.catalog_name().to_string(),
75                Self::schema_name(),
76                table,
77                TableOptions::default(),
78            )
79        })
80    }
81    fn system_table(&self, name: &str) -> Option<SystemTableRef>;
82}
83
84fn build_system_table(
85    catalog_name: String,
86    schema_name: &str,
87    table: SystemTableRef,
88    options: TableOptions,
89) -> TableRef {
90    let table_meta = TableMetaBuilder::empty()
91        .schema(table.schema())
92        .primary_key_indices(vec![])
93        .next_column_id(0)
94        .options(options)
95        .build()
96        .unwrap();
97    let table_info = TableInfoBuilder::default()
98        .table_id(table.table_id())
99        .name(table.table_name().to_string())
100        .catalog_name(catalog_name)
101        .schema_name(schema_name.to_string())
102        .meta(table_meta)
103        .table_type(table.table_type())
104        .build()
105        .unwrap();
106    Arc::new(Table::new(
107        Arc::new(table_info),
108        FilterPushDownType::Inexact,
109        Arc::new(SystemTableDataSource::new(table)),
110    ))
111}
112
113pub trait SystemTable {
114    fn table_id(&self) -> TableId;
115
116    fn table_name(&self) -> &'static str;
117
118    fn schema(&self) -> SchemaRef;
119
120    fn to_stream(&self, request: ScanRequest) -> Result<SendableRecordBatchStream>;
121
122    fn scan_plan(&self, _request: ScanRequest) -> Result<Option<Arc<dyn ExecutionPlan>>> {
123        Ok(None)
124    }
125
126    fn table_type(&self) -> TableType {
127        TableType::Temporary
128    }
129}
130
131pub type SystemTableRef = Arc<dyn SystemTable + Send + Sync>;
132
133struct SystemTableDataSource {
134    table: SystemTableRef,
135}
136
137impl SystemTableDataSource {
138    fn new(table: SystemTableRef) -> Self {
139        Self { table }
140    }
141
142    fn try_project(&self, projection: &[usize]) -> std::result::Result<SchemaRef, BoxedError> {
143        let schema = self
144            .table
145            .schema()
146            .try_project(projection)
147            .context(SchemaConversionSnafu)
148            .map_err(BoxedError::new)?;
149        Ok(Arc::new(schema))
150    }
151}
152
153impl DataSource for SystemTableDataSource {
154    fn get_stream(
155        &self,
156        request: ScanRequest,
157    ) -> std::result::Result<SendableRecordBatchStream, BoxedError> {
158        let projection = request.projection.clone();
159        let projected_schema = match request.projection.as_ref() {
160            Some(projection) => self.try_project(projection)?,
161            None => self.table.schema(),
162        };
163
164        let stream = self
165            .table
166            .to_stream(request)
167            .map_err(BoxedError::new)
168            .context(TablesRecordBatchSnafu)
169            .map_err(BoxedError::new)?
170            .map(move |batch| match (&projection, batch) {
171                // Some tables (e.g., inspect tables) already honor projection in their inner stream;
172                // others ignore it and return full rows. We will only apply projection here if the
173                // inner batch width doesn't match the projection size.
174                (Some(p), Ok(b)) if b.num_columns() != p.len() => b.try_project(p),
175                (_, res) => res,
176            });
177
178        let stream = RecordBatchStreamWrapper {
179            schema: projected_schema,
180            stream: Box::pin(stream),
181            output_ordering: None,
182            metrics: Default::default(),
183            span: Span::current(),
184        };
185
186        Ok(Box::pin(stream))
187    }
188
189    fn get_physical_plan(
190        &self,
191        request: ScanRequest,
192    ) -> std::result::Result<Option<Arc<dyn ExecutionPlan>>, BoxedError> {
193        self.table
194            .scan_plan(request)
195            .map_err(BoxedError::new)
196            .context(TablesRecordBatchSnafu)
197            .map_err(BoxedError::new)
198    }
199}