Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions datafusion/optimizer/src/push_down_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,13 @@ fn infer_join_predicates(

let join_type = join.join_type;

// Null-aware anti joins implement NOT IN semantics, where NULLs on the
// right side affect the result. Inferring predicates across the join can
// incorrectly filter those NULLs.
if join_type == JoinType::LeftAnti && join.null_aware {
return Ok(vec![]);
}

let mut inferred_predicates = InferredPredicates::new(join_type);

infer_join_predicates_from_predicates(
Expand Down
48 changes: 48 additions & 0 deletions datafusion/sqllogictest/test_files/not_in_null_pushdown.slt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# Regression test for NOT IN null-aware semantics. The right-side NULL must not
# be removed by inferred predicate pushdown through the null-aware anti join.

statement ok
CREATE TABLE pushdown_t(id INT, a INT)

statement ok
INSERT INTO pushdown_t VALUES (1, 1), (2, 2)

statement ok
CREATE TABLE pushdown_u(k INT)

statement ok
INSERT INTO pushdown_u VALUES (NULL)

# Expected result is empty:
# - id=1: a <> 1 is false
# - id=2: a <> 1 is true, but 2 NOT IN (NULL) is UNKNOWN
# WHERE only keeps TRUE, so neither row should be returned.
query I
SELECT id
FROM pushdown_t
WHERE a != 1
AND a NOT IN (SELECT k FROM pushdown_u);
----

statement ok
DROP TABLE pushdown_t

statement ok
DROP TABLE pushdown_u
Loading