micromegas_analytics/lakehouse/
list_query_denials_table_function.rs1use super::query_deny_list::QueryDenyList;
5use async_trait::async_trait;
6use datafusion::arrow::array::{RecordBatch, StringArray, TimestampNanosecondArray};
7use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef, TimeUnit};
8use datafusion::catalog::Session;
9use datafusion::catalog::TableFunctionArgs;
10use datafusion::catalog::TableFunctionImpl;
11use datafusion::catalog::TableProvider;
12use datafusion::datasource::TableType;
13use datafusion::datasource::memory::{DataSourceExec, MemorySourceConfig};
14use datafusion::error::DataFusionError;
15use datafusion::physical_plan::ExecutionPlan;
16use datafusion::prelude::Expr;
17use std::sync::Arc;
18
19fn schema() -> SchemaRef {
20 Arc::new(Schema::new(vec![
21 Field::new("rule_id", DataType::Utf8, false),
22 Field::new(
23 "created_at",
24 DataType::Timestamp(TimeUnit::Nanosecond, Some("+00:00".into())),
25 false,
26 ),
27 Field::new("created_by", DataType::Utf8, false),
28 Field::new("reason", DataType::Utf8, false),
29 Field::new("match_expr", DataType::Utf8, false),
30 Field::new(
31 "last_hit_at",
32 DataType::Timestamp(TimeUnit::Nanosecond, Some("+00:00".into())),
33 true,
34 ),
35 ]))
36}
37
38#[derive(Debug)]
40pub struct ListQueryDenialsTableFunction {
41 query_denials: Arc<QueryDenyList>,
42}
43
44impl ListQueryDenialsTableFunction {
45 pub fn new(query_denials: Arc<QueryDenyList>) -> Self {
46 Self { query_denials }
47 }
48}
49
50impl TableFunctionImpl for ListQueryDenialsTableFunction {
51 fn call_with_args(
52 &self,
53 _args: TableFunctionArgs,
54 ) -> datafusion::error::Result<Arc<dyn TableProvider>> {
55 Ok(Arc::new(ListQueryDenialsTableProvider {
56 query_denials: self.query_denials.clone(),
57 }))
58 }
59}
60
61#[derive(Debug)]
62struct ListQueryDenialsTableProvider {
63 query_denials: Arc<QueryDenyList>,
64}
65
66#[async_trait]
67impl TableProvider for ListQueryDenialsTableProvider {
68 fn schema(&self) -> SchemaRef {
69 schema()
70 }
71
72 fn table_type(&self) -> TableType {
73 TableType::Temporary
74 }
75
76 async fn scan(
77 &self,
78 _state: &dyn Session,
79 projection: Option<&Vec<usize>>,
80 _filters: &[Expr],
81 limit: Option<usize>,
82 ) -> datafusion::error::Result<Arc<dyn ExecutionPlan>> {
83 let mut rows = self
84 .query_denials
85 .list()
86 .await
87 .map_err(|e| DataFusionError::External(e.into()))?;
88 if let Some(n) = limit {
89 rows.truncate(n);
90 }
91 let rule_id: StringArray = rows.iter().map(|r| Some(r.rule_id.to_string())).collect();
92 let created_at: TimestampNanosecondArray = rows
93 .iter()
94 .map(|r| r.created_at.timestamp_nanos_opt())
95 .collect::<TimestampNanosecondArray>()
96 .with_timezone("+00:00".to_string());
97 let created_by: StringArray = rows.iter().map(|r| Some(r.created_by.clone())).collect();
98 let reason: StringArray = rows.iter().map(|r| Some(r.reason.clone())).collect();
99 let match_expr: StringArray = rows.iter().map(|r| Some(r.match_expr.clone())).collect();
100 let last_hit_at: TimestampNanosecondArray = rows
101 .iter()
102 .map(|r| r.last_hit_at.and_then(|t| t.timestamp_nanos_opt()))
103 .collect::<TimestampNanosecondArray>()
104 .with_timezone("+00:00".to_string());
105 let rb = RecordBatch::try_new(
106 schema(),
107 vec![
108 Arc::new(rule_id),
109 Arc::new(created_at),
110 Arc::new(created_by),
111 Arc::new(reason),
112 Arc::new(match_expr),
113 Arc::new(last_hit_at),
114 ],
115 )?;
116 let source =
117 MemorySourceConfig::try_new(&[vec![rb]], schema(), projection.map(|v| v.to_owned()))?;
118 Ok(DataSourceExec::from_data_source(source))
119 }
120}