1use std::collections::HashMap;
16use std::sync::Arc;
17
18use datafusion::catalog::{
19 CatalogProvider, CatalogProviderList, MemTable, SchemaProvider, TableProvider,
20};
21use datafusion::datasource::provider_as_source;
22use datafusion::error as df_error;
23use datafusion::error::Result as DfResult;
24use datafusion_common::DataFusionError;
25use datafusion_common::tree_node::{Transformed, TreeNode, TreeNodeRecursion, TreeNodeRewriter};
26use datafusion_expr::{LogicalPlan, TableSource};
27use futures::TryStreamExt;
28use session::context::QueryContextRef;
29use snafu::{OptionExt, ResultExt};
30use store_api::region_info::RegionInfoEntry;
31use store_api::sst_entry::{ManifestSstEntry, PuffinIndexMetaEntry, StorageSstEntry};
32use store_api::storage::RegionId;
33
34use crate::error::{DataFusionSnafu, ListStorageSstsSnafu, Result, UnexpectedSnafu};
35use crate::region_server::RegionServer;
36
37#[allow(clippy::enum_variant_names)]
40#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
41enum InternalTableKind {
42 InspectSstManifest,
43 InspectSstStorage,
44 InspectSstIndexMeta,
45 InspectRegionInfo,
46}
47
48impl InternalTableKind {
49 pub fn from_table_name(name: &str) -> Option<Self> {
51 if name.eq_ignore_ascii_case(ManifestSstEntry::reserved_table_name_for_inspection()) {
52 return Some(Self::InspectSstManifest);
53 }
54 if name.eq_ignore_ascii_case(StorageSstEntry::reserved_table_name_for_inspection()) {
55 return Some(Self::InspectSstStorage);
56 }
57 if name.eq_ignore_ascii_case(PuffinIndexMetaEntry::reserved_table_name_for_inspection()) {
58 return Some(Self::InspectSstIndexMeta);
59 }
60 if name.eq_ignore_ascii_case(RegionInfoEntry::reserved_table_name_for_inspection()) {
61 return Some(Self::InspectRegionInfo);
62 }
63 None
64 }
65
66 pub async fn table_provider(&self, server: &RegionServer) -> Result<Arc<dyn TableProvider>> {
68 match self {
69 Self::InspectSstManifest => server.inspect_sst_manifest_provider().await,
70 Self::InspectSstStorage => server.inspect_sst_storage_provider().await,
71 Self::InspectSstIndexMeta => server.inspect_sst_index_meta_provider().await,
72 Self::InspectRegionInfo => server.inspect_region_info_provider().await,
73 }
74 }
75}
76
77impl RegionServer {
78 pub async fn inspect_sst_manifest_provider(&self) -> Result<Arc<dyn TableProvider>> {
80 let mito = {
81 let guard = self.inner.mito_engine.read().unwrap();
82 guard.as_ref().cloned().context(UnexpectedSnafu {
83 violated: "mito engine not available",
84 })?
85 };
86
87 let entries = mito.all_ssts_from_manifest().await;
88 let schema = ManifestSstEntry::schema().arrow_schema().clone();
89 let batch = ManifestSstEntry::to_record_batch(&entries)
90 .map_err(DataFusionError::from)
91 .context(DataFusionSnafu)?;
92
93 let table = MemTable::try_new(schema, vec![vec![batch]]).context(DataFusionSnafu)?;
94 Ok(Arc::new(table))
95 }
96
97 pub async fn inspect_sst_storage_provider(&self) -> Result<Arc<dyn TableProvider>> {
99 let mito = {
100 let guard = self.inner.mito_engine.read().unwrap();
101 guard.as_ref().cloned().context(UnexpectedSnafu {
102 violated: "mito engine not available",
103 })?
104 };
105 let entries = mito
106 .all_ssts_from_storage()
107 .try_collect::<Vec<_>>()
108 .await
109 .context(ListStorageSstsSnafu)?;
110 let schema = StorageSstEntry::schema().arrow_schema().clone();
111 let batch = StorageSstEntry::to_record_batch(&entries)
112 .map_err(DataFusionError::from)
113 .context(DataFusionSnafu)?;
114
115 let table = MemTable::try_new(schema, vec![vec![batch]]).context(DataFusionSnafu)?;
116 Ok(Arc::new(table))
117 }
118
119 pub async fn inspect_sst_index_meta_provider(&self) -> Result<Arc<dyn TableProvider>> {
121 let mito = {
122 let guard = self.inner.mito_engine.read().unwrap();
123 guard.as_ref().cloned().context(UnexpectedSnafu {
124 violated: "mito engine not available",
125 })?
126 };
127
128 let entries = mito.all_index_metas().await;
129 let schema = PuffinIndexMetaEntry::schema().arrow_schema().clone();
130 let batch = PuffinIndexMetaEntry::to_record_batch(&entries)
131 .map_err(DataFusionError::from)
132 .context(DataFusionSnafu)?;
133
134 let table = MemTable::try_new(schema, vec![vec![batch]]).context(DataFusionSnafu)?;
135 Ok(Arc::new(table))
136 }
137
138 pub async fn inspect_region_info_provider(&self) -> Result<Arc<dyn TableProvider>> {
140 let mito = {
141 let guard = self.inner.mito_engine.read().unwrap();
142 guard.as_ref().cloned().context(UnexpectedSnafu {
143 violated: "mito engine not available",
144 })?
145 };
146
147 let entries = mito.all_region_infos().await;
148 let schema = RegionInfoEntry::schema().arrow_schema().clone();
149 let batch = RegionInfoEntry::to_record_batch(&entries)
150 .map_err(DataFusionError::from)
151 .context(DataFusionSnafu)?;
152
153 let table = MemTable::try_new(schema, vec![vec![batch]]).context(DataFusionSnafu)?;
154 Ok(Arc::new(table))
155 }
156}
157
158#[derive(Clone, Debug)]
162pub(crate) struct NameAwareCatalogList {
163 catalog: NameAwareCatalogProvider,
164}
165
166impl NameAwareCatalogList {
167 pub fn new(server: RegionServer, region_id: RegionId, query_ctx: QueryContextRef) -> Self {
169 let schema_provider = NameAwareSchemaProvider {
170 server,
171 region_id,
172 query_ctx,
173 };
174 let catalog = NameAwareCatalogProvider {
175 schema: schema_provider,
176 };
177 Self { catalog }
178 }
179}
180
181impl CatalogProviderList for NameAwareCatalogList {
182 fn register_catalog(
183 &self,
184 _name: String,
185 _catalog: Arc<dyn CatalogProvider>,
186 ) -> Option<Arc<dyn CatalogProvider>> {
187 None
188 }
189 fn catalog_names(&self) -> Vec<String> {
190 vec![]
191 }
192 fn catalog(&self, _name: &str) -> Option<Arc<dyn CatalogProvider>> {
193 Some(Arc::new(self.catalog.clone()))
194 }
195}
196
197#[derive(Clone, Debug)]
198struct NameAwareCatalogProvider {
199 schema: NameAwareSchemaProvider,
200}
201
202impl CatalogProvider for NameAwareCatalogProvider {
203 fn schema_names(&self) -> Vec<String> {
204 vec![]
205 }
206 fn schema(&self, _name: &str) -> Option<Arc<dyn SchemaProvider>> {
207 Some(Arc::new(self.schema.clone()))
208 }
209}
210
211#[derive(Clone)]
212struct NameAwareSchemaProvider {
213 server: RegionServer,
214 region_id: RegionId,
215 query_ctx: QueryContextRef,
216}
217
218impl std::fmt::Debug for NameAwareSchemaProvider {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 write!(f, "NameAwareSchemaProvider")
221 }
222}
223
224#[async_trait::async_trait]
225impl SchemaProvider for NameAwareSchemaProvider {
226 fn table_names(&self) -> Vec<String> {
227 vec![]
228 }
229
230 async fn table(&self, name: &str) -> DfResult<Option<Arc<dyn TableProvider>>> {
231 if let Some(kind) = InternalTableKind::from_table_name(name) {
233 return kind
234 .table_provider(&self.server)
235 .await
236 .map(Some)
237 .map_err(|e| df_error::DataFusionError::External(Box::new(e)));
238 }
239
240 let provider = self
242 .server
243 .table_provider(self.region_id, Some(self.query_ctx.clone()))
244 .await
245 .map_err(|e| df_error::DataFusionError::External(Box::new(e)))?;
246 Ok(Some(provider))
247 }
248
249 fn table_exist(&self, _name: &str) -> bool {
250 true
251 }
252}
253pub(crate) struct NameAwareDataSourceInjectorBuilder {
259 need_region_provider: bool,
261 reserved_table_needed: Vec<InternalTableKind>,
263}
264
265impl NameAwareDataSourceInjectorBuilder {
266 pub fn from_plan(plan: &LogicalPlan) -> DfResult<Self> {
269 let mut need_region_provider = false;
270 let mut reserved_table_needed = Vec::new();
271 plan.apply(|node| {
272 if let LogicalPlan::TableScan(ts) = node {
273 let name = ts.table_name.to_string();
274 if let Some(kind) = InternalTableKind::from_table_name(&name) {
275 if !reserved_table_needed.contains(&kind) {
276 reserved_table_needed.push(kind);
277 }
278 } else {
279 need_region_provider = true;
281 }
282 }
283 Ok(TreeNodeRecursion::Continue)
284 })?;
285
286 Ok(Self {
287 need_region_provider,
288 reserved_table_needed,
289 })
290 }
291
292 pub async fn build(
293 self,
294 server: &RegionServer,
295 region_id: RegionId,
296 query_ctx: QueryContextRef,
297 ) -> Result<NameAwareDataSourceInjector> {
298 let region = if self.need_region_provider {
299 let provider = server.table_provider(region_id, Some(query_ctx)).await?;
300 Some(provider_as_source(provider))
301 } else {
302 None
303 };
304
305 let mut reserved_sources = HashMap::new();
306 for kind in &self.reserved_table_needed {
307 let provider = kind.table_provider(server).await?;
308 reserved_sources.insert(*kind, provider_as_source(provider));
309 }
310
311 Ok(NameAwareDataSourceInjector {
312 reserved_sources,
313 region_source: region,
314 })
315 }
316}
317
318pub(crate) struct NameAwareDataSourceInjector {
321 reserved_sources: HashMap<InternalTableKind, Arc<dyn TableSource>>,
323 region_source: Option<Arc<dyn TableSource>>,
325}
326
327impl TreeNodeRewriter for NameAwareDataSourceInjector {
328 type Node = LogicalPlan;
329
330 fn f_up(&mut self, node: Self::Node) -> DfResult<Transformed<Self::Node>> {
331 Ok(match node {
332 LogicalPlan::TableScan(mut scan) => {
333 let name = scan.table_name.to_string();
334 if let Some(kind) = InternalTableKind::from_table_name(&name)
335 && let Some(source) = self.reserved_sources.get(&kind)
336 {
337 scan.source = source.clone();
339 } else {
340 let Some(region) = &self.region_source else {
341 return Err(datafusion::error::DataFusionError::Plan(
343 "region provider not available".to_string(),
344 ));
345 };
346 scan.source = region.clone();
348 }
349 Transformed::yes(LogicalPlan::TableScan(scan))
350 }
351 _ => Transformed::no(node),
352 })
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use std::sync::Arc;
359
360 use datafusion::catalog::MemTable as DfMemTable;
361 use datafusion_common::tree_node::TreeNode;
362 use datafusion_expr::{LogicalPlanBuilder, table_scan};
363 use datatypes::arrow::array::Int32Array;
364 use datatypes::arrow::datatypes::{DataType, Field, Schema};
365 use datatypes::arrow::record_batch::RecordBatch;
366 use store_api::region_info::RegionInfoEntry;
367
368 use super::*; fn test_schema() -> Schema {
371 Schema::new(vec![Field::new("a", DataType::Int32, true)])
372 }
373
374 fn empty_mem_table() -> Arc<DfMemTable> {
375 let schema = Arc::new(test_schema());
376 let batch = RecordBatch::try_new(
377 schema.clone(),
378 vec![Arc::new(Int32Array::from(Vec::<i32>::new()))],
379 )
380 .unwrap();
381 Arc::new(DfMemTable::try_new(schema, vec![vec![batch]]).unwrap())
382 }
383
384 #[test]
385 fn test_injector_builder_from_plan_flags() {
386 let schema = test_schema();
387 let reserved = ManifestSstEntry::reserved_table_name_for_inspection();
388 let plan1 = table_scan(Some(reserved), &schema, None)
390 .unwrap()
391 .build()
392 .unwrap();
393 let b1 = NameAwareDataSourceInjectorBuilder::from_plan(&plan1).unwrap();
394 assert!(!b1.need_region_provider);
395 assert_eq!(
396 b1.reserved_table_needed,
397 vec![InternalTableKind::InspectSstManifest]
398 );
399
400 let plan2 = table_scan(Some("normal_table"), &schema, None)
402 .unwrap()
403 .build()
404 .unwrap();
405 let b2 = NameAwareDataSourceInjectorBuilder::from_plan(&plan2).unwrap();
406 assert!(b2.need_region_provider);
407 assert!(b2.reserved_table_needed.is_empty());
408
409 let p_res = table_scan(Some(reserved), &schema, None)
411 .unwrap()
412 .build()
413 .unwrap();
414 let p_norm = table_scan(Some("normal_table"), &schema, None)
415 .unwrap()
416 .build()
417 .unwrap();
418 let plan3 = LogicalPlanBuilder::from(p_res)
419 .union(LogicalPlanBuilder::from(p_norm).build().unwrap())
420 .unwrap()
421 .build()
422 .unwrap();
423 let b3 = NameAwareDataSourceInjectorBuilder::from_plan(&plan3).unwrap();
424 assert!(b3.need_region_provider);
425 assert_eq!(
426 b3.reserved_table_needed,
427 vec![InternalTableKind::InspectSstManifest]
428 );
429
430 let region_info = RegionInfoEntry::reserved_table_name_for_inspection();
431 let plan4 = table_scan(Some(region_info), &schema, None)
432 .unwrap()
433 .build()
434 .unwrap();
435 let b4 = NameAwareDataSourceInjectorBuilder::from_plan(&plan4).unwrap();
436 assert!(!b4.need_region_provider);
437 assert_eq!(
438 b4.reserved_table_needed,
439 vec![InternalTableKind::InspectRegionInfo]
440 );
441 }
442
443 #[test]
444 fn test_rewriter_replaces_with_reserved_source() {
445 let schema = test_schema();
446 let table_name = ManifestSstEntry::reserved_table_name_for_inspection();
447 let plan = table_scan(Some(table_name), &schema, None)
448 .unwrap()
449 .build()
450 .unwrap();
451
452 let provider = empty_mem_table();
453 let source = provider_as_source(provider);
454
455 let mut injector = NameAwareDataSourceInjector {
456 reserved_sources: {
457 let mut m = HashMap::new();
458 m.insert(InternalTableKind::InspectSstManifest, source.clone());
459 m
460 },
461 region_source: None,
462 };
463
464 let transformed = plan.rewrite(&mut injector).unwrap();
465 let new_plan = transformed.data;
466
467 if let LogicalPlan::TableScan(scan) = new_plan {
468 let src_ptr = Arc::as_ptr(&scan.source);
470 let want_ptr = Arc::as_ptr(&source);
471 assert!(std::ptr::eq(src_ptr, want_ptr));
472 } else {
473 panic!("expected TableScan after rewrite");
474 }
475 }
476
477 #[test]
478 fn test_rewriter_replaces_with_region_info_reserved_source() {
479 let schema = test_schema();
480 let table_name = RegionInfoEntry::reserved_table_name_for_inspection();
481 let plan = table_scan(Some(table_name), &schema, None)
482 .unwrap()
483 .build()
484 .unwrap();
485
486 let provider = empty_mem_table();
487 let source = provider_as_source(provider);
488
489 let mut injector = NameAwareDataSourceInjector {
490 reserved_sources: {
491 let mut m = HashMap::new();
492 m.insert(InternalTableKind::InspectRegionInfo, source.clone());
493 m
494 },
495 region_source: None,
496 };
497
498 let transformed = plan.rewrite(&mut injector).unwrap();
499 let new_plan = transformed.data;
500
501 if let LogicalPlan::TableScan(scan) = new_plan {
502 let src_ptr = Arc::as_ptr(&scan.source);
503 let want_ptr = Arc::as_ptr(&source);
504 assert!(std::ptr::eq(src_ptr, want_ptr));
505 } else {
506 panic!("expected TableScan after rewrite");
507 }
508 }
509
510 #[test]
511 fn test_rewriter_replaces_with_region_source_for_normal() {
512 let schema = test_schema();
513 let plan = table_scan(Some("normal_table"), &schema, None)
514 .unwrap()
515 .build()
516 .unwrap();
517
518 let provider = empty_mem_table();
519 let region_source = provider_as_source(provider);
520
521 let mut injector = NameAwareDataSourceInjector {
522 reserved_sources: HashMap::new(),
523 region_source: Some(region_source.clone()),
524 };
525
526 let transformed = plan.rewrite(&mut injector).unwrap();
527 let new_plan = transformed.data;
528
529 if let LogicalPlan::TableScan(scan) = new_plan {
530 let src_ptr = Arc::as_ptr(&scan.source);
531 let want_ptr = Arc::as_ptr(®ion_source);
532 assert!(std::ptr::eq(src_ptr, want_ptr));
533 } else {
534 panic!("expected TableScan after rewrite");
535 }
536 }
537}