use super::{PlanNode, QueryPlan, ResultSink, SourceNode};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NativePlanCostReason {
NoColumnData,
HostLiteralPassthrough,
}
impl NativePlanCostReason {
pub const fn code(self) -> &'static str {
match self {
Self::NoColumnData => "native_no_column_data_not_selected_by_cost",
Self::HostLiteralPassthrough => "native_host_literal_passthrough_not_selected_by_cost",
}
}
}
pub fn native_plan_cost_decision(plan: &QueryPlan, frontend_plan_allowed: bool) -> Option<NativePlanCostReason> {
if !frontend_plan_allowed {
return None;
}
if reads_no_column_data(plan) {
return Some(NativePlanCostReason::NoColumnData);
}
if passes_host_literals_through(plan) {
return Some(NativePlanCostReason::HostLiteralPassthrough);
}
None
}
fn reads_no_column_data(plan: &QueryPlan) -> bool {
plan.nodes().iter().all(|node| match &node.kind {
PlanNode::Source(source) => matches!(
source.as_ref(),
SourceNode::LiteralBatch(literal)
if literal.schema.fields().is_empty() || literal.batches.iter().all(|batch| batch.num_rows() == 0)
),
PlanNode::GraphAlgorithm(_) | PlanNode::VectorAlgorithm(_) | PlanNode::MlAlgorithm(_) => false,
_ => true,
})
}
fn passes_host_literals_through(plan: &QueryPlan) -> bool {
!matches!(plan.sink(), ResultSink::GpuResident { .. })
&& plan.nodes().iter().all(|node| {
matches!(&node.kind, PlanNode::Source(source) if matches!(source.as_ref(), SourceNode::LiteralBatch(_)))
})
}