Path: blob/main/crates/polars-plan/src/plans/optimizer/projection_pushdown/projection.rs
7889 views
use super::*;12#[inline]3pub(super) fn is_count(node: Node, expr_arena: &Arena<AExpr>) -> bool {4matches!(expr_arena.get(node), AExpr::Len)5}67#[allow(clippy::too_many_arguments)]8pub(super) fn process_projection(9proj_pd: &mut ProjectionPushDown,10input: Node,11mut exprs: Vec<ExprIR>,12mut ctx: ProjectionContext,13lp_arena: &mut Arena<IR>,14expr_arena: &mut Arena<AExpr>,15// Whether is SimpleProjection.16simple: bool,17) -> PolarsResult<IR> {18let mut local_projection = Vec::with_capacity(exprs.len());1920// Special path for `SELECT count(*) FROM`21// as there would be no projections and we would read22// the whole file while we only want the count23if exprs.len() == 1 && is_count(exprs[0].node(), expr_arena) {24// Clear all accumulated projections since we only project a single column from this level.25ctx.acc_projections.clear();26ctx.projected_names.clear();2728let input_lp = lp_arena.get(input);2930// If the input node is not aware of `is_count_star` we must project a single column from31// this level, otherwise the upstream nodes may end up projecting everything.32let input_is_count_star_aware = match input_lp {33IR::DataFrameScan { .. } | IR::Scan { .. } => true,34#[cfg(feature = "python")]35IR::PythonScan { .. } => true,36_ => false,37};3839if !input_is_count_star_aware {40if let Some(name) = input_lp41.schema(lp_arena)42.get_at_index(0)43.map(|(name, _)| name)44{45ctx.acc_projections46.push(ColumnNode(expr_arena.add(AExpr::Column(name.clone()))));47ctx.projected_names.insert(name.clone());48}49}5051local_projection.push(exprs.pop().unwrap());5253if input_is_count_star_aware {54ctx.inner.is_count_star = true;55proj_pd.is_count_star = true;56}57} else {58// `remove_names` tracks projected names that need to be removed as they may be aliased59// names that are created on this level.60let mut remove_names = PlHashSet::new();6162// If there are non-scalar projections we must project at least one of them to maintain the63// output height.64let mut opt_non_scalar = None;65let mut projection_has_non_scalar = false;6667let projected_exprs: Vec<ExprIR> = exprs68.into_iter()69.filter(|e| {70let is_non_scalar = !e.is_scalar(expr_arena);7172if opt_non_scalar.is_none() && is_non_scalar {73opt_non_scalar = Some(e.clone())74}7576let name = match e.output_name_inner() {77OutputName::LiteralLhs(name) | OutputName::Alias(name) => {78remove_names.insert(name.clone());79name80},81#[cfg(feature = "dtype-struct")]82OutputName::Field(name) => {83remove_names.insert(name.clone());84name85},86OutputName::ColumnLhs(name) => name,87OutputName::None => {88if cfg!(debug_assertions) {89panic!()90} else {91return false;92}93},94};9596let project = ctx.acc_projections.is_empty() || ctx.projected_names.contains(name);97projection_has_non_scalar |= project & is_non_scalar;98project99})100.collect();101102// Remove aliased before adding new ones.103if !remove_names.is_empty() {104if !ctx.projected_names.is_empty() {105for name in remove_names.iter() {106ctx.projected_names.remove(name);107}108}109110ctx.acc_projections111.retain(|c| !remove_names.contains(column_node_to_name(*c, expr_arena)));112}113114for e in projected_exprs {115add_expr_to_accumulated(116e.node(),117&mut ctx.acc_projections,118&mut ctx.projected_names,119expr_arena,120);121122// do local as we still need the effect of the projection123// e.g. a projection is more than selecting a column, it can124// also be a function/ complicated expression125local_projection.push(e);126}127128if !projection_has_non_scalar {129if let Some(non_scalar) = opt_non_scalar {130add_expr_to_accumulated(131non_scalar.node(),132&mut ctx.acc_projections,133&mut ctx.projected_names,134expr_arena,135);136137local_projection.push(non_scalar);138}139}140}141142ctx.inner.projections_seen += 1;143proj_pd.pushdown_and_assign(input, ctx, lp_arena, expr_arena)?;144145let builder = IRBuilder::new(input, expr_arena, lp_arena);146147let lp = if !local_projection.is_empty() && simple {148builder149.project_simple_nodes(local_projection.into_iter().map(|e| e.node()))?150.build()151} else {152proj_pd.finish_node(local_projection, builder)153};154155Ok(lp)156}157158159