use arrow::datatypes::DataType;
use datafusion::{
common::{DataFusionError, JoinConstraint, JoinType},
logical_expr::{
expr::{AggregateFunction, Placeholder},
Expr, Operator,
},
physical_plan,
};
use derive_more::Display;
use proof_of_sql::{
base::math::decimal::DecimalError,
sql::{proof_plans::AggregateExecError, AnalyzeError},
};
use snafu::Snafu;
use sqlparser::parser::ParserError;
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq, Snafu)]
pub enum AggregatePlanError {
#[snafu(display("group expression {expression} has no output alias"))]
MissingGroupExpressionAlias {
expression: String,
},
#[snafu(display("aggregate expression {expression} has no output alias"))]
MissingAggregateExpressionAlias {
expression: String,
},
#[snafu(display("aggregate expression list contains non-aggregate expression {expression}"))]
UnexpectedAggregateExpression {
expression: String,
},
#[snafu(transparent)]
AggregateExec {
source: AggregateExecError,
},
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq, Snafu)]
pub enum JoinPlanError {
#[snafu(display("join type {join_type} is not supported"))]
UnsupportedJoinType {
join_type: JoinType,
},
#[snafu(display("join constraint {constraint:?} is not supported"))]
UnsupportedJoinConstraint {
constraint: JoinConstraint,
},
#[snafu(display(
"join predicate must pair supported columns with the same unqualified name, found {left} and {right}"
))]
UnsupportedPredicate {
left: String,
right: String,
},
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Display, Eq, PartialEq)]
pub enum LogicalPlanNodeKind {
#[display(fmt = "window")]
Window,
#[display(fmt = "sort")]
Sort,
#[display(fmt = "cross join")]
CrossJoin,
#[display(fmt = "repartition")]
Repartition,
#[display(fmt = "table scan")]
TableScan,
#[display(fmt = "subquery")]
Subquery,
#[display(fmt = "statement")]
Statement,
#[display(fmt = "values")]
Values,
#[display(fmt = "explain")]
Explain,
#[display(fmt = "analyze")]
Analyze,
#[display(fmt = "extension")]
Extension,
#[display(fmt = "distinct")]
Distinct,
#[display(fmt = "prepare")]
Prepare,
#[display(fmt = "data manipulation")]
Dml,
#[display(fmt = "data definition")]
Ddl,
#[display(fmt = "copy")]
Copy,
#[display(fmt = "describe table")]
DescribeTable,
#[display(fmt = "unnest")]
Unnest,
#[display(fmt = "recursive query")]
RecursiveQuery,
}
#[non_exhaustive]
#[derive(Debug, Snafu)]
pub enum PlannerError {
#[snafu(transparent)]
AnalyzeError {
source: AnalyzeError,
},
#[snafu(transparent)]
DecimalError {
source: DecimalError,
},
#[snafu(transparent)]
SqlParserError {
source: ParserError,
},
#[snafu(transparent)]
DataFusionError {
source: DataFusionError,
},
#[snafu(display("Column not found"))]
ColumnNotFound,
#[snafu(display("Table not found: {}", table_name))]
TableNotFound {
table_name: String,
},
#[snafu(display("Placeholder id {id:?} is invalid"))]
InvalidPlaceholderId {
id: String,
},
#[snafu(display("Placeholder {placeholder:?} is untyped"))]
UntypedPlaceholder {
placeholder: Placeholder,
},
#[snafu(display("Unsupported datatype: {}", data_type))]
UnsupportedDataType {
data_type: DataType,
},
#[snafu(display("Binary operator {} is not supported", op))]
UnsupportedBinaryOperator {
op: Operator,
},
#[snafu(display("Aggregate operation {op:?} is not supported"))]
UnsupportedAggregateOperation {
op: physical_plan::aggregates::AggregateFunction,
},
#[snafu(display("AggregateFunction {function:?} is not supported"))]
UnsupportedAggregateFunction {
function: AggregateFunction,
},
#[snafu(display("Logical expression {:?} is not supported", expr))]
UnsupportedLogicalExpression {
expr: Box<Expr>,
},
#[snafu(
context(false),
display("Aggregate logical plan is not supported: {source}")
)]
UnsupportedAggregatePlan {
source: AggregatePlanError,
},
#[snafu(
context(false),
display("Join logical plan is not supported: {source}")
)]
UnsupportedJoinPlan {
source: JoinPlanError,
},
#[snafu(display("Logical plan node or shape {node} is not supported"))]
UnsupportedLogicalPlan {
node: LogicalPlanNodeKind,
},
#[snafu(display("LogicalPlan is not resolved"))]
UnresolvedLogicalPlan,
#[snafu(display("Catalog is not supported"))]
CatalogNotSupported,
}
pub type PlannerResult<T> = Result<T, PlannerError>;
#[cfg(test)]
mod tests {
use super::*;
use proof_of_sql::base::database::ColumnType;
#[test]
fn planner_sub_errors_include_actionable_context() {
assert_eq!(
AggregatePlanError::MissingGroupExpressionAlias {
expression: "table.id".to_string(),
}
.to_string(),
"group expression table.id has no output alias"
);
assert_eq!(
AggregatePlanError::AggregateExec {
source: AggregateExecError::UnsupportedGroupByExpressionType {
data_type: ColumnType::VarChar,
},
}
.to_string(),
"grouping and deduplication do not support expressions of type VARCHAR"
);
assert_eq!(
JoinPlanError::UnsupportedPredicate {
left: "left.a".to_string(),
right: "right.b".to_string(),
}
.to_string(),
"join predicate must pair supported columns with the same unqualified name, found left.a and right.b"
);
}
#[test]
fn logical_plan_node_kinds_have_human_readable_labels() {
let expected_labels = [
(LogicalPlanNodeKind::Window, "window"),
(LogicalPlanNodeKind::Sort, "sort"),
(LogicalPlanNodeKind::CrossJoin, "cross join"),
(LogicalPlanNodeKind::Repartition, "repartition"),
(LogicalPlanNodeKind::TableScan, "table scan"),
(LogicalPlanNodeKind::Subquery, "subquery"),
(LogicalPlanNodeKind::Statement, "statement"),
(LogicalPlanNodeKind::Values, "values"),
(LogicalPlanNodeKind::Explain, "explain"),
(LogicalPlanNodeKind::Analyze, "analyze"),
(LogicalPlanNodeKind::Extension, "extension"),
(LogicalPlanNodeKind::Distinct, "distinct"),
(LogicalPlanNodeKind::Prepare, "prepare"),
(LogicalPlanNodeKind::Dml, "data manipulation"),
(LogicalPlanNodeKind::Ddl, "data definition"),
(LogicalPlanNodeKind::Copy, "copy"),
(LogicalPlanNodeKind::DescribeTable, "describe table"),
(LogicalPlanNodeKind::Unnest, "unnest"),
(LogicalPlanNodeKind::RecursiveQuery, "recursive query"),
];
for (node, expected_label) in expected_labels {
assert_eq!(node.to_string(), expected_label);
}
}
}