SEPURI-SAI-KRISHNA opened a new issue, #17655:
URL: https://github.com/apache/iceberg/issues/17655

   ### Apache Iceberg version
   
   main (development)
   
   ### Query engine
   
   Other
   
   ### Please describe the bug 🐞
   
   `ParquetDictionaryRowGroupFilter.notStartsWith` can skip row groups that 
contain
   matching rows, silently dropping those rows from scan results.
   
   Verified on `main` at `cd758fcda`. Not a regression — `notStartsWith` is 
unchanged
   in `1.11.0`, so released versions are affected the same way.
   
   The filter decides whether a row group can be skipped by inspecting only the
   column's dictionary. A Parquet dictionary contains non-null values only, so 
when
   every dictionary entry starts with the prefix the filter returns
   `ROWS_CANNOT_MATCH` — even when the column also contains nulls.
   
   In Iceberg a null value matches `notStartsWith`: `Evaluator` implements it as
   `!startsWith(...)`, and `startsWith` evaluates to false for null. A row group
   holding nulls therefore does contain matching rows and must not be skipped.
   
   ### Reproduce
   
   Write a Parquet file with a dictionary-encoded optional string column where 
every
   non-null value shares a prefix and some rows are null, then scan with a
   `notStartsWith` filter on that prefix:
   
   ```java
   Expression filter = Expressions.notStartsWith("some_nulls", "some");
   ```
   
   The rows with a null `some_nulls` are missing from the result. Reading the 
same
   data without dictionary encoding (or through the metrics filter alone) 
returns
   them.
   
   ### Expected
   
   The row group is read, and the null rows are returned.
   
   ### Why this is inconsistent with the rest of the codebase
   
   Every other evaluator already handles this:
   
   - `InclusiveEvalVisitor#notStartsWith` returns `ROWS_MIGHT_MATCH` when
     `mayContainNull(id)`
   - `ParquetMetricsRowGroupFilter#notStartsWith` returns `ROWS_MIGHT_MATCH` 
when
     `mayContainNull(colStats)`
   - `ParquetBloomRowGroupFilter` and ORC's `ExpressionToSearchArgument` do not 
push
     the predicate down at all
   
   And within `ParquetDictionaryRowGroupFilter` itself, `notEq`, `notIn` and
   `notNaN` all consult `mayContainNulls` for exactly this reason (#6431, 
#6836).
   `notStartsWith` is the only negated predicate missing the check.
   
   The existing unit test asserts the current behavior, so this is not caught 
today:
   
   ```java
   new ParquetDictionaryRowGroupFilter(SCHEMA, notStartsWith("some_nulls", 
"some"))
       .shouldRead(parquetSchema, rowGroupMetadata, dictionaryStore);
   // asserted isFalse()
   ```
   
   `some_nulls` is written as `(i % 10 == 0) ? null : "some"`, so that row 
group does
   contain matching (null) rows. `data`'s `TestMetricsRowGroupFilter` asserts 
the
   opposite for the metrics filter on a comparable column.
   
   ### Note on engine-visible impact
   
   In Spark SQL, `NOT (col LIKE 'x%')` yields NULL for a null `col` and Spark
   re-applies the filter after the scan, so Spark SQL users typically will not
   observe missing rows. The data loss is directly observable through Iceberg's 
own
   API, e.g. 
`IcebergGenerics.read(table).where(Expressions.notStartsWith(...))`,
   and in any engine that trusts the pushdown. Independently of that, the same 
query
   returning different results depending on whether a column happened to be
   dictionary-encoded is the core defect.
   
   
   ### Willingness to contribute
   
   - [x] I can contribute a fix for this bug independently
   - [x] I would be willing to contribute a fix for this bug with guidance from 
the Iceberg community
   - [ ] I cannot contribute a fix for this bug at this time


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to