blob: 2784523e7748760792fdb9fbf30eb8a483ed06aa [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use datafusion_expr::logical_plan::TableScan;
use pyo3::prelude::*;
use std::fmt::{self, Display, Formatter};
use crate::expr::logical_node::LogicalNode;
use crate::sql::logical::PyLogicalPlan;
use crate::{common::df_schema::PyDFSchema, expr::PyExpr};
#[pyclass(name = "TableScan", module = "datafusion.expr", subclass)]
#[derive(Clone)]
pub struct PyTableScan {
table_scan: TableScan,
}
impl PyTableScan {
pub fn new(table_scan: TableScan) -> Self {
Self { table_scan }
}
}
impl From<PyTableScan> for TableScan {
fn from(tbl_scan: PyTableScan) -> TableScan {
tbl_scan.table_scan
}
}
impl From<TableScan> for PyTableScan {
fn from(table_scan: TableScan) -> PyTableScan {
PyTableScan { table_scan }
}
}
impl Display for PyTableScan {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"TableScan\nTable Name: {}
\nProjections: {:?}
\nProjected Schema: {:?}
\nFilters: {:?}",
&self.table_scan.table_name,
&self.py_projections(),
&self.py_schema(),
&self.py_filters(),
)
}
}
#[pymethods]
impl PyTableScan {
/// Retrieves the name of the table represented by this `TableScan` instance
#[pyo3(name = "table_name")]
fn py_table_name(&self) -> PyResult<&str> {
Ok(&self.table_scan.table_name)
}
/// TODO: Bindings for `TableSource` need to exist first. Left as a
/// placeholder to display intention to add when able to.
// #[pyo3(name = "source")]
// fn py_source(&self) -> PyResult<Arc<dyn TableSource>> {
// Ok(self.table_scan.source)
// }
/// The column indexes that should be. Note if this is empty then
/// all columns should be read by the `TableProvider`. This function
/// provides a Tuple of the (index, column_name) to make things simplier
/// for the calling code since often times the name is preferred to
/// the index which is a lower level abstraction.
#[pyo3(name = "projection")]
fn py_projections(&self) -> PyResult<Vec<(usize, String)>> {
match &self.table_scan.projection {
Some(indices) => {
let schema = self.table_scan.source.schema();
Ok(indices
.iter()
.map(|i| (*i, schema.field(*i).name().to_string()))
.collect())
}
None => Ok(vec![]),
}
}
/// Resulting schema from the `TableScan` operation
#[pyo3(name = "schema")]
fn py_schema(&self) -> PyResult<PyDFSchema> {
Ok((*self.table_scan.projected_schema).clone().into())
}
/// Certain `TableProvider` physical readers offer the capability to filter rows that
/// are read at read time. These `filters` are contained here.
#[pyo3(name = "filters")]
fn py_filters(&self) -> PyResult<Vec<PyExpr>> {
Ok(self
.table_scan
.filters
.iter()
.map(|expr| PyExpr::from(expr.clone()))
.collect())
}
/// Optional number of rows that should be read at read time by the `TableProvider`
#[pyo3(name = "fetch")]
fn py_fetch(&self) -> PyResult<Option<usize>> {
Ok(self.table_scan.fetch)
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("TableScan({})", self))
}
}
impl LogicalNode for PyTableScan {
fn input(&self) -> Vec<PyLogicalPlan> {
// table scans are leaf nodes and do not have inputs
vec![]
}
}