Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions misc/python/materialize/mzcompose/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def get_minimal_system_parameters(
"true" if version >= MzVersion.parse_mz("v0.132.0-dev") else "false"
),
"enable_alter_swap": "true",
"enable_cast_elimination": "true",
"enable_columnar_lgalloc": "false",
"enable_columnation_lgalloc": "false",
"enable_compute_correction_v2": "true",
Expand Down
12 changes: 12 additions & 0 deletions src/expr/src/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,18 @@ impl MirScalarExpr {
/* #endregion */
}

pub fn elimimate_noop_casts(&mut self) -> Result<(), RecursionLimitError> {
self.visit_mut_post(&mut |e| match e {
MirScalarExpr::CallUnary {
func: UnaryFunc::CastVarCharToString(_),
expr,
} => {
*e = expr.take();
}
_ => {}
})
}

/// Decompose an IsNull expression into a disjunction of
/// simpler expressions.
///
Expand Down
4 changes: 4 additions & 0 deletions src/repr/src/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,12 @@ optimizer_feature_flags!({
enable_less_reduce_in_eqprop: bool,
// See the feature flag of the same name.
enable_dequadratic_eqprop_map: bool,
// See the feature flag of the same name.
enable_fast_path_plan_insights: bool,
// See the feature flag of the same name.
enable_repr_typecheck: bool,
// See the feature flag of the same name.
enable_cast_elimination: bool,
});

/// A trait used to implement layered config construction.
Expand Down
1 change: 1 addition & 0 deletions src/sql/src/plan/statement/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4914,6 +4914,7 @@ pub fn unplan_create_cluster(
enable_eq_classes_withholding_errors: _,
enable_fast_path_plan_insights: _,
enable_repr_typecheck: _,
enable_cast_elimination: _,
} = optimizer_feature_overrides;
// The ones from above that don't occur below are not wired up to cluster features.
let features_extracted = ClusterFeatureExtracted {
Expand Down
1 change: 1 addition & 0 deletions src/sql/src/plan/statement/dml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ impl TryFrom<ExplainPlanOptionExtracted> for ExplainConfig {
enable_eq_classes_withholding_errors: Default::default(),
enable_fast_path_plan_insights: Default::default(),
enable_repr_typecheck: Default::default(),
enable_cast_elimination: Default::default(),
},
})
}
Expand Down
9 changes: 8 additions & 1 deletion src/sql/src/session/vars/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2220,7 +2220,7 @@ feature_flags!(
},
{
name: enable_repr_typecheck,
desc: "Enable typechecking using representation types",
desc: "Enable typechecking using representation types.",
default: false,
enable_for_item_parsing: false,
},
Expand All @@ -2230,6 +2230,12 @@ feature_flags!(
default: false,
enable_for_item_parsing: false,
},
{
name: enable_cast_elimination,
desc: "Allow the optimizer to eliminate noop casts between values of equivalent representation types.",
default: false,
enable_for_item_parsing: false,
},
);

impl From<&super::SystemVars> for OptimizerFeatures {
Expand All @@ -2254,6 +2260,7 @@ impl From<&super::SystemVars> for OptimizerFeatures {
enable_eq_classes_withholding_errors: vars.enable_eq_classes_withholding_errors(),
enable_fast_path_plan_insights: vars.enable_fast_path_plan_insights(),
enable_repr_typecheck: vars.enable_repr_typecheck(),
enable_cast_elimination: vars.enable_cast_elimination(),
}
}
}
100 changes: 100 additions & 0 deletions src/transform/src/eliminate_noop_casts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

//! SQL types distinguish between `varchar` and `varchar(n)` and `text` as
//! types, but they are all represented using `Datum::String` at runtime.
//! This transform eliminates noop casts between values of equivalent
//! representation types. s

use mz_expr::MirRelationExpr;
use mz_ore::soft_assert_or_log;

use crate::{TransformCtx, TransformError};

/// A transform that eliminates noop casts between values of equivalent representation types.
#[derive(Debug)]
pub struct EliminateNoopCasts;

impl crate::Transform for EliminateNoopCasts {
fn name(&self) -> &'static str {
"EliminateNoopCasts"
}

fn actually_perform_transform(
&self,
relation: &mut MirRelationExpr,
ctx: &mut TransformCtx,
) -> Result<(), TransformError> {
soft_assert_or_log!(
ctx.features.enable_cast_elimination,
"cast elimination is not enabled but the pass ran anyway"
);

// Descend the AST, reducing scalar expressions.
let mut todo = vec![&mut *relation];
while let Some(expr) = todo.pop() {
match expr {
MirRelationExpr::Constant { .. }
| MirRelationExpr::Get { .. }
| MirRelationExpr::Let { .. }
| MirRelationExpr::LetRec { .. }
| MirRelationExpr::Project { .. }
| MirRelationExpr::Union { .. }
| MirRelationExpr::Threshold { .. }
| MirRelationExpr::Negate { .. } => {
// No expressions to reduce
}
MirRelationExpr::Map { scalars: exprs, .. }
| MirRelationExpr::FlatMap { exprs, .. }
| MirRelationExpr::Filter {
predicates: exprs, ..
} => {
for e in exprs.iter_mut() {
e.elimimate_noop_casts().map_err(TransformError::from)?;
}
}
MirRelationExpr::Join {
equivalences: vecexprs,
..
}
| MirRelationExpr::ArrangeBy { keys: vecexprs, .. } => {
for exprs in vecexprs.iter_mut() {
for e in exprs.iter_mut() {
e.elimimate_noop_casts().map_err(TransformError::from)?;
}
}
}
MirRelationExpr::Reduce {
group_key: exprs,
aggregates,
..
} => {
for e in exprs.iter_mut() {
e.elimimate_noop_casts().map_err(TransformError::from)?;
}

for agg in aggregates.iter_mut() {
agg.expr
.elimimate_noop_casts()
.map_err(TransformError::from)?;
}
}
MirRelationExpr::TopK { limit, .. } => {
if let Some(limit) = limit {
limit.elimimate_noop_casts().map_err(TransformError::from)?;
}
}
}

todo.extend(expr.children_mut())
}

Ok(())
}
}
9 changes: 7 additions & 2 deletions src/transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use crate::canonicalize_mfp::CanonicalizeMfp;
use crate::column_knowledge::ColumnKnowledge;
use crate::dataflow::DataflowMetainfo;
use crate::demand::Demand;
use crate::eliminate_noop_casts::EliminateNoopCasts;
use crate::equivalence_propagation::EquivalencePropagation;
use crate::fold_constants::FoldConstants;
use crate::join_implementation::JoinImplementation;
Expand Down Expand Up @@ -71,6 +72,7 @@ pub mod compound;
pub mod cse;
pub mod dataflow;
pub mod demand;
pub mod eliminate_noop_casts;
pub mod equivalence_propagation;
pub mod fold_constants;
pub mod fusion;
Expand Down Expand Up @@ -747,6 +749,8 @@ impl Optimizer {
let transforms: Vec<Box<dyn Transform>> = transforms![
Box::new(Typecheck::new(ctx.typecheck()).strict_join_equivalences()),
Box::new(ReprTypecheck::new(ctx.repr_typecheck()).strict_join_equivalences()); if ctx.features.enable_repr_typecheck,
Box::new(EliminateNoopCasts); if ctx.features.enable_cast_elimination,
Box::new(ReprTypecheck::new(ctx.repr_typecheck()).strict_join_equivalences()); if ctx.features.enable_repr_typecheck && ctx.features.enable_cast_elimination,
// 1. Structure-agnostic cleanup
Box::new(normalize()),
Box::new(NonNullRequirements::default()),
Expand Down Expand Up @@ -956,8 +960,9 @@ impl Optimizer {
}

/// Builds a tiny optimizer, which is only suitable for optimizing fast-path queries.
pub fn fast_path_optimizer(_ctx: &mut TransformCtx) -> Self {
let transforms: Vec<Box<dyn Transform>> = vec![
pub fn fast_path_optimizer(ctx: &mut TransformCtx) -> Self {
let transforms: Vec<Box<dyn Transform>> = transforms![
Box::new(EliminateNoopCasts); if ctx.features.enable_cast_elimination,
Box::new(canonicalization::ReduceScalars),
Box::new(LiteralConstraints),
Box::new(CanonicalizeMfp),
Expand Down
7 changes: 6 additions & 1 deletion src/transform/tests/test_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod tests {
use mz_transform::dataflow::{
DataflowMetainfo, optimize_dataflow_demand_inner, optimize_dataflow_filters_inner,
};
use mz_transform::eliminate_noop_casts::EliminateNoopCasts;
use mz_transform::{Optimizer, Transform, TransformCtx, reprtypecheck, typecheck};
use proc_macro2::TokenTree;

Expand Down Expand Up @@ -427,12 +428,16 @@ mod tests {
Some(TEST_GLOBAL_ID),
);

let cast_elim = EliminateNoopCasts;
let log_optimizer = Optimizer::logical_cleanup_pass(&mut transform_ctx, true);
let phys_optimizer = Optimizer::physical_optimizer(&mut transform_ctx);
dataflow = dataflow
.into_iter()
.map(|(id, rel)| {
.map(|(id, mut rel)| {
transform_ctx.set_global_id(id);
if transform_ctx.features.enable_cast_elimination {
cast_elim.transform(&mut rel, &mut transform_ctx).unwrap();
}
let local_mir_plan = log_optimizer
.optimize(rel, &mut transform_ctx)
.unwrap()
Expand Down