Yaoxuan Wu created SPARK-60916:
----------------------------------

             Summary: Parquet filter pushdown of IN (..., NULL) on a BOOLEAN 
column drops matching rows when the list is longer than 
spark.sql.parquet.pushdown.inFilterThreshold
                 Key: SPARK-60916
                 URL: https://issues.apache.org/jira/browse/SPARK-60916
             Project: Spark
          Issue Type: Bug
          Components: SQL
    Affects Versions: 4.1.1, 4.2.0, 3.5.8, 4.0.0
            Reporter: Yaoxuan Wu


{code:python}
from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()
path = "/tmp/bool_in_null"
spark.sql("SELECT * FROM VALUES (true), (false) AS 
t(b)").coalesce(1).write.mode("overwrite").parquet(path)
spark.sql("SELECT CAST(NULL AS BOOLEAN) AS 
b").write.mode("append").parquet(path)
spark.read.parquet(path).createOrReplaceTempView("t")

q = "SELECT b FROM t WHERE b IN (true, NULL)"
spark.sql(q).show()   # one row: true
spark.conf.set("spark.sql.parquet.pushdown.inFilterThreshold", "1")
spark.sql(q).show()   # expected one row: true; actual: no row
{code}
Same on 3.5.8, 4.0.0, 4.1.1 and 4.2.0. {{b IN (true, false, NULL)}} with 
threshold 2 also returns no row. With 
{{spark.sql.parquet.filterPushdown=false}}, or for an INT column ({{x IN (1, 
NULL)}}), the result is right.

Cause: {{ParquetFilters.createFilterHelper}}, {{case sources.In}} (v4.2.0 lines 
798-818, unchanged on master). When the IN list has more values than 
{{inFilterThreshold}} and contains NULL, the pushed filter is an OR of an 
equality with NULL and a Parquet IN predicate on the other values:
{code:scala}
if (values.contains(null)) {
  Seq(makeEq.lift(fieldType).map(_(fieldNames, null)),
    makeInPredicate.lift(fieldType).map(_(fieldNames, values.filter(_ != null)))
  ).flatten.reduceLeftOption(FilterApi.or)
{code}
{{makeEq}} has a {{ParquetBooleanType}} case, but {{makeInPredicate}} has none 
(it is the only type {{makeEq}} supports and {{makeInPredicate}} does not). For 
a BOOLEAN column the second element is {{None}}, {{flatten}} drops it, and the 
pushed filter becomes {{b = NULL}} alone, which is stricter than the original 
predicate; Parquet then skips every row group without NULLs. Pushing nothing 
when either part cannot be built, or adding a BOOLEAN case to 
{{makeInPredicate}}, would fix it. The code was added in SPARK-39312 (3.4.0).

The default threshold (10) is not affected: {{OptimizeIn}} removes duplicate 
values from the IN list ({{ExpressionSet(list)}}), and a BOOLEAN list has at 
most three distinct values, so this needs {{inFilterThreshold}} <= 2.




--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to