1use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17
18use common_recordbatch::SendableRecordBatchStream;
19use datafusion::execution::FunctionRegistry;
20use datafusion::logical_expr::Cast;
21use datafusion::logical_expr::expr::ScalarFunction;
22use datafusion::physical_plan::ExecutionPlan;
23use datafusion::prelude::SessionContext;
24use datafusion_expr::expr::Expr;
25use datatypes::data_type::DataType;
26use datatypes::prelude::ConcreteDataType;
27use datatypes::schema::constraint::{CURRENT_TIMESTAMP, CURRENT_TIMESTAMP_FN, NOW_FN};
28use datatypes::schema::{ColumnDefaultConstraint, ColumnSchema, SchemaRef};
29use lazy_static::lazy_static;
30use snafu::ResultExt;
31use store_api::data_source::DataSourceRef;
32use store_api::storage::ScanRequest;
33
34use crate::error::{Result, TablesRecordBatchSnafu};
35use crate::metadata::{FilterPushDownType, TableInfoRef, TableType};
36
37pub mod adapter;
38mod metrics;
39pub mod numbers;
40pub mod scan;
41
42lazy_static! {
43 static ref NOW_EXPR: Expr = {
45 let ctx = SessionContext::new();
46
47 let now_udf = ctx.udf("now").expect("now UDF not found");
48
49 Expr::ScalarFunction(ScalarFunction {
50 func: now_udf,
51 args: vec![],
52 })
53 };
54}
55
56pub struct PartitionRules {
59 pub extra_phy_cols_not_in_logical_table: Vec<String>,
64}
65
66pub type TableRef = Arc<Table>;
67
68pub struct Table {
70 table_info: TableInfoRef,
71 filter_pushdown: FilterPushDownType,
72 data_source: DataSourceRef,
73 column_defaults: HashMap<String, Expr>,
75 partition_rules: Option<PartitionRules>,
76}
77
78impl Table {
79 pub fn new(
80 table_info: TableInfoRef,
81 filter_pushdown: FilterPushDownType,
82 data_source: DataSourceRef,
83 ) -> Self {
84 Self {
85 column_defaults: collect_column_defaults(table_info.meta.schema.column_schemas()),
86 table_info,
87 filter_pushdown,
88 data_source,
89 partition_rules: None,
90 }
91 }
92
93 pub fn new_partitioned(
94 table_info: TableInfoRef,
95 filter_pushdown: FilterPushDownType,
96 data_source: DataSourceRef,
97 partition_rules: Option<PartitionRules>,
98 ) -> Self {
99 Self {
100 column_defaults: collect_column_defaults(table_info.meta.schema.column_schemas()),
101 table_info,
102 filter_pushdown,
103 data_source,
104 partition_rules,
105 }
106 }
107
108 pub fn get_column_default(&self, column: &str) -> Option<&Expr> {
110 self.column_defaults.get(column)
111 }
112
113 pub fn data_source(&self) -> DataSourceRef {
114 self.data_source.clone()
115 }
116
117 pub fn schema(&self) -> SchemaRef {
119 self.table_info.meta.schema.clone()
120 }
121
122 pub fn schema_ref(&self) -> &SchemaRef {
123 &self.table_info.meta.schema
124 }
125
126 pub fn table_info(&self) -> TableInfoRef {
128 self.table_info.clone()
129 }
130
131 pub fn table_type(&self) -> TableType {
133 self.table_info.table_type
134 }
135
136 pub fn partition_rules(&self) -> Option<&PartitionRules> {
137 self.partition_rules.as_ref()
138 }
139
140 pub async fn scan_to_stream(&self, request: ScanRequest) -> Result<SendableRecordBatchStream> {
141 self.data_source
142 .get_stream(request)
143 .context(TablesRecordBatchSnafu)
144 }
145
146 pub fn scan_to_plan(&self, request: ScanRequest) -> Result<Option<Arc<dyn ExecutionPlan>>> {
147 self.data_source
148 .get_physical_plan(request)
149 .context(TablesRecordBatchSnafu)
150 }
151
152 pub fn supports_filters_pushdown(&self, filters: &[&Expr]) -> Result<Vec<FilterPushDownType>> {
155 Ok(vec![self.filter_pushdown; filters.len()])
156 }
157
158 pub fn primary_key_columns(&self) -> impl Iterator<Item = ColumnSchema> + '_ {
160 self.table_info
161 .meta
162 .primary_key_indices
163 .iter()
164 .map(|i| self.table_info.meta.schema.column_schemas()[*i].clone())
165 }
166
167 pub fn field_columns(&self) -> impl Iterator<Item = ColumnSchema> + '_ {
169 let primary_keys = self
171 .table_info
172 .meta
173 .primary_key_indices
174 .iter()
175 .copied()
176 .collect::<HashSet<_>>();
177
178 self.table_info
179 .meta
180 .schema
181 .column_schemas()
182 .iter()
183 .enumerate()
184 .filter(move |(i, c)| !primary_keys.contains(i) && !c.is_time_index())
185 .map(|(_, c)| c.clone())
186 }
187}
188
189fn collect_column_defaults(column_schemas: &[ColumnSchema]) -> HashMap<String, Expr> {
191 column_schemas
192 .iter()
193 .filter_map(|column_schema| {
194 default_constraint_to_expr(
195 column_schema.default_constraint()?,
196 &column_schema.data_type,
197 )
198 .map(|expr| (column_schema.name.clone(), expr))
199 })
200 .collect()
201}
202
203fn default_constraint_to_expr(
205 default_constraint: &ColumnDefaultConstraint,
206 target_type: &ConcreteDataType,
207) -> Option<Expr> {
208 match default_constraint {
209 ColumnDefaultConstraint::Value(v) => v
210 .try_to_scalar_value(target_type)
211 .ok()
212 .map(|x| Expr::Literal(x, None)),
213
214 ColumnDefaultConstraint::Function(name)
215 if matches!(
216 name.as_str(),
217 CURRENT_TIMESTAMP | CURRENT_TIMESTAMP_FN | NOW_FN
218 ) =>
219 {
220 Some(Expr::Cast(Cast {
221 expr: Box::new(NOW_EXPR.clone()),
222 data_type: target_type.as_arrow_type(),
223 }))
224 }
225
226 ColumnDefaultConstraint::Function(_) => None,
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use datafusion_common::ScalarValue;
233 use datatypes::prelude::ConcreteDataType;
234 use datatypes::schema::ColumnDefaultConstraint;
235
236 use super::*;
237
238 #[test]
239 fn test_collect_columns_defaults() {
240 let column_schemas = [
241 ColumnSchema::new("col1", ConcreteDataType::int32_datatype(), false),
242 ColumnSchema::new("col2", ConcreteDataType::string_datatype(), true)
243 .with_default_constraint(Some(ColumnDefaultConstraint::Value("test".into())))
244 .unwrap(),
245 ColumnSchema::new(
246 "ts",
247 ConcreteDataType::timestamp_millisecond_datatype(),
248 false,
249 )
250 .with_time_index(true)
251 .with_default_constraint(Some(ColumnDefaultConstraint::Function(
252 "current_timestamp".to_string(),
253 )))
254 .unwrap(),
255 ];
256 let column_defaults = collect_column_defaults(&column_schemas[..]);
257
258 assert!(!column_defaults.contains_key("col1"));
259 assert!(matches!(column_defaults.get("col2").unwrap(),
260 Expr::Literal(ScalarValue::Utf8(Some(s)), _) if s == "test"));
261 assert!(matches!(
262 column_defaults.get("ts").unwrap(),
263 Expr::Cast(Cast {
264 expr,
265 data_type
266 }) if **expr == *NOW_EXPR && *data_type == ConcreteDataType::timestamp_millisecond_datatype().as_arrow_type()
267 ));
268 }
269}