1use std::sync::{Arc, Weak};
16
17use arrow_schema::SchemaRef as ArrowSchemaRef;
18use common_catalog::consts::INFORMATION_SCHEMA_KEY_COLUMN_USAGE_TABLE_ID;
19use common_error::ext::BoxedError;
20use common_recordbatch::adapter::RecordBatchStreamAdapter;
21use common_recordbatch::{RecordBatch, SendableRecordBatchStream};
22use datafusion::execution::TaskContext;
23use datafusion::physical_plan::SendableRecordBatchStream as DfSendableRecordBatchStream;
24use datafusion::physical_plan::stream::RecordBatchStreamAdapter as DfRecordBatchStreamAdapter;
25use datafusion::physical_plan::streaming::PartitionStream as DfPartitionStream;
26use datatypes::prelude::{ConcreteDataType, MutableVector, ScalarVectorBuilder, VectorRef};
27use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
28use datatypes::value::Value;
29use datatypes::vectors::{StringVector, StringVectorBuilder, UInt32VectorBuilder};
30use futures_util::TryStreamExt;
31use snafu::{OptionExt, ResultExt};
32use store_api::storage::{ScanRequest, TableId};
33
34use crate::CatalogManager;
35use crate::error::{
36 CreateRecordBatchSnafu, InternalSnafu, Result, UpgradeWeakCatalogManagerRefSnafu,
37};
38use crate::system_schema::SystemTable;
39use crate::system_schema::information_schema::{
40 InformationSchemaTableScope, InformationTable, KEY_COLUMN_USAGE, Predicates,
41 primary_key_encoding_index_type,
42};
43
44pub const CONSTRAINT_SCHEMA: &str = "constraint_schema";
45pub const CONSTRAINT_NAME: &str = "constraint_name";
46pub const TABLE_CATALOG: &str = "table_catalog";
48pub const REAL_TABLE_CATALOG: &str = "real_table_catalog";
50pub const TABLE_SCHEMA: &str = "table_schema";
51pub const TABLE_NAME: &str = "table_name";
52pub const COLUMN_NAME: &str = "column_name";
53pub const ORDINAL_POSITION: &str = "ordinal_position";
54pub const GREPTIME_INDEX_TYPE: &str = "greptime_index_type";
56const INIT_CAPACITY: usize = 42;
57
58pub(crate) const CONSTRAINT_NAME_TIME_INDEX: &str = "TIME INDEX";
60
61pub(crate) const CONSTRAINT_NAME_PRI: &str = "PRIMARY";
63
64pub(crate) const CONSTRAINT_NAME_INVERTED_INDEX: &str = "INVERTED INDEX";
66
67pub(crate) const CONSTRAINT_NAME_FULLTEXT_INDEX: &str = "FULLTEXT INDEX";
69
70pub(crate) const CONSTRAINT_NAME_SKIPPING_INDEX: &str = "SKIPPING INDEX";
72
73#[derive(Debug)]
77pub(super) struct InformationSchemaKeyColumnUsage {
78 schema: SchemaRef,
79 catalog_name: String,
80 catalog_manager: Weak<dyn CatalogManager>,
81}
82
83impl InformationSchemaKeyColumnUsage {
84 pub(super) fn new(catalog_name: String, catalog_manager: Weak<dyn CatalogManager>) -> Self {
85 Self {
86 schema: Self::schema(),
87 catalog_name,
88 catalog_manager,
89 }
90 }
91
92 pub(crate) fn schema() -> SchemaRef {
93 Arc::new(Schema::new(vec![
94 ColumnSchema::new(
95 "constraint_catalog",
96 ConcreteDataType::string_datatype(),
97 false,
98 ),
99 ColumnSchema::new(
100 CONSTRAINT_SCHEMA,
101 ConcreteDataType::string_datatype(),
102 false,
103 ),
104 ColumnSchema::new(CONSTRAINT_NAME, ConcreteDataType::string_datatype(), false),
105 ColumnSchema::new(TABLE_CATALOG, ConcreteDataType::string_datatype(), false),
106 ColumnSchema::new(
107 REAL_TABLE_CATALOG,
108 ConcreteDataType::string_datatype(),
109 false,
110 ),
111 ColumnSchema::new(TABLE_SCHEMA, ConcreteDataType::string_datatype(), false),
112 ColumnSchema::new(TABLE_NAME, ConcreteDataType::string_datatype(), false),
113 ColumnSchema::new(COLUMN_NAME, ConcreteDataType::string_datatype(), false),
114 ColumnSchema::new(ORDINAL_POSITION, ConcreteDataType::uint32_datatype(), false),
115 ColumnSchema::new(
116 "position_in_unique_constraint",
117 ConcreteDataType::uint32_datatype(),
118 true,
119 ),
120 ColumnSchema::new(
121 "referenced_table_schema",
122 ConcreteDataType::string_datatype(),
123 true,
124 ),
125 ColumnSchema::new(
126 "referenced_table_name",
127 ConcreteDataType::string_datatype(),
128 true,
129 ),
130 ColumnSchema::new(
131 "referenced_column_name",
132 ConcreteDataType::string_datatype(),
133 true,
134 ),
135 ColumnSchema::new(
136 GREPTIME_INDEX_TYPE,
137 ConcreteDataType::string_datatype(),
138 true,
139 ),
140 ]))
141 }
142
143 fn builder(&self) -> InformationSchemaKeyColumnUsageBuilder {
144 InformationSchemaKeyColumnUsageBuilder::new(
145 self.schema.clone(),
146 self.catalog_name.clone(),
147 self.catalog_manager.clone(),
148 )
149 }
150}
151
152impl InformationTable for InformationSchemaKeyColumnUsage {
153 fn scope(&self) -> InformationSchemaTableScope {
154 InformationSchemaTableScope::Catalog
155 }
156}
157
158impl SystemTable for InformationSchemaKeyColumnUsage {
159 fn table_id(&self) -> TableId {
160 INFORMATION_SCHEMA_KEY_COLUMN_USAGE_TABLE_ID
161 }
162
163 fn table_name(&self) -> &'static str {
164 KEY_COLUMN_USAGE
165 }
166
167 fn schema(&self) -> SchemaRef {
168 self.schema.clone()
169 }
170
171 fn to_stream(&self, request: ScanRequest) -> Result<SendableRecordBatchStream> {
172 let schema = self.schema.arrow_schema().clone();
173 let mut builder = self.builder();
174 let stream = Box::pin(DfRecordBatchStreamAdapter::new(
175 schema,
176 futures::stream::once(async move {
177 builder
178 .make_key_column_usage(Some(request))
179 .await
180 .map(|x| x.into_df_record_batch())
181 .map_err(Into::into)
182 }),
183 ));
184 Ok(Box::pin(
185 RecordBatchStreamAdapter::try_new(stream)
186 .map_err(BoxedError::new)
187 .context(InternalSnafu)?,
188 ))
189 }
190}
191
192struct InformationSchemaKeyColumnUsageBuilder {
196 schema: SchemaRef,
197 catalog_name: String,
198 catalog_manager: Weak<dyn CatalogManager>,
199
200 constraint_catalog: StringVectorBuilder,
201 constraint_schema: StringVectorBuilder,
202 constraint_name: StringVectorBuilder,
203 table_catalog: StringVectorBuilder,
204 real_table_catalog: StringVectorBuilder,
205 table_schema: StringVectorBuilder,
206 table_name: StringVectorBuilder,
207 column_name: StringVectorBuilder,
208 ordinal_position: UInt32VectorBuilder,
209 position_in_unique_constraint: UInt32VectorBuilder,
210 greptime_index_type: StringVectorBuilder,
211}
212
213impl InformationSchemaKeyColumnUsageBuilder {
214 fn new(
215 schema: SchemaRef,
216 catalog_name: String,
217 catalog_manager: Weak<dyn CatalogManager>,
218 ) -> Self {
219 Self {
220 schema,
221 catalog_name,
222 catalog_manager,
223 constraint_catalog: StringVectorBuilder::with_capacity(INIT_CAPACITY),
224 constraint_schema: StringVectorBuilder::with_capacity(INIT_CAPACITY),
225 constraint_name: StringVectorBuilder::with_capacity(INIT_CAPACITY),
226 table_catalog: StringVectorBuilder::with_capacity(INIT_CAPACITY),
227 real_table_catalog: StringVectorBuilder::with_capacity(INIT_CAPACITY),
228 table_schema: StringVectorBuilder::with_capacity(INIT_CAPACITY),
229 table_name: StringVectorBuilder::with_capacity(INIT_CAPACITY),
230 column_name: StringVectorBuilder::with_capacity(INIT_CAPACITY),
231 ordinal_position: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
232 position_in_unique_constraint: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
233 greptime_index_type: StringVectorBuilder::with_capacity(INIT_CAPACITY),
234 }
235 }
236
237 async fn make_key_column_usage(&mut self, request: Option<ScanRequest>) -> Result<RecordBatch> {
239 let catalog_name = self.catalog_name.clone();
240 let catalog_manager = self
241 .catalog_manager
242 .upgrade()
243 .context(UpgradeWeakCatalogManagerRefSnafu)?;
244 let predicates = Predicates::from_scan_request(&request);
245
246 for schema_name in catalog_manager.schema_names(&catalog_name, None).await? {
247 let mut stream = catalog_manager.tables(&catalog_name, &schema_name, None);
248
249 while let Some(table) = stream.try_next().await? {
250 let table_info = table.table_info();
251 let table_name = &table_info.name;
252 let keys = &table_info.meta.primary_key_indices;
253 let schema = table.schema();
254 let primary_key_encoding =
255 primary_key_encoding_index_type(&table_info.meta.options.extra_options);
256
257 for (idx, column) in schema.column_schemas().iter().enumerate() {
258 if column.is_time_index() {
259 self.add_key_column_usage(
260 &predicates,
261 &schema_name,
262 CONSTRAINT_NAME_TIME_INDEX,
263 &catalog_name,
264 &schema_name,
265 table_name,
266 &column.name,
267 1, "",
269 );
270 }
271 if let Some(pk_seq) = keys.iter().position(|k| *k == idx) {
272 self.add_key_column_usage(
273 &predicates,
274 &schema_name,
275 CONSTRAINT_NAME_PRI,
276 &catalog_name,
277 &schema_name,
278 table_name,
279 &column.name,
280 pk_seq as u32 + 1,
281 primary_key_encoding,
282 );
283 }
284 }
285 }
286 }
287
288 self.finish()
289 }
290
291 #[allow(clippy::too_many_arguments)]
294 fn add_key_column_usage(
295 &mut self,
296 predicates: &Predicates,
297 constraint_schema: &str,
298 constraint_name: &str,
299 table_catalog: &str,
300 table_schema: &str,
301 table_name: &str,
302 column_name: &str,
303 ordinal_position: u32,
304 index_types: &str,
305 ) {
306 let row = [
307 (CONSTRAINT_SCHEMA, &Value::from(constraint_schema)),
308 (CONSTRAINT_NAME, &Value::from(constraint_name)),
309 (REAL_TABLE_CATALOG, &Value::from(table_catalog)),
310 (TABLE_SCHEMA, &Value::from(table_schema)),
311 (TABLE_NAME, &Value::from(table_name)),
312 (COLUMN_NAME, &Value::from(column_name)),
313 (ORDINAL_POSITION, &Value::from(ordinal_position)),
314 (GREPTIME_INDEX_TYPE, &Value::from(index_types)),
315 ];
316
317 if !predicates.eval(&row) {
318 return;
319 }
320
321 self.constraint_catalog.push(Some("def"));
322 self.constraint_schema.push(Some(constraint_schema));
323 self.constraint_name.push(Some(constraint_name));
324 self.table_catalog.push(Some("def"));
325 self.real_table_catalog.push(Some(table_catalog));
326 self.table_schema.push(Some(table_schema));
327 self.table_name.push(Some(table_name));
328 self.column_name.push(Some(column_name));
329 self.ordinal_position.push(Some(ordinal_position));
330 self.position_in_unique_constraint.push(None);
331 self.greptime_index_type.push(Some(index_types));
332 }
333
334 fn finish(&mut self) -> Result<RecordBatch> {
335 let rows_num = self.table_catalog.len();
336
337 let null_string_vector = Arc::new(StringVector::from(vec![None as Option<&str>; rows_num]));
338 let columns: Vec<VectorRef> = vec![
339 Arc::new(self.constraint_catalog.finish()),
340 Arc::new(self.constraint_schema.finish()),
341 Arc::new(self.constraint_name.finish()),
342 Arc::new(self.table_catalog.finish()),
343 Arc::new(self.real_table_catalog.finish()),
344 Arc::new(self.table_schema.finish()),
345 Arc::new(self.table_name.finish()),
346 Arc::new(self.column_name.finish()),
347 Arc::new(self.ordinal_position.finish()),
348 Arc::new(self.position_in_unique_constraint.finish()),
349 null_string_vector.clone(),
350 null_string_vector.clone(),
351 null_string_vector,
352 Arc::new(self.greptime_index_type.finish()),
353 ];
354 RecordBatch::new(self.schema.clone(), columns).context(CreateRecordBatchSnafu)
355 }
356}
357
358impl DfPartitionStream for InformationSchemaKeyColumnUsage {
359 fn schema(&self) -> &ArrowSchemaRef {
360 self.schema.arrow_schema()
361 }
362
363 fn execute(&self, _: Arc<TaskContext>) -> DfSendableRecordBatchStream {
364 let schema = self.schema.arrow_schema().clone();
365 let mut builder = self.builder();
366 Box::pin(DfRecordBatchStreamAdapter::new(
367 schema,
368 futures::stream::once(async move {
369 builder
370 .make_key_column_usage(None)
371 .await
372 .map(|x| x.into_df_record_batch())
373 .map_err(Into::into)
374 }),
375 ))
376 }
377}