use std::sync::atomic::{AtomicI64, Ordering};
use rudb_common::{LogicalType, Result};
use rudb_plan::{CompareOp, Expr, ExprRef, Plan};
use rudb_vector::Vector;
use crate::lookup::{Lookup, MISS};
use crate::schema::Schema;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Spread {
pub(crate) driving: usize,
pub(crate) gathered: usize,
op: CompareOp,
}
impl Spread {
pub(crate) fn of(
plan: &Plan,
residual: &[ExprRef],
combined: &Schema,
width: usize,
) -> Option<Self> {
let [expr] = residual else { return None };
let Expr::Compare { op, left, right } = *plan.expr(*expr) else { return None };
let place = |expr: ExprRef| match *plan.expr(expr) {
Expr::Column(binding) => combined.position_of(binding),
_ => None,
};
let (left, right) = (place(left)?, place(right)?);
let types = combined.types();
let (Some(left_type), Some(right_type)) = (types.get(left), types.get(right)) else {
return None;
};
if left_type != right_type || !ordered(left_type) {
return None;
}
let (driving, gathered, op) = match (left < width, right < width) {
(false, true) => (right, left - width, op),
(true, false) => (left, right - width, flipped(op)?),
_ => return None,
};
matches!(
op,
CompareOp::NotEqual
| CompareOp::Less
| CompareOp::LessOrEqual
| CompareOp::Greater
| CompareOp::GreaterOrEqual
)
.then_some(Self { driving, gathered, op })
}
fn meets(self, g: i64, low: i64, high: i64) -> bool {
match self.op {
CompareOp::NotEqual => low != g || high != g,
CompareOp::Less => high > g,
CompareOp::LessOrEqual => high >= g,
CompareOp::Greater => low < g,
CompareOp::GreaterOrEqual => low <= g,
_ => false,
}
}
}
fn ordered(ty: &LogicalType) -> bool {
matches!(
ty,
LogicalType::TinyInt
| LogicalType::SmallInt
| LogicalType::Integer
| LogicalType::BigInt
| LogicalType::Date
)
}
fn flipped(op: CompareOp) -> Option<CompareOp> {
Some(match op {
CompareOp::NotEqual => CompareOp::NotEqual,
CompareOp::Less => CompareOp::Greater,
CompareOp::LessOrEqual => CompareOp::GreaterOrEqual,
CompareOp::Greater => CompareOp::Less,
CompareOp::GreaterOrEqual => CompareOp::LessOrEqual,
_ => return None,
})
}
#[derive(Debug)]
pub(crate) struct Extents {
low: Vec<AtomicI64>,
high: Vec<AtomicI64>,
values: Vec<i64>,
nulls: Vec<bool>,
}
impl Extents {
pub(crate) fn new(slots: usize, column: &Vector) -> Option<Self> {
let mut values = Vec::new();
if !column.signed_block(&mut values) {
return None;
}
let nulls = if column.none_null() {
Vec::new()
} else {
(0..values.len()).map(|row| column.is_null_at(row)).collect()
};
Some(Self {
low: (0..slots).map(|_| AtomicI64::new(i64::MAX)).collect(),
high: (0..slots).map(|_| AtomicI64::new(i64::MIN)).collect(),
values,
nulls,
})
}
pub(crate) fn footprint(&self) -> u64 {
let bytes = self.low.len() * 2 * size_of::<AtomicI64>()
+ self.values.len() * size_of::<i64>()
+ self.nulls.len();
u64::try_from(bytes).unwrap_or(u64::MAX)
}
pub(crate) fn widen(
&self,
column: &Vector,
slots: &[usize],
block: &mut Vec<i64>,
) -> Result<bool> {
if column.signed_block(block) {
return Ok(self.read(column, slots, block));
}
let flat = column.flatten()?;
Ok(flat.signed_block(block) && self.read(&flat, slots, block))
}
fn read(&self, column: &Vector, slots: &[usize], block: &[i64]) -> bool {
if block.len() < slots.len() {
return false;
}
let nullable = !column.none_null();
for (row, (&slot, &value)) in slots.iter().zip(block.iter()).enumerate() {
if slot == MISS || nullable && column.is_null_at(row) {
continue;
}
let (Some(low), Some(high)) = (self.low.get(slot), self.high.get(slot)) else {
continue;
};
if value < low.load(Ordering::Relaxed) {
low.fetch_min(value, Ordering::Relaxed);
}
if value > high.load(Ordering::Relaxed) {
high.fetch_max(value, Ordering::Relaxed);
}
}
true
}
pub(crate) fn mark(&self, spread: Spread, index: &Lookup, bits: &mut [u64]) {
let mut chain = Vec::new();
for (slot, (low, high)) in self.low.iter().zip(&self.high).enumerate() {
let (low, high) = (low.load(Ordering::Relaxed), high.load(Ordering::Relaxed));
if low > high {
continue;
}
index.matches(slot, &mut chain);
for &row in &chain {
let at = row as usize;
let Some(&g) = self.values.get(at) else { continue };
if self.nulls.get(at).copied().unwrap_or(false) || !spread.meets(g, low, high) {
continue;
}
if let Some(word) = bits.get_mut(at / u64::BITS as usize) {
*word |= 1 << (at % u64::BITS as usize);
}
}
}
}
}