blob: 3a86bad886eb2edc4ee010d69a1930172bb24e17 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! BTree index query abstraction.
//!
//! Provides the `IndexQuery` trait for evaluating predicates against index readers,
//! and query optimization utilities like between-pattern detection.
use crate::btree::key_serde::serialize_datum;
use crate::btree::reader::BTreeIndexReader;
use crate::spec::{DataType, Datum, PredicateOperator};
use roaring::RoaringTreemap;
use std::cmp::Ordering;
use std::io;
/// Trait for index readers that can evaluate predicates and return row ID bitmaps.
#[async_trait::async_trait]
pub trait IndexQuery: Send + Sync {
/// Evaluate a predicate and return matching row IDs.
async fn query(
&self,
op: PredicateOperator,
literals: &[Datum],
data_type: &DataType,
) -> io::Result<RoaringTreemap>;
}
#[async_trait::async_trait]
impl<F> IndexQuery for BTreeIndexReader<F>
where
F: Fn(&[u8], &[u8]) -> Ordering + Send + Sync,
{
async fn query(
&self,
op: PredicateOperator,
literals: &[Datum],
data_type: &DataType,
) -> io::Result<RoaringTreemap> {
match op {
PredicateOperator::Eq => {
let key = serialize_datum(&literals[0], data_type);
self.query_equal(&key).await
}
PredicateOperator::Lt => {
let key = serialize_datum(&literals[0], data_type);
self.query_less_than(&key).await
}
PredicateOperator::LtEq => {
let key = serialize_datum(&literals[0], data_type);
self.query_less_or_equal(&key).await
}
PredicateOperator::Gt => {
let key = serialize_datum(&literals[0], data_type);
self.query_greater_than(&key).await
}
PredicateOperator::GtEq => {
let key = serialize_datum(&literals[0], data_type);
self.query_greater_or_equal(&key).await
}
PredicateOperator::In => {
let keys: Vec<Vec<u8>> = literals
.iter()
.map(|lit| serialize_datum(lit, data_type))
.collect();
let key_refs: Vec<&[u8]> = keys.iter().map(|k| k.as_slice()).collect();
self.query_in(&key_refs).await
}
PredicateOperator::IsNull => Ok(self.null_bitmap().clone()),
PredicateOperator::IsNotNull => self.all_non_null_rows().await,
PredicateOperator::NotEq => {
let key = serialize_datum(&literals[0], data_type);
self.query_not_equal(&key).await
}
PredicateOperator::NotIn => {
let mut all_non_null = self.all_non_null_rows().await?;
let keys: Vec<Vec<u8>> = literals
.iter()
.map(|lit| serialize_datum(lit, data_type))
.collect();
let key_refs: Vec<&[u8]> = keys.iter().map(|k| k.as_slice()).collect();
let excluded = self.query_in(&key_refs).await?;
all_non_null -= excluded;
Ok(all_non_null)
}
}
}
}
/// Detected between pattern from predicate pairs.
pub(crate) struct BetweenInfo<'a> {
pub from: &'a Datum,
pub to: &'a Datum,
pub from_inclusive: bool,
pub to_inclusive: bool,
pub data_type: &'a DataType,
}
pub(crate) type ExtractBetweenResult<'a> = (
Option<BetweenInfo<'a>>,
Vec<(PredicateOperator, &'a [Datum], &'a DataType)>,
);
/// Try to extract a between pattern (lower + upper bound) from predicates.
/// Returns (between_info, remaining_predicates).
pub(crate) fn extract_between<'a>(
predicates: &[(PredicateOperator, &'a [Datum], &'a DataType)],
) -> ExtractBetweenResult<'a> {
if predicates.len() < 2 {
return (None, predicates.to_vec());
}
let mut lower: Option<(usize, bool)> = None; // (index, inclusive)
let mut upper: Option<(usize, bool)> = None;
for (i, (op, literals, _)) in predicates.iter().enumerate() {
if literals.len() != 1 {
continue;
}
match op {
PredicateOperator::GtEq if lower.is_none() => lower = Some((i, true)),
PredicateOperator::Gt if lower.is_none() => lower = Some((i, false)),
PredicateOperator::LtEq if upper.is_none() => upper = Some((i, true)),
PredicateOperator::Lt if upper.is_none() => upper = Some((i, false)),
_ => {}
}
}
match (lower, upper) {
(Some((li, from_inclusive)), Some((ui, to_inclusive))) => {
let between = BetweenInfo {
from: &predicates[li].1[0],
to: &predicates[ui].1[0],
from_inclusive,
to_inclusive,
data_type: predicates[li].2,
};
let remaining: Vec<_> = predicates
.iter()
.enumerate()
.filter(|(i, _)| *i != li && *i != ui)
.map(|(_, p)| *p)
.collect();
(Some(between), remaining)
}
_ => (None, predicates.to_vec()),
}
}