1use datafusion_common::instant::Instant;
21use std::fmt;
22use std::fs::File;
23use std::str::FromStr;
24use std::sync::Arc;
25
26use arrow::array::{
27 DurationMillisecondArray, GenericListArray, Int64Array, StringArray, StructArray,
28 TimestampMillisecondArray, UInt64Array,
29};
30use arrow::buffer::{Buffer, OffsetBuffer, ScalarBuffer};
31use arrow::datatypes::{DataType, Field, Fields, Schema, SchemaRef, TimeUnit};
32use arrow::record_batch::RecordBatch;
33use arrow::util::pretty::pretty_format_batches;
34use datafusion::catalog::{Session, TableFunctionArgs, TableFunctionImpl};
35use datafusion::common::{Column, plan_err};
36use datafusion::datasource::TableProvider;
37use datafusion::datasource::memory::MemorySourceConfig;
38use datafusion::error::Result;
39use datafusion::execution::cache::cache_manager::CacheManager;
40use datafusion::logical_expr::Expr;
41use datafusion::physical_plan::ExecutionPlan;
42use datafusion::scalar::ScalarValue;
43
44use async_trait::async_trait;
45use parquet::basic::ConvertedType;
46use parquet::data_type::{ByteArray, FixedLenByteArray};
47use parquet::file::reader::FileReader;
48use parquet::file::serialized_reader::SerializedFileReader;
49use parquet::file::statistics::Statistics;
50
51#[derive(Debug)]
52pub enum Function {
53 Select,
54 Explain,
55 Show,
56 CreateTable,
57 CreateTableAs,
58 Insert,
59 DropTable,
60}
61
62const ALL_FUNCTIONS: [Function; 7] = [
63 Function::CreateTable,
64 Function::CreateTableAs,
65 Function::DropTable,
66 Function::Explain,
67 Function::Insert,
68 Function::Select,
69 Function::Show,
70];
71
72impl Function {
73 pub fn function_details(&self) -> Result<&str> {
74 let details = match self {
75 Function::Select => {
76 r#"
77Command: SELECT
78Description: retrieve rows from a table or view
79Syntax:
80SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
81 [ * | expression [ [ AS ] output_name ] [, ...] ]
82 [ FROM from_item [, ...] ]
83 [ WHERE condition ]
84 [ GROUP BY [ ALL | DISTINCT ] grouping_element [, ...] ]
85 [ HAVING condition ]
86 [ WINDOW window_name AS ( window_definition ) [, ...] ]
87 [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
88 [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
89 [ LIMIT { count | ALL } ]
90 [ OFFSET start [ ROW | ROWS ] ]
91
92where from_item can be one of:
93
94 [ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
95 [ TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] ]
96 [ LATERAL ] ( select ) [ AS ] alias [ ( column_alias [, ...] ) ]
97 with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
98 [ LATERAL ] function_name ( [ argument [, ...] ] )
99 [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
100 [ LATERAL ] function_name ( [ argument [, ...] ] ) [ AS ] alias ( column_definition [, ...] )
101 [ LATERAL ] function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )
102 [ LATERAL ] ROWS FROM( function_name ( [ argument [, ...] ] ) [ AS ( column_definition [, ...] ) ] [, ...] )
103 [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
104 from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) [ AS join_using_alias ] ]
105
106and grouping_element can be one of:
107
108 ( )
109 expression
110 ( expression [, ...] )
111
112and with_query is:
113
114 with_query_name [ ( column_name [, ...] ) ] AS [ [ NOT ] MATERIALIZED ] ( select | values | insert | update | delete )
115
116TABLE [ ONLY ] table_name [ * ]"#
117 }
118 Function::Explain => {
119 r#"
120Command: EXPLAIN
121Description: show the execution plan of a statement
122Syntax:
123EXPLAIN [ ANALYZE ] statement
124"#
125 }
126 Function::Show => {
127 r#"
128Command: SHOW
129Description: show the value of a run-time parameter
130Syntax:
131SHOW name
132"#
133 }
134 Function::CreateTable => {
135 r#"
136Command: CREATE TABLE
137Description: define a new table
138Syntax:
139CREATE [ EXTERNAL ] TABLE table_name ( [
140 { column_name data_type }
141 [, ... ]
142] )
143"#
144 }
145 Function::CreateTableAs => {
146 r#"
147Command: CREATE TABLE AS
148Description: define a new table from the results of a query
149Syntax:
150CREATE TABLE table_name
151 [ (column_name [, ...] ) ]
152 AS query
153 [ WITH [ NO ] DATA ]
154"#
155 }
156 Function::Insert => {
157 r#"
158Command: INSERT
159Description: create new rows in a table
160Syntax:
161INSERT INTO table_name [ ( column_name [, ...] ) ]
162 { VALUES ( { expression } [, ...] ) [, ...] }
163"#
164 }
165 Function::DropTable => {
166 r#"
167Command: DROP TABLE
168Description: remove a table
169Syntax:
170DROP TABLE [ IF EXISTS ] name [, ...]
171"#
172 }
173 };
174 Ok(details)
175 }
176}
177
178impl FromStr for Function {
179 type Err = ();
180
181 fn from_str(s: &str) -> Result<Self, Self::Err> {
182 Ok(match s.trim().to_uppercase().as_str() {
183 "SELECT" => Self::Select,
184 "EXPLAIN" => Self::Explain,
185 "SHOW" => Self::Show,
186 "CREATE TABLE" => Self::CreateTable,
187 "CREATE TABLE AS" => Self::CreateTableAs,
188 "INSERT" => Self::Insert,
189 "DROP TABLE" => Self::DropTable,
190 _ => return Err(()),
191 })
192 }
193}
194
195impl fmt::Display for Function {
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 match *self {
198 Function::Select => write!(f, "SELECT"),
199 Function::Explain => write!(f, "EXPLAIN"),
200 Function::Show => write!(f, "SHOW"),
201 Function::CreateTable => write!(f, "CREATE TABLE"),
202 Function::CreateTableAs => write!(f, "CREATE TABLE AS"),
203 Function::Insert => write!(f, "INSERT"),
204 Function::DropTable => write!(f, "DROP TABLE"),
205 }
206 }
207}
208
209pub fn display_all_functions() -> Result<()> {
210 println!("Available help:");
211 let array = StringArray::from(
212 ALL_FUNCTIONS
213 .iter()
214 .map(|f| format!("{f}"))
215 .collect::<Vec<String>>(),
216 );
217 let schema = Schema::new(vec![Field::new("Function", DataType::Utf8, false)]);
218 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(array)])?;
219 println!("{}", pretty_format_batches(&[batch]).unwrap());
220 Ok(())
221}
222
223#[derive(Debug)]
225struct ParquetMetadataTable {
226 schema: SchemaRef,
227 batch: RecordBatch,
228}
229
230#[async_trait]
231impl TableProvider for ParquetMetadataTable {
232 fn schema(&self) -> SchemaRef {
233 self.schema.clone()
234 }
235
236 fn table_type(&self) -> datafusion::logical_expr::TableType {
237 datafusion::logical_expr::TableType::Base
238 }
239
240 async fn scan(
241 &self,
242 _state: &dyn Session,
243 projection: Option<&Vec<usize>>,
244 _filters: &[Expr],
245 _limit: Option<usize>,
246 ) -> Result<Arc<dyn ExecutionPlan>> {
247 Ok(MemorySourceConfig::try_new_exec(
248 &[vec![self.batch.clone()]],
249 TableProvider::schema(self),
250 projection.cloned(),
251 )?)
252 }
253}
254
255fn convert_parquet_statistics(
256 value: &Statistics,
257 converted_type: ConvertedType,
258) -> (Option<String>, Option<String>) {
259 match (value, converted_type) {
260 (Statistics::Boolean(val), _) => (
261 val.min_opt().map(|v| v.to_string()),
262 val.max_opt().map(|v| v.to_string()),
263 ),
264 (Statistics::Int32(val), _) => (
265 val.min_opt().map(|v| v.to_string()),
266 val.max_opt().map(|v| v.to_string()),
267 ),
268 (Statistics::Int64(val), _) => (
269 val.min_opt().map(|v| v.to_string()),
270 val.max_opt().map(|v| v.to_string()),
271 ),
272 (Statistics::Int96(val), _) => (
273 val.min_opt().map(|v| v.to_string()),
274 val.max_opt().map(|v| v.to_string()),
275 ),
276 (Statistics::Float(val), _) => (
277 val.min_opt().map(|v| v.to_string()),
278 val.max_opt().map(|v| v.to_string()),
279 ),
280 (Statistics::Double(val), _) => (
281 val.min_opt().map(|v| v.to_string()),
282 val.max_opt().map(|v| v.to_string()),
283 ),
284 (Statistics::ByteArray(val), ConvertedType::UTF8) => (
285 byte_array_to_string(val.min_opt()),
286 byte_array_to_string(val.max_opt()),
287 ),
288 (Statistics::ByteArray(val), _) => (
289 val.min_opt().map(|v| v.to_string()),
290 val.max_opt().map(|v| v.to_string()),
291 ),
292 (Statistics::FixedLenByteArray(val), ConvertedType::UTF8) => (
293 fixed_len_byte_array_to_string(val.min_opt()),
294 fixed_len_byte_array_to_string(val.max_opt()),
295 ),
296 (Statistics::FixedLenByteArray(val), _) => (
297 val.min_opt().map(|v| v.to_string()),
298 val.max_opt().map(|v| v.to_string()),
299 ),
300 }
301}
302
303fn byte_array_to_string(val: Option<&ByteArray>) -> Option<String> {
305 val.map(|v| {
306 v.as_utf8()
307 .map(|s| s.to_string())
308 .unwrap_or_else(|_e| v.to_string())
309 })
310}
311
312fn fixed_len_byte_array_to_string(val: Option<&FixedLenByteArray>) -> Option<String> {
314 val.map(|v| {
315 v.as_utf8()
316 .map(|s| s.to_string())
317 .unwrap_or_else(|_e| v.to_string())
318 })
319}
320
321#[derive(Debug)]
322pub struct ParquetMetadataFunc {}
323
324impl TableFunctionImpl for ParquetMetadataFunc {
325 fn call_with_args(&self, args: TableFunctionArgs) -> Result<Arc<dyn TableProvider>> {
326 let exprs = args.exprs();
327 let filename = match exprs.first() {
328 Some(Expr::Literal(ScalarValue::Utf8(Some(s)), _)) => s, Some(Expr::Column(Column { name, .. })) => name, _ => {
331 return plan_err!(
332 "parquet_metadata requires string argument as its input"
333 );
334 }
335 };
336
337 let file = File::open(filename.clone())?;
338 let reader = SerializedFileReader::new(file)?;
339 let metadata = reader.metadata();
340
341 let schema = Arc::new(Schema::new(vec![
342 Field::new("filename", DataType::Utf8, true),
343 Field::new("row_group_id", DataType::Int64, true),
344 Field::new("row_group_num_rows", DataType::Int64, true),
345 Field::new("row_group_num_columns", DataType::Int64, true),
346 Field::new("row_group_bytes", DataType::Int64, true),
347 Field::new("column_id", DataType::Int64, true),
348 Field::new("file_offset", DataType::Int64, true),
349 Field::new("num_values", DataType::Int64, true),
350 Field::new("path_in_schema", DataType::Utf8, true),
351 Field::new("type", DataType::Utf8, true),
352 Field::new("stats_min", DataType::Utf8, true),
353 Field::new("stats_max", DataType::Utf8, true),
354 Field::new("stats_null_count", DataType::Int64, true),
355 Field::new("stats_distinct_count", DataType::Int64, true),
356 Field::new("stats_min_value", DataType::Utf8, true),
357 Field::new("stats_max_value", DataType::Utf8, true),
358 Field::new("compression", DataType::Utf8, true),
359 Field::new("encodings", DataType::Utf8, true),
360 Field::new("index_page_offset", DataType::Int64, true),
361 Field::new("dictionary_page_offset", DataType::Int64, true),
362 Field::new("data_page_offset", DataType::Int64, true),
363 Field::new("total_compressed_size", DataType::Int64, true),
364 Field::new("total_uncompressed_size", DataType::Int64, true),
365 ]));
366
367 let mut filename_arr = vec![];
369 let mut row_group_id_arr = vec![];
370 let mut row_group_num_rows_arr = vec![];
371 let mut row_group_num_columns_arr = vec![];
372 let mut row_group_bytes_arr = vec![];
373 let mut column_id_arr = vec![];
374 let mut file_offset_arr = vec![];
375 let mut num_values_arr = vec![];
376 let mut path_in_schema_arr = vec![];
377 let mut type_arr = vec![];
378 let mut stats_min_arr = vec![];
379 let mut stats_max_arr = vec![];
380 let mut stats_null_count_arr = vec![];
381 let mut stats_distinct_count_arr = vec![];
382 let mut stats_min_value_arr = vec![];
383 let mut stats_max_value_arr = vec![];
384 let mut compression_arr = vec![];
385 let mut encodings_arr = vec![];
386 let mut index_page_offset_arr = vec![];
387 let mut dictionary_page_offset_arr = vec![];
388 let mut data_page_offset_arr = vec![];
389 let mut total_compressed_size_arr = vec![];
390 let mut total_uncompressed_size_arr = vec![];
391 for (rg_idx, row_group) in metadata.row_groups().iter().enumerate() {
392 for (col_idx, column) in row_group.columns().iter().enumerate() {
393 filename_arr.push(filename.clone());
394 row_group_id_arr.push(rg_idx as i64);
395 row_group_num_rows_arr.push(row_group.num_rows());
396 row_group_num_columns_arr.push(row_group.num_columns() as i64);
397 row_group_bytes_arr.push(row_group.total_byte_size());
398 column_id_arr.push(col_idx as i64);
399 file_offset_arr.push(column.file_offset());
400 num_values_arr.push(column.num_values());
401 path_in_schema_arr.push(column.column_path().to_string());
402 type_arr.push(column.column_type().to_string());
403 let converted_type = column.column_descr().converted_type();
404
405 if let Some(s) = column.statistics() {
406 let (min_val, max_val) =
407 convert_parquet_statistics(s, converted_type);
408 stats_min_arr.push(min_val.clone());
409 stats_max_arr.push(max_val.clone());
410 stats_null_count_arr.push(s.null_count_opt().map(|c| c as i64));
411 stats_distinct_count_arr
412 .push(s.distinct_count_opt().map(|c| c as i64));
413 stats_min_value_arr.push(min_val);
414 stats_max_value_arr.push(max_val);
415 } else {
416 stats_min_arr.push(None);
417 stats_max_arr.push(None);
418 stats_null_count_arr.push(None);
419 stats_distinct_count_arr.push(None);
420 stats_min_value_arr.push(None);
421 stats_max_value_arr.push(None);
422 };
423 compression_arr.push(format!("{:?}", column.compression()));
424 let encodings: Vec<_> = column.encodings().collect();
426 encodings_arr.push(format!("{encodings:?}"));
427 index_page_offset_arr.push(column.index_page_offset());
428 dictionary_page_offset_arr.push(column.dictionary_page_offset());
429 data_page_offset_arr.push(column.data_page_offset());
430 total_compressed_size_arr.push(column.compressed_size());
431 total_uncompressed_size_arr.push(column.uncompressed_size());
432 }
433 }
434
435 let rb = RecordBatch::try_new(
436 schema.clone(),
437 vec![
438 Arc::new(StringArray::from(filename_arr)),
439 Arc::new(Int64Array::from(row_group_id_arr)),
440 Arc::new(Int64Array::from(row_group_num_rows_arr)),
441 Arc::new(Int64Array::from(row_group_num_columns_arr)),
442 Arc::new(Int64Array::from(row_group_bytes_arr)),
443 Arc::new(Int64Array::from(column_id_arr)),
444 Arc::new(Int64Array::from(file_offset_arr)),
445 Arc::new(Int64Array::from(num_values_arr)),
446 Arc::new(StringArray::from(path_in_schema_arr)),
447 Arc::new(StringArray::from(type_arr)),
448 Arc::new(StringArray::from(stats_min_arr)),
449 Arc::new(StringArray::from(stats_max_arr)),
450 Arc::new(Int64Array::from(stats_null_count_arr)),
451 Arc::new(Int64Array::from(stats_distinct_count_arr)),
452 Arc::new(StringArray::from(stats_min_value_arr)),
453 Arc::new(StringArray::from(stats_max_value_arr)),
454 Arc::new(StringArray::from(compression_arr)),
455 Arc::new(StringArray::from(encodings_arr)),
456 Arc::new(Int64Array::from(index_page_offset_arr)),
457 Arc::new(Int64Array::from(dictionary_page_offset_arr)),
458 Arc::new(Int64Array::from(data_page_offset_arr)),
459 Arc::new(Int64Array::from(total_compressed_size_arr)),
460 Arc::new(Int64Array::from(total_uncompressed_size_arr)),
461 ],
462 )?;
463
464 let parquet_metadata = ParquetMetadataTable { schema, batch: rb };
465 Ok(Arc::new(parquet_metadata))
466 }
467}
468
469#[derive(Debug)]
471struct MetadataCacheTable {
472 schema: SchemaRef,
473 batch: RecordBatch,
474}
475
476#[async_trait]
477impl TableProvider for MetadataCacheTable {
478 fn schema(&self) -> SchemaRef {
479 self.schema.clone()
480 }
481
482 fn table_type(&self) -> datafusion::logical_expr::TableType {
483 datafusion::logical_expr::TableType::Base
484 }
485
486 async fn scan(
487 &self,
488 _state: &dyn Session,
489 projection: Option<&Vec<usize>>,
490 _filters: &[Expr],
491 _limit: Option<usize>,
492 ) -> Result<Arc<dyn ExecutionPlan>> {
493 Ok(MemorySourceConfig::try_new_exec(
494 &[vec![self.batch.clone()]],
495 TableProvider::schema(self),
496 projection.cloned(),
497 )?)
498 }
499}
500
501#[derive(Debug)]
502pub struct MetadataCacheFunc {
503 cache_manager: Arc<CacheManager>,
504}
505
506impl MetadataCacheFunc {
507 pub fn new(cache_manager: Arc<CacheManager>) -> Self {
508 Self { cache_manager }
509 }
510}
511
512impl TableFunctionImpl for MetadataCacheFunc {
513 fn call_with_args(&self, args: TableFunctionArgs) -> Result<Arc<dyn TableProvider>> {
514 let exprs = args.exprs();
515 if !exprs.is_empty() {
516 return plan_err!("metadata_cache should have no arguments");
517 }
518
519 let schema = Arc::new(Schema::new(vec![
520 Field::new("path", DataType::Utf8, false),
521 Field::new(
522 "file_modified",
523 DataType::Timestamp(TimeUnit::Millisecond, None),
524 false,
525 ),
526 Field::new("file_size_bytes", DataType::UInt64, false),
527 Field::new("e_tag", DataType::Utf8, true),
528 Field::new("version", DataType::Utf8, true),
529 Field::new("metadata_size_bytes", DataType::UInt64, false),
530 Field::new("hits", DataType::UInt64, false),
531 Field::new("extra", DataType::Utf8, true),
532 ]));
533
534 let mut path_arr = vec![];
536 let mut file_modified_arr = vec![];
537 let mut file_size_bytes_arr = vec![];
538 let mut e_tag_arr = vec![];
539 let mut version_arr = vec![];
540 let mut metadata_size_bytes = vec![];
541 let mut hits_arr = vec![];
542 let mut extra_arr = vec![];
543
544 let cached_entries = self.cache_manager.get_file_metadata_cache().list_entries();
545
546 for (path, entry) in cached_entries {
547 path_arr.push(path.to_string());
548 file_modified_arr
549 .push(Some(entry.object_meta.last_modified.timestamp_millis()));
550 file_size_bytes_arr.push(entry.object_meta.size);
551 e_tag_arr.push(entry.object_meta.e_tag);
552 version_arr.push(entry.object_meta.version);
553 metadata_size_bytes.push(entry.size_bytes as u64);
554 hits_arr.push(entry.hits as u64);
555
556 let mut extra = entry
557 .extra
558 .iter()
559 .map(|(k, v)| format!("{k}={v}"))
560 .collect::<Vec<_>>();
561 extra.sort();
562 extra_arr.push(extra.join(" "));
563 }
564
565 let batch = RecordBatch::try_new(
566 schema.clone(),
567 vec![
568 Arc::new(StringArray::from(path_arr)),
569 Arc::new(TimestampMillisecondArray::from(file_modified_arr)),
570 Arc::new(UInt64Array::from(file_size_bytes_arr)),
571 Arc::new(StringArray::from(e_tag_arr)),
572 Arc::new(StringArray::from(version_arr)),
573 Arc::new(UInt64Array::from(metadata_size_bytes)),
574 Arc::new(UInt64Array::from(hits_arr)),
575 Arc::new(StringArray::from(extra_arr)),
576 ],
577 )?;
578
579 let metadata_cache = MetadataCacheTable { schema, batch };
580 Ok(Arc::new(metadata_cache))
581 }
582}
583
584#[derive(Debug)]
586struct StatisticsCacheTable {
587 schema: SchemaRef,
588 batch: RecordBatch,
589}
590
591#[async_trait]
592impl TableProvider for StatisticsCacheTable {
593 fn schema(&self) -> SchemaRef {
594 self.schema.clone()
595 }
596
597 fn table_type(&self) -> datafusion::logical_expr::TableType {
598 datafusion::logical_expr::TableType::Base
599 }
600
601 async fn scan(
602 &self,
603 _state: &dyn Session,
604 projection: Option<&Vec<usize>>,
605 _filters: &[Expr],
606 _limit: Option<usize>,
607 ) -> Result<Arc<dyn ExecutionPlan>> {
608 Ok(MemorySourceConfig::try_new_exec(
609 &[vec![self.batch.clone()]],
610 TableProvider::schema(self),
611 projection.cloned(),
612 )?)
613 }
614}
615
616#[derive(Debug)]
617pub struct StatisticsCacheFunc {
618 cache_manager: Arc<CacheManager>,
619}
620
621impl StatisticsCacheFunc {
622 pub fn new(cache_manager: Arc<CacheManager>) -> Self {
623 Self { cache_manager }
624 }
625}
626
627impl TableFunctionImpl for StatisticsCacheFunc {
628 fn call_with_args(&self, args: TableFunctionArgs) -> Result<Arc<dyn TableProvider>> {
629 let exprs = args.exprs();
630 if !exprs.is_empty() {
631 return plan_err!("statistics_cache should have no arguments");
632 }
633
634 let schema = Arc::new(Schema::new(vec![
635 Field::new("path", DataType::Utf8, false),
636 Field::new("table", DataType::Utf8, false),
637 Field::new(
638 "file_modified",
639 DataType::Timestamp(TimeUnit::Millisecond, None),
640 false,
641 ),
642 Field::new("file_size_bytes", DataType::UInt64, false),
643 Field::new("e_tag", DataType::Utf8, true),
644 Field::new("version", DataType::Utf8, true),
645 Field::new("num_rows", DataType::Utf8, false),
646 Field::new("num_columns", DataType::UInt64, false),
647 Field::new("table_size_bytes", DataType::Utf8, false),
648 Field::new("statistics_size_bytes", DataType::UInt64, false),
649 ]));
650
651 let mut path_arr = vec![];
653 let mut table_arr = vec![];
654 let mut file_modified_arr = vec![];
655 let mut file_size_bytes_arr = vec![];
656 let mut e_tag_arr = vec![];
657 let mut version_arr = vec![];
658 let mut num_rows_arr = vec![];
659 let mut num_columns_arr = vec![];
660 let mut table_size_bytes_arr = vec![];
661 let mut statistics_size_bytes_arr = vec![];
662
663 if let Some(file_statistics_cache) = self.cache_manager.get_file_statistic_cache()
664 {
665 for (path, entry) in file_statistics_cache.list_entries() {
666 path_arr.push(path.path.to_string());
667 table_arr
668 .push(path.table.map_or_else(|| "".to_string(), |t| t.to_string()));
669 file_modified_arr
670 .push(Some(entry.object_meta.last_modified.timestamp_millis()));
671 file_size_bytes_arr.push(entry.object_meta.size);
672 e_tag_arr.push(entry.object_meta.e_tag);
673 version_arr.push(entry.object_meta.version);
674 num_rows_arr.push(entry.num_rows.to_string());
675 num_columns_arr.push(entry.num_columns as u64);
676 table_size_bytes_arr.push(entry.table_size_bytes.to_string());
677 statistics_size_bytes_arr.push(entry.statistics_size_bytes as u64);
678 }
679 }
680
681 let batch = RecordBatch::try_new(
682 schema.clone(),
683 vec![
684 Arc::new(StringArray::from(path_arr)),
685 Arc::new(StringArray::from(table_arr)),
686 Arc::new(TimestampMillisecondArray::from(file_modified_arr)),
687 Arc::new(UInt64Array::from(file_size_bytes_arr)),
688 Arc::new(StringArray::from(e_tag_arr)),
689 Arc::new(StringArray::from(version_arr)),
690 Arc::new(StringArray::from(num_rows_arr)),
691 Arc::new(UInt64Array::from(num_columns_arr)),
692 Arc::new(StringArray::from(table_size_bytes_arr)),
693 Arc::new(UInt64Array::from(statistics_size_bytes_arr)),
694 ],
695 )?;
696
697 let statistics_cache = StatisticsCacheTable { schema, batch };
698 Ok(Arc::new(statistics_cache))
699 }
700}
701
702#[derive(Debug)]
723struct ListFilesCacheTable {
724 schema: SchemaRef,
725 batch: RecordBatch,
726}
727
728#[async_trait]
729impl TableProvider for ListFilesCacheTable {
730 fn schema(&self) -> SchemaRef {
731 self.schema.clone()
732 }
733
734 fn table_type(&self) -> datafusion::logical_expr::TableType {
735 datafusion::logical_expr::TableType::Base
736 }
737
738 async fn scan(
739 &self,
740 _state: &dyn Session,
741 projection: Option<&Vec<usize>>,
742 _filters: &[Expr],
743 _limit: Option<usize>,
744 ) -> Result<Arc<dyn ExecutionPlan>> {
745 Ok(MemorySourceConfig::try_new_exec(
746 &[vec![self.batch.clone()]],
747 TableProvider::schema(self),
748 projection.cloned(),
749 )?)
750 }
751}
752
753#[derive(Debug)]
754pub struct ListFilesCacheFunc {
755 cache_manager: Arc<CacheManager>,
756}
757
758impl ListFilesCacheFunc {
759 pub fn new(cache_manager: Arc<CacheManager>) -> Self {
760 Self { cache_manager }
761 }
762}
763
764impl TableFunctionImpl for ListFilesCacheFunc {
765 fn call_with_args(&self, args: TableFunctionArgs) -> Result<Arc<dyn TableProvider>> {
766 let exprs = args.exprs();
767 if !exprs.is_empty() {
768 return plan_err!("list_files_cache should have no arguments");
769 }
770
771 let nested_fields = Fields::from(vec![
772 Field::new("file_path", DataType::Utf8, false),
773 Field::new(
774 "file_modified",
775 DataType::Timestamp(TimeUnit::Millisecond, None),
776 false,
777 ),
778 Field::new("file_size_bytes", DataType::UInt64, false),
779 Field::new("e_tag", DataType::Utf8, true),
780 Field::new("version", DataType::Utf8, true),
781 ]);
782
783 let metadata_field =
784 Field::new("metadata", DataType::Struct(nested_fields.clone()), true);
785
786 let schema = Arc::new(Schema::new(vec![
787 Field::new("table", DataType::Utf8, true),
788 Field::new("path", DataType::Utf8, false),
789 Field::new("metadata_size_bytes", DataType::UInt64, false),
790 Field::new(
792 "expires_in",
793 DataType::Duration(TimeUnit::Millisecond),
794 true,
795 ),
796 Field::new(
797 "metadata_list",
798 DataType::List(Arc::new(metadata_field.clone())),
799 true,
800 ),
801 ]));
802
803 let mut table_arr = vec![];
804 let mut path_arr = vec![];
805 let mut metadata_size_bytes_arr = vec![];
806 let mut expires_arr = vec![];
807
808 let mut file_path_arr = vec![];
809 let mut file_modified_arr = vec![];
810 let mut file_size_bytes_arr = vec![];
811 let mut etag_arr = vec![];
812 let mut version_arr = vec![];
813 let mut offsets: Vec<i32> = vec![0];
814
815 if let Some(list_files_cache) = self.cache_manager.get_list_files_cache() {
816 let now = Instant::now();
817 let mut current_offset: i32 = 0;
818
819 for (path, entry) in list_files_cache.list_entries() {
820 table_arr.push(path.table.map(|t| t.to_string()));
821 path_arr.push(path.path.to_string());
822 metadata_size_bytes_arr.push(entry.size_bytes as u64);
823 expires_arr.push(
825 entry
826 .expires
827 .map(|t| t.duration_since(now).as_millis() as i64),
828 );
829
830 for meta in entry.metas.files.iter() {
831 file_path_arr.push(meta.location.to_string());
832 file_modified_arr.push(meta.last_modified.timestamp_millis());
833 file_size_bytes_arr.push(meta.size);
834 etag_arr.push(meta.e_tag.clone());
835 version_arr.push(meta.version.clone());
836 }
837 current_offset += entry.metas.files.len() as i32;
838 offsets.push(current_offset);
839 }
840 }
841
842 let struct_arr = StructArray::new(
843 nested_fields,
844 vec![
845 Arc::new(StringArray::from(file_path_arr)),
846 Arc::new(TimestampMillisecondArray::from(file_modified_arr)),
847 Arc::new(UInt64Array::from(file_size_bytes_arr)),
848 Arc::new(StringArray::from(etag_arr)),
849 Arc::new(StringArray::from(version_arr)),
850 ],
851 None,
852 );
853
854 let offsets_buffer: OffsetBuffer<i32> =
855 OffsetBuffer::new(ScalarBuffer::from(Buffer::from_vec(offsets)));
856
857 let batch = RecordBatch::try_new(
858 schema.clone(),
859 vec![
860 Arc::new(StringArray::from(table_arr)),
861 Arc::new(StringArray::from(path_arr)),
862 Arc::new(UInt64Array::from(metadata_size_bytes_arr)),
863 Arc::new(DurationMillisecondArray::from(expires_arr)),
864 Arc::new(GenericListArray::new(
865 Arc::new(metadata_field),
866 offsets_buffer,
867 Arc::new(struct_arr),
868 None,
869 )),
870 ],
871 )?;
872
873 let list_files_cache = ListFilesCacheTable { schema, batch };
874 Ok(Arc::new(list_files_cache))
875 }
876}