use crate::storage::query::ast::FieldRef;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PathKeyDirection {
Ascending,
Descending,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PathKeyNulls {
First,
Last,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PathKey {
pub field: FieldRef,
pub direction: PathKeyDirection,
pub nulls: PathKeyNulls,
}
impl PathKey {
pub fn asc(field: FieldRef) -> Self {
Self {
field,
direction: PathKeyDirection::Ascending,
nulls: PathKeyNulls::Last,
}
}
pub fn desc(field: FieldRef) -> Self {
Self {
field,
direction: PathKeyDirection::Descending,
nulls: PathKeyNulls::First,
}
}
pub fn same_column_and_direction(&self, other: &PathKey) -> bool {
self.field == other.field && self.direction == other.direction
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PathKeys {
pub keys: Vec<PathKey>,
}
impl PathKeys {
pub fn unordered() -> Self {
Self { keys: Vec::new() }
}
pub fn asc(field: FieldRef) -> Self {
Self {
keys: vec![PathKey::asc(field)],
}
}
pub fn desc(field: FieldRef) -> Self {
Self {
keys: vec![PathKey::desc(field)],
}
}
pub fn is_unordered(&self) -> bool {
self.keys.is_empty()
}
pub fn prefix_match(&self, required: &PathKeys) -> usize {
let mut matched = 0;
for (mine, req) in self.keys.iter().zip(required.keys.iter()) {
if mine.same_column_and_direction(req) {
matched += 1;
} else {
break;
}
}
matched
}
pub fn satisfies(&self, required: &PathKeys) -> bool {
if required.keys.len() > self.keys.len() {
return false;
}
self.prefix_match(required) == required.keys.len()
}
pub fn appended(&self, key: PathKey) -> Self {
let mut keys = self.keys.clone();
keys.push(key);
Self { keys }
}
pub fn truncated(&self, n: usize) -> Self {
Self {
keys: self.keys.iter().take(n).cloned().collect(),
}
}
pub fn len(&self) -> usize {
self.keys.len()
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortStrategy {
None,
Incremental { prefix_len: usize },
Full,
}
pub fn plan_sort(input_order: &PathKeys, required_order: &PathKeys) -> SortStrategy {
if required_order.is_unordered() {
return SortStrategy::None;
}
if input_order.satisfies(required_order) {
return SortStrategy::None;
}
let prefix = input_order.prefix_match(required_order);
if prefix == 0 {
return SortStrategy::Full;
}
SortStrategy::Incremental { prefix_len: prefix }
}