in crates/iceberg/src/expr/visitors/page_index_evaluator.rs [583:647]
fn starts_with(
&mut self,
reference: &BoundReference,
datum: &Datum,
_predicate: &BoundPredicate,
) -> Result<RowSelection> {
let field_id = reference.field().id;
let PrimitiveLiteral::String(datum) = datum.literal() else {
return Err(Error::new(
ErrorKind::Unexpected,
"Cannot use StartsWith operator on non-string values",
));
};
self.calc_row_selection(
field_id,
|min, max, nulls| {
if matches!(nulls, PageNullCount::AllNull) {
return Ok(false);
}
if let Some(lower_bound) = min {
let PrimitiveLiteral::String(lower_bound) = lower_bound.literal() else {
return Err(Error::new(
ErrorKind::Unexpected,
"Cannot use StartsWith operator on non-string lower_bound value",
));
};
let prefix_length = lower_bound.chars().count().min(datum.chars().count());
// truncate lower bound so that its length
// is not greater than the length of prefix
let truncated_lower_bound =
lower_bound.chars().take(prefix_length).collect::<String>();
if datum < &truncated_lower_bound {
return Ok(false);
}
}
if let Some(upper_bound) = max {
let PrimitiveLiteral::String(upper_bound) = upper_bound.literal() else {
return Err(Error::new(
ErrorKind::Unexpected,
"Cannot use StartsWith operator on non-string upper_bound value",
));
};
let prefix_length = upper_bound.chars().count().min(datum.chars().count());
// truncate upper bound so that its length
// is not greater than the length of prefix
let truncated_upper_bound =
upper_bound.chars().take(prefix_length).collect::<String>();
if datum > &truncated_upper_bound {
return Ok(false);
}
}
Ok(true)
},
MissingColBehavior::CantMatch,
)
}