use core::fmt;
use super::expression::Expr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OrderDirection {
#[default]
Asc,
Desc,
}
impl OrderDirection {
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Asc => "ASC",
Self::Desc => "DESC",
}
}
}
impl fmt::Display for OrderDirection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NullOrdering {
First,
Last,
}
impl NullOrdering {
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Self::First => "NULLS FIRST",
Self::Last => "NULLS LAST",
}
}
}
impl fmt::Display for NullOrdering {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct OrderBy {
pub expr: Expr,
pub direction: OrderDirection,
pub nulls: Option<NullOrdering>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JoinType {
Inner,
Left,
Right,
Full,
Cross,
}
impl JoinType {
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Inner => "INNER JOIN",
Self::Left => "LEFT JOIN",
Self::Right => "RIGHT JOIN",
Self::Full => "FULL JOIN",
Self::Cross => "CROSS JOIN",
}
}
}
impl fmt::Display for JoinType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct JoinClause {
pub join_type: JoinType,
pub table: TableRef,
pub on: Option<Expr>,
pub using: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TableRef {
Table {
schema: Option<String>,
name: String,
alias: Option<String>,
},
Subquery {
query: Box<SelectStatement>,
alias: String,
},
Join {
left: Box<TableRef>,
join: Box<JoinClause>,
},
}
impl TableRef {
#[must_use]
pub fn table(name: impl Into<String>) -> Self {
Self::Table {
schema: None,
name: name.into(),
alias: None,
}
}
#[must_use]
pub fn with_schema(schema: impl Into<String>, name: impl Into<String>) -> Self {
Self::Table {
schema: Some(schema.into()),
name: name.into(),
alias: None,
}
}
#[must_use]
pub fn alias(self, alias: impl Into<String>) -> Self {
match self {
Self::Table { schema, name, .. } => Self::Table {
schema,
name,
alias: Some(alias.into()),
},
Self::Subquery { query, .. } => Self::Subquery {
query,
alias: alias.into(),
},
Self::Join { left, join } => Self::Join {
left: Box::new((*left).alias(alias)),
join,
},
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SelectStatement {
pub distinct: bool,
pub columns: Vec<SelectColumn>,
pub from: Option<TableRef>,
pub where_clause: Option<Expr>,
pub group_by: Vec<Expr>,
pub having: Option<Expr>,
pub order_by: Vec<OrderBy>,
pub limit: Option<Expr>,
pub offset: Option<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SelectColumn {
pub expr: Expr,
pub alias: Option<String>,
}
impl SelectColumn {
#[must_use]
pub fn new(expr: Expr) -> Self {
Self { expr, alias: None }
}
#[must_use]
pub fn with_alias(expr: Expr, alias: impl Into<String>) -> Self {
Self {
expr,
alias: Some(alias.into()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct InsertStatement {
pub schema: Option<String>,
pub table: String,
pub columns: Vec<String>,
pub values: InsertSource,
pub on_conflict: Option<OnConflict>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum InsertSource {
Values(Vec<Vec<Expr>>),
Query(Box<SelectStatement>),
DefaultValues,
}
#[derive(Debug, Clone, PartialEq)]
pub struct OnConflict {
pub columns: Vec<String>,
pub action: ConflictAction,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConflictAction {
DoNothing,
DoUpdate(Vec<UpdateAssignment>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct UpdateStatement {
pub schema: Option<String>,
pub table: String,
pub alias: Option<String>,
pub assignments: Vec<UpdateAssignment>,
pub from: Option<TableRef>,
pub where_clause: Option<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct UpdateAssignment {
pub column: String,
pub value: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DeleteStatement {
pub schema: Option<String>,
pub table: String,
pub alias: Option<String>,
pub where_clause: Option<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
Select(SelectStatement),
Insert(InsertStatement),
Update(UpdateStatement),
Delete(DeleteStatement),
}
impl fmt::Display for OrderBy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.expr, self.direction)?;
if let Some(nulls) = &self.nulls {
write!(f, " {nulls}")?;
}
Ok(())
}
}
impl fmt::Display for JoinClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.join_type, self.table)?;
if let Some(on) = &self.on {
write!(f, " ON {on}")?;
}
if !self.using.is_empty() {
write!(f, " USING (")?;
for (i, col) in self.using.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{col}")?;
}
write!(f, ")")?;
}
Ok(())
}
}
impl fmt::Display for TableRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Table {
schema,
name,
alias,
} => {
if let Some(s) = schema {
write!(f, "{s}.")?;
}
write!(f, "{name}")?;
if let Some(a) = alias {
write!(f, " AS {a}")?;
}
Ok(())
}
Self::Subquery { query, alias } => {
write!(f, "({query}) AS {alias}")
}
Self::Join { left, join } => {
write!(f, "{left} {join}")
}
}
}
}
impl fmt::Display for SelectColumn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.expr)?;
if let Some(a) = &self.alias {
write!(f, " AS {a}")?;
}
Ok(())
}
}
impl fmt::Display for SelectStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SELECT")?;
if self.distinct {
write!(f, " DISTINCT")?;
}
for (i, col) in self.columns.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {col}")?;
}
if let Some(from) = &self.from {
write!(f, " FROM {from}")?;
}
if let Some(w) = &self.where_clause {
write!(f, " WHERE {w}")?;
}
if !self.group_by.is_empty() {
write!(f, " GROUP BY")?;
for (i, g) in self.group_by.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {g}")?;
}
}
if let Some(h) = &self.having {
write!(f, " HAVING {h}")?;
}
if !self.order_by.is_empty() {
write!(f, " ORDER BY")?;
for (i, o) in self.order_by.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {o}")?;
}
}
if let Some(l) = &self.limit {
write!(f, " LIMIT {l}")?;
}
if let Some(o) = &self.offset {
write!(f, " OFFSET {o}")?;
}
Ok(())
}
}
impl fmt::Display for InsertSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Values(rows) => {
write!(f, "VALUES")?;
for (i, row) in rows.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " (")?;
for (j, val) in row.iter().enumerate() {
if j > 0 {
write!(f, ", ")?;
}
write!(f, "{val}")?;
}
write!(f, ")")?;
}
Ok(())
}
Self::Query(q) => write!(f, "{q}"),
Self::DefaultValues => write!(f, "DEFAULT VALUES"),
}
}
}
impl fmt::Display for OnConflict {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ON CONFLICT (")?;
for (i, col) in self.columns.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{col}")?;
}
write!(f, ") {}", self.action)
}
}
impl fmt::Display for ConflictAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DoNothing => write!(f, "DO NOTHING"),
Self::DoUpdate(assignments) => {
write!(f, "DO UPDATE SET")?;
for (i, a) in assignments.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {a}")?;
}
Ok(())
}
}
}
}
impl fmt::Display for InsertStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "INSERT INTO ")?;
if let Some(s) = &self.schema {
write!(f, "{s}.")?;
}
write!(f, "{}", self.table)?;
if !self.columns.is_empty() {
write!(f, " (")?;
for (i, col) in self.columns.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{col}")?;
}
write!(f, ")")?;
}
write!(f, " {}", self.values)?;
if let Some(oc) = &self.on_conflict {
write!(f, " {oc}")?;
}
Ok(())
}
}
impl fmt::Display for UpdateAssignment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} = {}", self.column, self.value)
}
}
impl fmt::Display for UpdateStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UPDATE ")?;
if let Some(s) = &self.schema {
write!(f, "{s}.")?;
}
write!(f, "{}", self.table)?;
if let Some(a) = &self.alias {
write!(f, " AS {a}")?;
}
write!(f, " SET")?;
for (i, a) in self.assignments.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {a}")?;
}
if let Some(from) = &self.from {
write!(f, " FROM {from}")?;
}
if let Some(w) = &self.where_clause {
write!(f, " WHERE {w}")?;
}
Ok(())
}
}
impl fmt::Display for DeleteStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DELETE FROM ")?;
if let Some(s) = &self.schema {
write!(f, "{s}.")?;
}
write!(f, "{}", self.table)?;
if let Some(a) = &self.alias {
write!(f, " AS {a}")?;
}
if let Some(w) = &self.where_clause {
write!(f, " WHERE {w}")?;
}
Ok(())
}
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Select(s) => write!(f, "{s}"),
Self::Insert(i) => write!(f, "{i}"),
Self::Update(u) => write!(f, "{u}"),
Self::Delete(d) => write!(f, "{d}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_order_direction() {
assert_eq!(OrderDirection::Asc.as_str(), "ASC");
assert_eq!(OrderDirection::Desc.as_str(), "DESC");
}
#[test]
fn test_join_type() {
assert_eq!(JoinType::Inner.as_str(), "INNER JOIN");
assert_eq!(JoinType::Left.as_str(), "LEFT JOIN");
}
#[test]
fn test_table_ref_builder() {
let table = TableRef::table("users").alias("u");
assert!(
matches!(table, TableRef::Table { name, alias, .. } if name == "users" && alias == Some(String::from("u")))
);
}
}