in table/evaluators.go [1133:1186]
func (m *inclusiveMetricsEval) VisitNotStartsWith(t iceberg.BoundTerm, lit iceberg.Literal) bool {
field := t.Ref().Field()
fieldID := field.ID
if m.mayContainNull(fieldID) {
return rowsMightMatch
}
if _, ok := field.Type.(iceberg.PrimitiveType); !ok {
panic(fmt.Errorf("%w: expected iceberg.PrimitiveType, got %s",
iceberg.ErrInvalidTypeString, field.Type))
}
// not_starts_with will match unless all values must start with the prefix.
// this happens when the lower and upper bounds both start with the prefix
lowerBoundBytes, upperBoundBytes := m.lowerBounds[fieldID], m.upperBounds[fieldID]
if lowerBoundBytes != nil && upperBoundBytes != nil {
lowerBound, err := iceberg.LiteralFromBytes(field.Type, lowerBoundBytes)
if err != nil {
panic(err)
}
upperBound, err := iceberg.LiteralFromBytes(field.Type, upperBoundBytes)
if err != nil {
panic(err)
}
var prefix, lower, upper string
if val, ok := lit.(iceberg.TypedLiteral[string]); ok {
prefix = val.Value()
lower, upper = lowerBound.(iceberg.TypedLiteral[string]).Value(), upperBound.(iceberg.TypedLiteral[string]).Value()
} else {
prefix = string(lit.(iceberg.TypedLiteral[[]byte]).Value())
lower, upper = string(lowerBound.(iceberg.TypedLiteral[[]byte]).Value()), string(upperBound.(iceberg.TypedLiteral[[]byte]).Value())
}
lenPrefix := len(prefix)
if len(lower) < lenPrefix {
return rowsMightMatch
}
if lower[:lenPrefix] == prefix {
if len(upper) < lenPrefix {
return rowsMightMatch
}
if upper[:lenPrefix] == prefix {
return rowsCannotMatch
}
}
}
return rowsMightMatch
}