aisle 0.2.1

Metadata-driven Parquet pruning for Rust: Skip irrelevant data before reading
Documentation
use super::{cmp, context::RowGroupContext, page::PagePruning, stats, strings};
use crate::expr::{CmpOp, TriState};

pub(super) fn eval_starts_with(column: &str, prefix: &str, ctx: &RowGroupContext<'_>) -> TriState {
    if prefix.is_empty() {
        return TriState::Unknown;
    }
    let data_type = match stats::data_type_for_path(ctx.schema, column) {
        Some(data_type) => data_type,
        None => return TriState::Unknown,
    };
    let lower = match strings::string_scalar_for_type(prefix, &data_type) {
        Some(value) => value,
        None => return TriState::Unknown,
    };
    let (min, max, null_count, row_count) = match stats::stats_for_column(column, ctx) {
        Some(stats) => stats,
        None => return TriState::Unknown,
    };
    let lower_eval = cmp::eval_cmp_stats(
        CmpOp::GtEq,
        &lower,
        Some(&min),
        Some(&max),
        null_count,
        row_count,
    );
    let upper = strings::next_prefix_string(prefix)
        .and_then(|next| strings::string_scalar_for_type(&next, &data_type));
    match upper {
        Some(upper) => {
            let upper_eval = cmp::eval_cmp_stats(
                CmpOp::Lt,
                &upper,
                Some(&min),
                Some(&max),
                null_count,
                row_count,
            );
            lower_eval.and(upper_eval)
        }
        None => lower_eval,
    }
}

pub(super) fn page_selection_for_starts_with(
    column: &str,
    prefix: &str,
    ctx: &RowGroupContext<'_>,
) -> Option<PagePruning> {
    if prefix.is_empty() {
        return None;
    }
    let data_type = stats::data_type_for_path(ctx.schema, column)?;
    let lower = strings::string_scalar_for_type(prefix, &data_type)?;
    let mut low_sel = cmp::page_selection_for_cmp(column, CmpOp::GtEq, &lower, ctx)?;
    let upper = strings::next_prefix_string(prefix)
        .and_then(|next| strings::string_scalar_for_type(&next, &data_type));
    match upper {
        Some(upper) => {
            let high_sel = cmp::page_selection_for_cmp(column, CmpOp::Lt, &upper, ctx)?;
            Some(low_sel.intersection(high_sel))
        }
        None => {
            low_sel.exact = false;
            Some(low_sel)
        }
    }
}