Repository navigation
Expand file tree
/
Copy pathview.rs
More file actions
159 lines (140 loc) · 4.9 KB
/
Copy pathview.rs
File metadata and controls
159 lines (140 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: See the NOTICE file distributed with this work for additional information regarding copyright ownership
// SPDX-FileContributor: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements
//! View data source which uses a LogicalPlan as it's input.
use std::{borrow::Cow, sync::Arc};
use crate::Session;
use crate::TableProvider;
use arrow::datatypes::SchemaRef;
use async_trait::async_trait;
use datafusion_common::Column;
use datafusion_common::error::Result;
use datafusion_expr::TableType;
use datafusion_expr::{Expr, LogicalPlan};
use datafusion_expr::{LogicalPlanBuilder, TableProviderFilterPushDown};
use datafusion_physical_plan::ExecutionPlan;
use futures::future::BoxFuture;
/// An implementation of `TableProvider` that uses another logical plan.
#[derive(Debug)]
pub struct ViewTable {
/// LogicalPlan of the view
logical_plan: LogicalPlan,
/// File fields + partition columns
table_schema: SchemaRef,
/// SQL used to create the view, if available
definition: Option<String>,
}
impl ViewTable {
/// Create new view that is executed at query runtime.
///
/// Takes a `LogicalPlan` and optionally the SQL text of the `CREATE`
/// statement.
///
/// Notes: the `LogicalPlan` is not validated or type coerced. If this is
/// needed it should be done after calling this function.
pub fn new(logical_plan: LogicalPlan, definition: Option<String>) -> Self {
let table_schema = Arc::clone(logical_plan.schema().inner());
Self {
logical_plan,
table_schema,
definition,
}
}
/// Get definition ref
pub fn definition(&self) -> Option<&String> {
self.definition.as_ref()
}
/// Get logical_plan ref
pub fn logical_plan(&self) -> &LogicalPlan {
&self.logical_plan
}
}
#[async_trait]
impl TableProvider for ViewTable {
fn get_logical_plan(&'_ self) -> Option<Cow<'_, LogicalPlan>> {
Some(Cow::Borrowed(&self.logical_plan))
}
fn schema(&self) -> SchemaRef {
Arc::clone(&self.table_schema)
}
fn table_type(&self) -> TableType {
TableType::View
}
fn get_table_definition(&self) -> Option<&str> {
self.definition.as_deref()
}
fn supports_filters_pushdown(
&self,
filters: &[&Expr],
) -> Result<Vec<TableProviderFilterPushDown>> {
// A filter is added on the View when given
Ok(vec![TableProviderFilterPushDown::Exact; filters.len()])
}
// Hand-written `#[async_trait]` expansion to reduce compile time. See
// <https://github.com/apache/datafusion/issues/13814#issuecomment-5292709677>
fn scan<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
state: &'life1 dyn Session,
projection: Option<&'life2 [usize]>,
filters: &'life3 [Expr],
limit: Option<usize>,
) -> BoxFuture<'async_trait, Result<Arc<dyn ExecutionPlan>>>
where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Self: 'async_trait,
{
self.scan_boxed(state, projection, filters, limit)
}
}
impl ViewTable {
fn scan_boxed<'a>(
&'a self,
state: &'a dyn Session,
projection: Option<&'a [usize]>,
filters: &'a [Expr],
limit: Option<usize>,
) -> BoxFuture<'a, Result<Arc<dyn ExecutionPlan>>> {
Box::pin(self.scan_inner(state, projection, filters, limit))
}
async fn scan_inner(
&self,
state: &dyn Session,
projection: Option<&[usize]>,
filters: &[Expr],
limit: Option<usize>,
) -> Result<Arc<dyn ExecutionPlan>> {
let filter = filters.iter().cloned().reduce(|acc, new| acc.and(new));
let plan = self.logical_plan().clone();
let mut plan = LogicalPlanBuilder::from(plan);
if let Some(filter) = filter {
plan = plan.filter(filter)?;
}
let mut plan = if let Some(projection) = projection {
// avoiding adding a redundant projection (e.g. SELECT * FROM view)
let current_projection =
(0..plan.schema().fields().len()).collect::<Vec<usize>>();
if projection == current_projection {
plan
} else {
let fields: Vec<Expr> = projection
.iter()
.map(|i| {
Expr::Column(Column::from(
self.logical_plan.schema().qualified_field(*i),
))
})
.collect();
plan.project(fields)?
}
} else {
plan
};
if let Some(limit) = limit {
plan = plan.limit(0, Some(limit))?;
}
state.create_physical_plan(&plan.build()?).await
}
}