Expand description

Pushes predicates down through other operators.

This action generally improves the quality of the query, in that selective per-record filters reduce the volume of data before they arrive at more expensive operators.

The one time when this action might not improve the quality of a query is if a filter gets pushed down on an arrangement because that blocks arrangement reuse. It assumed that actions that need an arrangement are responsible for lifting filters out of the way.

Predicate pushdown will not push down literal errors, unless it is certain that the literal errors will be unconditionally evaluated. For example, the pushdown will not happen if not all predicates can be pushed down (e.g. reduce and map), or if we are not certain that the input is non-empty (e.g. join).

use mz_expr::{BinaryFunc, MirRelationExpr, MirScalarExpr};
use mz_ore::id_gen::IdGen;
use mz_repr::{ColumnType, Datum, RelationType, ScalarType};

use mz_transform::predicate_pushdown::PredicatePushdown;

let input1 = MirRelationExpr::constant(vec![], RelationType::new(vec![
let input2 = MirRelationExpr::constant(vec![], RelationType::new(vec![
let input3 = MirRelationExpr::constant(vec![], RelationType::new(vec![
let join = MirRelationExpr::join(
    vec![input1.clone(), input2.clone(), input3.clone()],
    vec![vec![(0, 0), (2, 0)].into_iter().collect()],

let predicate0 = MirScalarExpr::column(0);
let predicate1 = MirScalarExpr::column(1);
let predicate01 = MirScalarExpr::column(0).call_binary(MirScalarExpr::column(2), BinaryFunc::AddInt64);
let predicate012 = MirScalarExpr::literal_ok(Datum::False, ScalarType::Bool);

let mut expr = join.filter(

use mz_transform::{Transform, TransformArgs};
PredicatePushdown::default().transform(&mut expr, TransformArgs {
  id_gen: &mut Default::default(),
  indexes: &mz_transform::EmptyIndexOracle,

let predicate00 = MirScalarExpr::column(0).call_binary(MirScalarExpr::column(0), BinaryFunc::AddInt64);
let expected_expr = MirRelationExpr::join(
        input1.clone().filter(vec![predicate0.clone(), predicate00.clone()]),
        input3.clone().filter(vec![predicate0, predicate00])
    vec![vec![(0, 0), (2, 0)].into_iter().collect()],
assert_eq!(expected_expr, expr)


Pushes predicates down through other operators.