[ 
https://issues.apache.org/jira/browse/IMPALA-15421?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Zoltán Borók-Nagy resolved IMPALA-15421.
----------------------------------------
    Fix Version/s: Impala 5.0.0
       Resolution: Fixed

> Iceberg predicate subsetting drops a needed conjunct when two residuals 
> sanitize to the same string
> ---------------------------------------------------------------------------------------------------
>
>                 Key: IMPALA-15421
>                 URL: https://issues.apache.org/jira/browse/IMPALA-15421
>             Project: IMPALA
>          Issue Type: Bug
>          Components: Frontend
>            Reporter: Zoltán Borók-Nagy
>            Assignee: Zoltán Borók-Nagy
>            Priority: Critical
>              Labels: correctness, impala-iceberg, 
> impala-iceberg-active-backlog
>             Fix For: Impala 5.0.0
>
>
> IcebergScanPlanner collects the residual expression of every FileScanTask 
> into a set
> that uses Iceberg's sanitized string as the comparator:
> {code:java}
> // IcebergScanPlanner.java:125
> private final Set<Expression> residualExpressions_ =
>     new TreeSet<>(Comparator.comparing(ExpressionUtil::toSanitizedString));
> {code}
> {{toSanitizedString()}} replaces numeric literals with placeholders, so 
> different
> residuals compare as equal:
> {noformat}
> id != 12345  ->  id != (5-digit-int)
> id != 54321  ->  id != (5-digit-int)
> {noformat}
> The set keeps only the first one. With 
> ICEBERG_PREDICATE_PUSHDOWN_SUBSETTING=true (the
> default), trySubsettingPredicatesBeingPushedDown() retains only the Impala 
> conjuncts that
> map to residuals in the set. The other conjunct goes to 
> {{skippedExpressions_}} and the
> scanner never evaluates it, so rows that should be filtered are returned.
> This happens when different files have different residuals that sanitize to 
> the same
> string. Non-identity partition transforms (truncate, bucket) are a typical 
> cause. For
> {{TRUNCATE(10000, id)}} and {{id != 12345 AND id != 54321}}, Iceberg's 
> ResidualEvaluator
> returns {{id != 12345}} for partition 10000 and {{id != 54321}} for partition 
> 50000.
> String literals are hashed by the sanitizer, so they don't collide.
> h3. Repro
> {code:sql}
> CREATE TABLE ice_residual (id INT)
>   PARTITIONED BY SPEC (TRUNCATE(10000, id)) STORED AS ICEBERG;
> INSERT INTO ice_residual VALUES (12345), (12346), (54321), (54322);
> SELECT id FROM ice_residual WHERE id != 12345 AND id != 54321 ORDER BY id;
> {code}
> Expected: 12346, 54322.
> Actual (on master a6af29eee0):
> {noformat}
> +-------+
> | id    |
> +-------+
> | 12346 |
> | 54321 |
> | 54322 |
> +-------+
> {noformat}
> The EXPLAIN output shows that the second conjunct is no longer evaluated by 
> the scan:
> {noformat}
> 00:SCAN HDFS [ice_residual_repro.ice_residual, RANDOM]
>    HDFS partitions=2/2 files=2 size=716B
>    predicates: id != CAST(12345 AS INT)
>    skipped Iceberg predicates: id != CAST(54321 AS INT)
> {noformat}
> Which of the two values leaks depends on which file planFiles() returns 
> first. With
> ICEBERG_PREDICATE_PUSHDOWN_SUBSETTING=false, the same query returns the 
> correct 12346,
> 54322.
> Workaround: SET ICEBERG_PREDICATE_PUSHDOWN_SUBSETTING=false.
> h3. Proposed fix
> Key the collection by the exact {{Expression.toString()}}, which prints 
> literals in full.
> Use a TreeMap<String, Expression> so the order of the retained conjuncts stays
> deterministic for planner tests. Add an identity short-circuit for the common 
> case where
> every file returns the same residual object (e.g. unpartitioned tables). This 
> also
> removes the per-file sanitization cost from planning, about 2 us per file on
> unpartitioned tables in a 100k-file microbenchmark. Add a regression test for 
> the case
> above.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to