Skip to content

Commit f5bc6d4

Browse files
Ted-JiangMazterQyou
authored andcommitted
fix alias rewrite In_List for filter push down (apache#2729)
1 parent ab50765 commit f5bc6d4

File tree

2 files changed

+176
-1
lines changed

2 files changed

+176
-1
lines changed

datafusion/core/src/optimizer/filter_push_down.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ mod tests {
601601
use crate::{logical_plan::col, prelude::JoinType};
602602
use arrow::datatypes::SchemaRef;
603603
use async_trait::async_trait;
604+
use datafusion_expr::expr_fn::in_list;
604605

605606
fn optimize_plan(plan: &LogicalPlan) -> LogicalPlan {
606607
let rule = FilterPushDown::new();
@@ -1506,4 +1507,174 @@ mod tests {
15061507

15071508
Ok(())
15081509
}
1510+
1511+
#[test]
1512+
fn test_filter_with_alias() -> Result<()> {
1513+
// in table scan the true col name is 'test.a',
1514+
// but we rename it as 'b', and use col 'b' in filter
1515+
// we need rewrite filter col before push down.
1516+
let table_scan = test_table_scan()?;
1517+
let plan = LogicalPlanBuilder::from(table_scan)
1518+
.project(vec![col("a").alias("b"), col("c")])?
1519+
.filter(and(col("b").gt(lit(10i64)), col("c").gt(lit(10i64))))?
1520+
.build()?;
1521+
1522+
// filter on col b
1523+
assert_eq!(
1524+
format!("{:?}", plan),
1525+
"\
1526+
Filter: #b > Int64(10) AND #test.c > Int64(10)\
1527+
\n Projection: #test.a AS b, #test.c\
1528+
\n TableScan: test projection=None\
1529+
"
1530+
);
1531+
1532+
// rewrite filter col b to test.a
1533+
let expected = "\
1534+
Projection: #test.a AS b, #test.c\
1535+
\n Filter: #test.a > Int64(10) AND #test.c > Int64(10)\
1536+
\n TableScan: test projection=None\
1537+
";
1538+
1539+
assert_optimized_plan_eq(&plan, expected);
1540+
1541+
Ok(())
1542+
}
1543+
1544+
#[test]
1545+
fn test_filter_with_alias_2() -> Result<()> {
1546+
// in table scan the true col name is 'test.a',
1547+
// but we rename it as 'b', and use col 'b' in filter
1548+
// we need rewrite filter col before push down.
1549+
let table_scan = test_table_scan()?;
1550+
let plan = LogicalPlanBuilder::from(table_scan)
1551+
.project(vec![col("a").alias("b"), col("c")])?
1552+
.project(vec![col("b"), col("c")])?
1553+
.filter(and(col("b").gt(lit(10i64)), col("c").gt(lit(10i64))))?
1554+
.build()?;
1555+
1556+
// filter on col b
1557+
assert_eq!(
1558+
format!("{:?}", plan),
1559+
"\
1560+
Filter: #b > Int64(10) AND #test.c > Int64(10)\
1561+
\n Projection: #b, #test.c\
1562+
\n Projection: #test.a AS b, #test.c\
1563+
\n TableScan: test projection=None\
1564+
"
1565+
);
1566+
1567+
// rewrite filter col b to test.a
1568+
let expected = "\
1569+
Projection: #b, #test.c\
1570+
\n Projection: #test.a AS b, #test.c\
1571+
\n Filter: #test.a > Int64(10) AND #test.c > Int64(10)\
1572+
\n TableScan: test projection=None\
1573+
";
1574+
1575+
assert_optimized_plan_eq(&plan, expected);
1576+
1577+
Ok(())
1578+
}
1579+
1580+
#[test]
1581+
fn test_filter_with_multi_alias() -> Result<()> {
1582+
let table_scan = test_table_scan()?;
1583+
let plan = LogicalPlanBuilder::from(table_scan)
1584+
.project(vec![col("a").alias("b"), col("c").alias("d")])?
1585+
.filter(and(col("b").gt(lit(10i64)), col("d").gt(lit(10i64))))?
1586+
.build()?;
1587+
1588+
// filter on col b and d
1589+
assert_eq!(
1590+
format!("{:?}", plan),
1591+
"\
1592+
Filter: #b > Int64(10) AND #d > Int64(10)\
1593+
\n Projection: #test.a AS b, #test.c AS d\
1594+
\n TableScan: test projection=None\
1595+
"
1596+
);
1597+
1598+
// rewrite filter col b to test.a, col d to test.c
1599+
let expected = "\
1600+
Projection: #test.a AS b, #test.c AS d\
1601+
\n Filter: #test.a > Int64(10) AND #test.c > Int64(10)\
1602+
\n TableScan: test projection=None\
1603+
";
1604+
1605+
assert_optimized_plan_eq(&plan, expected);
1606+
1607+
Ok(())
1608+
}
1609+
1610+
#[test]
1611+
fn test_in_filter_with_alias() -> Result<()> {
1612+
// in table scan the true col name is 'test.a',
1613+
// but we rename it as 'b', and use col 'b' in filter
1614+
// we need rewrite filter col before push down.
1615+
let table_scan = test_table_scan()?;
1616+
let filter_value = vec![lit(1u32), lit(2u32), lit(3u32), lit(4u32)];
1617+
let plan = LogicalPlanBuilder::from(table_scan)
1618+
.project(vec![col("a").alias("b"), col("c")])?
1619+
.filter(in_list(col("b"), filter_value, false))?
1620+
.build()?;
1621+
1622+
// filter on col b
1623+
assert_eq!(
1624+
format!("{:?}", plan),
1625+
"\
1626+
Filter: #b IN ([UInt32(1), UInt32(2), UInt32(3), UInt32(4)])\
1627+
\n Projection: #test.a AS b, #test.c\
1628+
\n TableScan: test projection=None\
1629+
"
1630+
);
1631+
1632+
// rewrite filter col b to test.a
1633+
let expected = "\
1634+
Projection: #test.a AS b, #test.c\
1635+
\n Filter: #test.a IN ([UInt32(1), UInt32(2), UInt32(3), UInt32(4)])\
1636+
\n TableScan: test projection=None\
1637+
";
1638+
1639+
assert_optimized_plan_eq(&plan, expected);
1640+
1641+
Ok(())
1642+
}
1643+
1644+
#[test]
1645+
fn test_in_filter_with_alias_2() -> Result<()> {
1646+
// in table scan the true col name is 'test.a',
1647+
// but we rename it as 'b', and use col 'b' in filter
1648+
// we need rewrite filter col before push down.
1649+
let table_scan = test_table_scan()?;
1650+
let filter_value = vec![lit(1u32), lit(2u32), lit(3u32), lit(4u32)];
1651+
let plan = LogicalPlanBuilder::from(table_scan)
1652+
.project(vec![col("a").alias("b"), col("c")])?
1653+
.project(vec![col("b"), col("c")])?
1654+
.filter(in_list(col("b"), filter_value, false))?
1655+
.build()?;
1656+
1657+
// filter on col b
1658+
assert_eq!(
1659+
format!("{:?}", plan),
1660+
"\
1661+
Filter: #b IN ([UInt32(1), UInt32(2), UInt32(3), UInt32(4)])\
1662+
\n Projection: #b, #test.c\
1663+
\n Projection: #test.a AS b, #test.c\
1664+
\n TableScan: test projection=None\
1665+
"
1666+
);
1667+
1668+
// rewrite filter col b to test.a
1669+
let expected = "\
1670+
Projection: #b, #test.c\
1671+
\n Projection: #test.a AS b, #test.c\
1672+
\n Filter: #test.a IN ([UInt32(1), UInt32(2), UInt32(3), UInt32(4)])\
1673+
\n TableScan: test projection=None\
1674+
";
1675+
1676+
assert_optimized_plan_eq(&plan, expected);
1677+
1678+
Ok(())
1679+
}
15091680
}

datafusion/core/src/optimizer/utils.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,10 +560,14 @@ pub fn rewrite_expression(expr: &Expr, expressions: &[Expr]) -> Result<Expr> {
560560
}
561561
Expr::Not(_) => Ok(Expr::Not(Box::new(expressions[0].clone()))),
562562
Expr::Negative(_) => Ok(Expr::Negative(Box::new(expressions[0].clone()))),
563+
Expr::InList { list, negated, .. } => Ok(Expr::InList {
564+
expr: Box::new(expressions[0].clone()),
565+
list: list.clone(),
566+
negated: *negated,
567+
}),
563568
Expr::Column(_)
564569
| Expr::OuterColumn(_, _)
565570
| Expr::Literal(_)
566-
| Expr::InList { .. }
567571
| Expr::ScalarVariable(_, _) => Ok(expr.clone()),
568572
Expr::Sort {
569573
asc, nulls_first, ..

0 commit comments

Comments
 (0)