use crate::common::{FQName, Identifier, OrderClause, RelationElement};
use itertools::Itertools;
use std::fmt::{Display, Formatter};
#[derive(PartialEq, Debug, Clone)]
pub struct Select {
pub distinct: bool,
pub json: bool,
pub table_name: FQName,
pub columns: Vec<SelectElement>,
pub where_clause: Vec<RelationElement>,
pub order: Option<OrderClause>,
pub limit: Option<i32>,
pub filtering: bool,
}
impl Select {
pub fn select_names(&self) -> Vec<String> {
self.columns
.iter()
.filter_map(|e| {
if let SelectElement::Column(named) = e {
Some(named.to_string())
} else {
None
}
})
.collect()
}
pub fn select_alias(&self) -> Vec<Identifier> {
self.columns
.iter()
.filter_map(|e| match e {
SelectElement::Column(named) => {
Some(named.alias.clone().unwrap_or_else(|| named.name.clone()))
}
_ => None,
})
.collect()
}
}
impl Display for Select {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"SELECT {}{}{} FROM {}{}{}{}{}",
if self.distinct { "DISTINCT " } else { "" },
if self.json { "JSON " } else { "" },
self.columns.iter().join(", "),
self.table_name,
if !self.where_clause.is_empty() {
format!(" WHERE {}", self.where_clause.iter().join(" AND "))
} else {
"".to_string()
},
self.order
.as_ref()
.map_or("".to_string(), |x| format!(" ORDER BY {}", x)),
self.limit
.map_or("".to_string(), |x| format!(" LIMIT {}", x)),
if self.filtering {
" ALLOW FILTERING"
} else {
""
}
)
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum SelectElement {
Star,
Column(Named),
Function(Named),
}
impl Display for SelectElement {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SelectElement::Star => write!(f, "*"),
SelectElement::Column(named) | SelectElement::Function(named) => write!(f, "{}", named),
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct Named {
pub name: Identifier,
pub alias: Option<Identifier>,
}
impl Named {
pub fn new(name: &str, alias: &str) -> Named {
Named {
name: Identifier::parse(name),
alias: Some(Identifier::parse(alias)),
}
}
pub fn simple(name: &str) -> Named {
Named {
name: Identifier::parse(name),
alias: None,
}
}
pub fn alias_or_name(&self) -> &Identifier {
match &self.alias {
None => &self.name,
Some(alias) => alias,
}
}
}
impl Display for Named {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.alias {
None => write!(f, "{}", self.name),
Some(a) => write!(f, "{} AS {}", self.name, a),
}
}
}
#[cfg(test)]
mod tests {
use crate::select::{Named, SelectElement};
#[test]
fn test_select_element_display() {
assert_eq!("*", SelectElement::Star.to_string());
assert_eq!(
"col",
SelectElement::Column(Named::simple("col")).to_string()
);
assert_eq!(
"func",
SelectElement::Function(Named::simple("func")).to_string()
);
assert_eq!(
"col AS alias",
SelectElement::Column(Named::new("col", "alias")).to_string()
);
assert_eq!(
"func AS alias",
SelectElement::Function(Named::new("func", "alias")).to_string()
);
}
}