use std::sync::Arc;
use glaredb_error::Result;
use super::binder::bind_context::BindContext;
use super::binder::table_list::TableRef;
use super::operator::{LogicalNode, Node};
use crate::catalog::entry::CatalogEntry;
use crate::explain::explainable::{EntryBuilder, ExplainConfig, ExplainEntry, Explainable};
use crate::expr::Expression;
use crate::functions::table::PlannedTableFunction;
use crate::statistics::value::StatisticsValue;
use crate::storage::scan_filter::ScanFilter;
#[derive(Debug, Clone)]
pub struct TableScanSource {
pub catalog: String,
pub schema: String,
pub source: Arc<CatalogEntry>,
pub function: PlannedTableFunction,
}
impl PartialEq for TableScanSource {
fn eq(&self, other: &Self) -> bool {
self.catalog == other.catalog
&& self.schema == other.schema
&& self.source.name == other.source.name
}
}
impl Eq for TableScanSource {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TableFunctionScanSource {
pub function: PlannedTableFunction,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ScanSource {
Table(TableScanSource),
Function(TableFunctionScanSource),
}
impl ScanSource {
pub fn into_function(self) -> PlannedTableFunction {
match self {
Self::Table(t) => t.function,
Self::Function(f) => f.function,
}
}
pub fn cardinality(&self) -> StatisticsValue<usize> {
match self {
Self::Table(table) => table.function.bind_state.cardinality,
Self::Function(func) => func.function.bind_state.cardinality,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TableScan {
pub table_ref: TableRef,
pub projection: Vec<usize>,
pub scan_filters: Vec<ScanFilter>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LogicalScan {
pub data_scan: TableScan,
pub meta_scan: Option<TableScan>,
pub source: Box<ScanSource>,
}
impl Explainable for LogicalScan {
fn explain_entry(&self, conf: ExplainConfig) -> ExplainEntry {
let mut builder = EntryBuilder::new("Scan", conf)
.with_contextual_values("data_scan_filters", &self.data_scan.scan_filters)
.with_value_if_verbose("data_table_ref", self.data_scan.table_ref)
.with_values_if_verbose("data_projection", &self.data_scan.projection);
if let Some(meta_scan) = &self.meta_scan {
builder = builder
.with_contextual_values("meta_scan_filters", &meta_scan.scan_filters)
.with_value_if_verbose("meta_table_ref", meta_scan.table_ref)
.with_values_if_verbose("meta_projection", &meta_scan.projection);
}
match self.source.as_ref() {
ScanSource::Table(table) => {
builder = builder.with_value(
"table",
format!("{}.{}.{}", table.catalog, table.schema, table.source.name),
)
}
ScanSource::Function(func) => {
builder = builder.with_value("function", func.function.name.to_string())
}
}
builder.build()
}
}
impl LogicalNode for Node<LogicalScan> {
fn name(&self) -> &'static str {
"Scan"
}
fn get_output_table_refs(&self, _bind_context: &BindContext) -> Vec<TableRef> {
match &self.node.meta_scan {
Some(meta_scan) => {
vec![self.node.data_scan.table_ref, meta_scan.table_ref]
}
None => {
vec![self.node.data_scan.table_ref]
}
}
}
fn for_each_expr<'a, F>(&'a self, mut func: F) -> Result<()>
where
F: FnMut(&'a Expression) -> Result<()>,
{
if let ScanSource::Function(table_func) = self.node.source.as_ref() {
for expr in &table_func.function.bind_state.input.positional {
func(expr)?
}
}
for filter in &self.node.data_scan.scan_filters {
func(&filter.expression)?;
}
if let Some(meta_scan) = &self.node.meta_scan {
for filter in &meta_scan.scan_filters {
func(&filter.expression)?;
}
}
Ok(())
}
fn for_each_expr_mut<'a, F>(&'a mut self, mut func: F) -> Result<()>
where
F: FnMut(&'a mut Expression) -> Result<()>,
{
if let ScanSource::Function(table_func) = self.node.source.as_mut() {
for expr in &mut table_func.function.bind_state.input.positional {
func(expr)?
}
}
for filter in &mut self.node.data_scan.scan_filters {
func(&mut filter.expression)?;
}
if let Some(meta_scan) = &mut self.node.meta_scan {
for filter in &mut meta_scan.scan_filters {
func(&mut filter.expression)?;
}
}
Ok(())
}
}