use crate::{ExprRef, Slice, StrRef, ValueRef};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ColumnBinding {
pub table: u32,
pub column: u32,
}
impl ColumnBinding {
#[must_use]
pub fn new(table: u32, column: u32) -> Self {
Self { table, column }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expr {
Column(ColumnBinding),
Constant(ValueRef),
Cast {
input: ExprRef,
try_cast: bool,
},
Compare {
op: CompareOp,
left: ExprRef,
right: ExprRef,
},
Conjunction {
op: ConjunctionOp,
children: Slice,
},
Function {
name: StrRef,
args: Slice,
},
Aggregate {
name: StrRef,
args: Slice,
distinct: bool,
filter: Option<ExprRef>,
},
Case {
arms: Slice,
otherwise: Option<ExprRef>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Arm {
pub when: ExprRef,
pub then: ExprRef,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SortKey {
pub expr: ExprRef,
pub descending: bool,
pub nulls_first: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompareOp {
Equal,
NotEqual,
Less,
LessOrEqual,
Greater,
GreaterOrEqual,
DistinctFrom,
NotDistinctFrom,
}
impl CompareOp {
#[must_use]
pub fn symbol(self) -> &'static str {
match self {
Self::Equal => "=",
Self::NotEqual => "<>",
Self::Less => "<",
Self::LessOrEqual => "<=",
Self::Greater => ">",
Self::GreaterOrEqual => ">=",
Self::DistinctFrom => "IS DISTINCT FROM",
Self::NotDistinctFrom => "IS NOT DISTINCT FROM",
}
}
pub(crate) const SPELLINGS: [Self; 8] = [
Self::NotDistinctFrom,
Self::DistinctFrom,
Self::NotEqual,
Self::LessOrEqual,
Self::GreaterOrEqual,
Self::Equal,
Self::Less,
Self::Greater,
];
#[must_use]
pub fn flip(self) -> Self {
match self {
Self::Less => Self::Greater,
Self::LessOrEqual => Self::GreaterOrEqual,
Self::Greater => Self::Less,
Self::GreaterOrEqual => Self::LessOrEqual,
other => other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ConjunctionOp {
And,
Or,
}
impl ConjunctionOp {
#[must_use]
pub fn keyword(self) -> &'static str {
match self {
Self::And => "AND",
Self::Or => "OR",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flipping_a_comparison_twice_is_the_comparison() {
for op in CompareOp::SPELLINGS {
assert_eq!(op.flip().flip(), op, "{} does not flip back", op.symbol());
}
}
#[test]
fn the_equalities_are_their_own_flip() {
for op in [CompareOp::Equal, CompareOp::NotEqual, CompareOp::NotDistinctFrom] {
assert_eq!(op.flip(), op, "{} should not care about operand order", op.symbol());
}
}
#[test]
fn every_comparison_has_exactly_one_spelling() {
let mut seen: Vec<&str> = CompareOp::SPELLINGS.iter().map(|op| op.symbol()).collect();
seen.sort_unstable();
let count = seen.len();
seen.dedup();
assert_eq!(seen.len(), count, "two comparisons print the same way");
}
#[test]
fn no_spelling_is_reachable_only_after_a_prefix_of_it() {
for (index, op) in CompareOp::SPELLINGS.iter().enumerate() {
for earlier in &CompareOp::SPELLINGS[..index] {
assert!(
!op.symbol().starts_with(earlier.symbol()),
"{} is tried after {}, which is a prefix of it",
op.symbol(),
earlier.symbol()
);
}
}
}
}