Skip to content

Commit b81073a

Browse files
Tptalamb
andauthored
feat: makes Expr::not normalize more negations (#17848)
Adds IN and EXISTS Co-authored-by: Andrew Lamb <[email protected]>
1 parent d3ca16e commit b81073a

File tree

2 files changed

+162
-1
lines changed

2 files changed

+162
-1
lines changed

datafusion/expr/src/operation.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
//! This module contains implementations of operations (unary, binary etc.) for DataFusion expressions.
1919
20+
use crate::expr::{Exists, Expr, InList, InSubquery, Like};
2021
use crate::expr_fn::binary_expr;
21-
use crate::{Expr, Like};
2222
use datafusion_expr_common::operator::Operator;
2323
use std::ops::{self, Not};
2424

@@ -153,6 +153,19 @@ impl Not for Expr {
153153
escape_char,
154154
case_insensitive,
155155
)),
156+
Expr::InList(InList {
157+
expr,
158+
list,
159+
negated,
160+
}) => Expr::InList(InList::new(expr, list, !negated)),
161+
Expr::Exists(Exists { subquery, negated }) => {
162+
Expr::Exists(Exists::new(subquery, !negated))
163+
}
164+
Expr::InSubquery(InSubquery {
165+
expr,
166+
subquery,
167+
negated,
168+
}) => Expr::InSubquery(InSubquery::new(expr, subquery, !negated)),
156169
_ => Expr::Not(Box::new(self)),
157170
}
158171
}

datafusion/optimizer/src/simplify_expressions/simplify_exprs.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,4 +926,152 @@ mod tests {
926926
"#
927927
)
928928
}
929+
930+
#[test]
931+
fn simplify_not_in_list() -> Result<()> {
932+
let schema = Schema::new(vec![Field::new("a", DataType::Utf8, false)]);
933+
let table_scan = table_scan(Some("test"), &schema, None)?.build()?;
934+
935+
let plan = LogicalPlanBuilder::from(table_scan)
936+
.filter(col("a").in_list(vec![lit("a"), lit("b")], false).not())?
937+
.build()?;
938+
939+
assert_optimized_plan_equal!(
940+
plan,
941+
@ r#"
942+
Filter: test.a != Utf8("a") AND test.a != Utf8("b")
943+
TableScan: test
944+
"#
945+
)
946+
}
947+
948+
#[test]
949+
fn simplify_not_not_in_list() -> Result<()> {
950+
let schema = Schema::new(vec![Field::new("a", DataType::Utf8, false)]);
951+
let table_scan = table_scan(Some("test"), &schema, None)?.build()?;
952+
953+
let plan = LogicalPlanBuilder::from(table_scan)
954+
.filter(
955+
col("a")
956+
.in_list(vec![lit("a"), lit("b")], false)
957+
.not()
958+
.not(),
959+
)?
960+
.build()?;
961+
962+
assert_optimized_plan_equal!(
963+
plan,
964+
@ r#"
965+
Filter: test.a = Utf8("a") OR test.a = Utf8("b")
966+
TableScan: test
967+
"#
968+
)
969+
}
970+
971+
#[test]
972+
fn simplify_not_exists() -> Result<()> {
973+
let schema = Schema::new(vec![Field::new("a", DataType::Utf8, false)]);
974+
let table_scan = table_scan(Some("test"), &schema, None)?.build()?;
975+
let table_scan2 =
976+
datafusion_expr::table_scan(Some("test2"), &schema, None)?.build()?;
977+
978+
let plan = LogicalPlanBuilder::from(table_scan)
979+
.filter(
980+
exists(Arc::new(LogicalPlanBuilder::from(table_scan2).build()?)).not(),
981+
)?
982+
.build()?;
983+
984+
assert_optimized_plan_equal!(
985+
plan,
986+
@ r"
987+
Filter: NOT EXISTS (<subquery>)
988+
Subquery:
989+
TableScan: test2
990+
TableScan: test
991+
"
992+
)
993+
}
994+
995+
#[test]
996+
fn simplify_not_not_exists() -> Result<()> {
997+
let schema = Schema::new(vec![Field::new("a", DataType::Utf8, false)]);
998+
let table_scan = table_scan(Some("test"), &schema, None)?.build()?;
999+
let table_scan2 =
1000+
datafusion_expr::table_scan(Some("test2"), &schema, None)?.build()?;
1001+
1002+
let plan = LogicalPlanBuilder::from(table_scan)
1003+
.filter(
1004+
exists(Arc::new(LogicalPlanBuilder::from(table_scan2).build()?))
1005+
.not()
1006+
.not(),
1007+
)?
1008+
.build()?;
1009+
1010+
assert_optimized_plan_equal!(
1011+
plan,
1012+
@ r"
1013+
Filter: EXISTS (<subquery>)
1014+
Subquery:
1015+
TableScan: test2
1016+
TableScan: test
1017+
"
1018+
)
1019+
}
1020+
1021+
#[test]
1022+
fn simplify_not_in_subquery() -> Result<()> {
1023+
let schema = Schema::new(vec![Field::new("a", DataType::Utf8, false)]);
1024+
let table_scan = table_scan(Some("test"), &schema, None)?.build()?;
1025+
let table_scan2 =
1026+
datafusion_expr::table_scan(Some("test2"), &schema, None)?.build()?;
1027+
1028+
let plan = LogicalPlanBuilder::from(table_scan)
1029+
.filter(
1030+
in_subquery(
1031+
col("a"),
1032+
Arc::new(LogicalPlanBuilder::from(table_scan2).build()?),
1033+
)
1034+
.not(),
1035+
)?
1036+
.build()?;
1037+
1038+
assert_optimized_plan_equal!(
1039+
plan,
1040+
@ r"
1041+
Filter: test.a NOT IN (<subquery>)
1042+
Subquery:
1043+
TableScan: test2
1044+
TableScan: test
1045+
"
1046+
)
1047+
}
1048+
1049+
#[test]
1050+
fn simplify_not_not_in_subquery() -> Result<()> {
1051+
let schema = Schema::new(vec![Field::new("a", DataType::Utf8, false)]);
1052+
let table_scan = table_scan(Some("test"), &schema, None)?.build()?;
1053+
let table_scan2 =
1054+
datafusion_expr::table_scan(Some("test2"), &schema, None)?.build()?;
1055+
1056+
let plan = LogicalPlanBuilder::from(table_scan)
1057+
.filter(
1058+
in_subquery(
1059+
col("a"),
1060+
Arc::new(LogicalPlanBuilder::from(table_scan2).build()?),
1061+
)
1062+
.not()
1063+
.not(),
1064+
)?
1065+
.build()?;
1066+
1067+
assert_optimized_plan_equal!(
1068+
plan,
1069+
@ r"
1070+
Filter: test.a IN (<subquery>)
1071+
Subquery:
1072+
TableScan: test2
1073+
TableScan: test
1074+
"
1075+
)
1076+
}
9291077
}

0 commit comments

Comments
 (0)