use super::{AggregateValueBuffer, SQLError, Value};
use uqa_core::memory::ProductionControl;
pub struct DistinctTracker {
pub(super) values: AggregateValueBuffer,
}
impl Default for DistinctTracker {
fn default() -> Self {
Self::new(32 * 1024 * 1024)
}
}
impl DistinctTracker {
pub(super) fn new(budget_bytes: usize) -> Self {
Self {
values: AggregateValueBuffer::new(budget_bytes),
}
}
pub(super) fn is_empty(&self) -> bool {
self.values.next_sequence == 0
}
pub(super) fn insert(
&mut self,
value: &Value,
mut sort_keys: Vec<(Value, bool)>,
) -> Result<(), SQLError> {
sort_keys.push((value.clone(), false));
self.values.push(value.clone(), sort_keys)
}
pub(super) fn for_each(
&self,
mut observe: impl FnMut(&Value) -> Result<(), SQLError>,
) -> Result<(), SQLError> {
let mut previous: Option<Value> = None;
self.values.for_each_ordered(|record| {
if let Some(previous) = &previous {
if uqa_sql::expr::compare_typed_values_with_control(
previous,
&record.value,
&ProductionControl::uncontrolled(),
)?
.is_eq()
{
return Ok(());
}
}
observe(&record.value)?;
previous = Some(record.value);
Ok(())
})
}
}
pub fn value_as_f64(v: &Value) -> Result<f64, SQLError> {
match v {
Value::Int(n) => Ok(*n as f64),
Value::Float(f) => Ok(*f),
Value::Decimal(d) => d.to_f64().ok_or_else(|| {
SQLError::TypeMismatch(format!("expected number that fits float, got {v:?}"))
}),
other => Err(SQLError::TypeMismatch(format!(
"expected number, got {other:?}"
))),
}
}
pub fn value_lt(a: &Value, b: &Value) -> Result<bool, SQLError> {
Ok(match (a, b) {
(
Value::Int(_) | Value::Float(_) | Value::Decimal(_),
Value::Int(_) | Value::Float(_) | Value::Decimal(_),
) => a < b,
(Value::Str(x), Value::Str(y)) => x < y,
(Value::FixedChar(x), Value::FixedChar(y)) => x.trim_end() < y.trim_end(),
(Value::Bytes(x), Value::Bytes(y)) => x < y,
(Value::Temporal(x), Value::Temporal(y)) => x < y,
(Value::LegacyVector(left), Value::LegacyVector(right)) => {
left.compare_as_array(right).is_lt()
}
(Value::Array(_), Value::Array(_))
| (Value::List(_), Value::List(_))
| (Value::Row(_), Value::Row(_))
| (Value::Record(_), Value::Record(_)) => super::compare_extrema(a, b)?.is_lt(),
(Value::Map(x), Value::Map(y)) => x < y,
_ => false,
})
}
pub fn value_gt(a: &Value, b: &Value) -> Result<bool, SQLError> {
value_lt(b, a)
}