mattfaltyn opened a new issue, #1870:
URL: https://github.com/apache/iceberg-go/issues/1870

   ## Apache Iceberg version
   
   `main` at `0f08e66e59cc1ec9b40898823d9eb991409bd080` (also present in v0.6.0)
   
   ## Please describe the bug 🐞
   
   Binding an `IN` or `NOT IN` predicate to a narrower numeric field retains 
`AboveMaxLiteral` and `BelowMinLiteral` sentinels in the bound set. Metrics 
evaluators assume every set member is a regular `TypedLiteral[T]`, so a valid 
scan can fail during planning when its filter mixes representable and 
out-of-range values.
   
   For example, an application may build an `int64` filter for an Iceberg 
`int32` column:
   
   ```go
   iceberg.IsIn(iceberg.Reference("id"), int64(35), int64(math.MaxInt32)+1)
   ```
   
   The second value can never equal an `int32` column value and should be 
discarded during binding. Instead, it becomes `aboveMaxLiteral[int32]`. 
Inclusive metrics evaluation reaches `removeBoundCmp`, asserts that every 
member implements `TypedLiteral[int32]`, and returns this error:
   
   ```text
   interface conversion: iceberg.aboveMaxLiteral[int32] is not 
iceberg.TypedLiteral[int32]: missing method Comparator
   ```
   
   This affects the normal scan-planning path because 
`Scan.collectManifestEntriesWithSchema` creates the inclusive metrics evaluator 
for the row filter.
   
   ### Expected behavior
   
   - Impossible set members are removed when binding to the field type.
   - The mixed predicate above behaves like `id IN (35)`.
   - A set containing only impossible members simplifies to `AlwaysFalse` for 
`IN` and `AlwaysTrue` for `NOT IN`.
   - Metrics evaluation completes without an error.
   
   ### Reproduction
   
   The following regression test can be added to `table/evaluators_test.go`:
   
   ```go
   func TestInclusiveMetricsMixedRangeIn(t *testing.T) {
        lower, err := iceberg.Int32Literal(30).MarshalBinary()
        require.NoError(t, err)
        upper, err := iceberg.Int32Literal(79).MarshalBinary()
        require.NoError(t, err)
   
        schema := iceberg.NewSchema(0,
                iceberg.NestedField{ID: 1, Name: "id", Type: 
iceberg.PrimitiveTypes.Int32},
        )
        file := &mockDataFile{
                count:       1,
                valueCounts: map[int]int64{1: 1},
                nullCounts:  map[int]int64{1: 0},
                lowerBounds: map[int][]byte{1: lower},
                upperBounds: map[int][]byte{1: upper},
        }
        filter := iceberg.IsIn(
                iceberg.Reference("id"),
                int64(35),
                int64(math.MaxInt32)+1,
        )
   
        eval, err := newInclusiveMetricsEvaluator(schema, filter, true, true)
        require.NoError(t, err)
        shouldRead, err := eval(file)
        require.NoError(t, err)
        assert.True(t, shouldRead)
   }
   ```
   
   On current `main`, `eval(file)` returns the interface-conversion error 
above. An in-range control using `int64(35), int64(36)` succeeds.
   
   ### Root cause
   
   `createBoundSetPredicate` calls `Literal.To(boundType)` and unconditionally 
inserts the result. Scalar literal binding already handles `AboveMaxLiteral` 
and `BelowMinLiteral` specially, but set binding does not.
   
   Dropping those impossible members before the existing zero/one-member 
simplification should preserve `IN` and `NOT IN` semantics while preventing 
non-comparable sentinel values from reaching expression, manifest, metrics, and 
bloom-filter consumers.
   
   


-- 
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