orql 0.1.0

A toy SQL parser for a subset of the Oracle dialect.
Documentation
use std::ops::Deref;

use super::{Condition, Function, Ident, Node, Query, Value};

/// A "value" expression.
///
/// Note: Oracle does not have a first class boolean type, therefore
/// expressions as represented by this structure don't represent comparisons;
/// these are modeled via [conditions](Condition). This separation makes the
/// AST type safe with regards to expressions and conditions being allowed
/// only in certain parts of SQL statements.
#[derive(Debug)]
pub enum Expr<'s, ID> {
    /// `(<expr>)`
    Nested(Node<Box<Expr<'s, ID>>, ID>),

    /// e.g. `(SELECT 1 FROM dual)`
    ///
    /// Scalar subqueries expression are always enclosed in parantheses (which
    /// are repesented by the enclosing [Node].)
    ///
    /// See <https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Scalar-Subquery-Expressions.html>
    SubQuery(Node<Box<Query<'s, ID>>, ID>),

    /// e.g. `-1`
    Unary(Box<UnaryExpr<'s, ID>>),

    /// e.g. `1 + 2` or `'asdf' || 'qwer'`
    Binary(Box<BinaryExpr<'s, ID>>),

    /// e.g. `CASE ... END`
    Case(Box<CaseExpr<'s, ID>>),

    /// e.g. `1` or `'string'`
    Value(Node<Value<'s>, ID>),

    /// e.g. `NVL(<expr>, expr>)`
    Function(Box<Function<'s, ID>>),

    /// an identifier, either plain or qualified, e.g. "name" or "schema.table.column"
    Identifier(Identifier<'s, ID>),
}

/// A list of expressions, e.g. `(1, 2)`. This is a comma separated list of
/// expressions enclosed in parentheses.
#[derive(Debug)]
pub struct ExprList<'s, ID>(pub Node<Vec<Expr<'s, ID>>, ID>);

impl<'s, ID> Deref for ExprList<'s, ID> {
    type Target = [Expr<'s, ID>];

    fn deref(&self) -> &Self::Target {
        &self.0.0
    }
}

/// A plain or qualified identifier
#[derive(Debug)]
pub enum Identifier<'s, ID> {
    Simple(Node<Ident<'s>, ID>),
    Qualified(Vec<Node<Ident<'s>, ID>>),
}

impl<'s, ID> From<Node<Ident<'s>, ID>> for Identifier<'s, ID> {
    fn from(value: Node<Ident<'s>, ID>) -> Self {
        Self::Simple(value)
    }
}

/// An unary expression, e.g. `-2`
#[derive(Debug)]
pub struct UnaryExpr<'s, ID> {
    pub op: Node<UnaryExprOp, ID>,
    pub expr: Expr<'s, ID>,
}

/// Unary operators
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryExprOp {
    Add,
    Sub,
}

/// A binary expression, e.g `1 + 2`
#[derive(Debug)]
pub struct BinaryExpr<'s, ID> {
    pub left: Expr<'s, ID>,
    pub op: Node<BinaryExprOp, ID>,
    pub right: Expr<'s, ID>,
}

/// Binary operators
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryExprOp {
    /// `a + b`
    Add,
    /// `a - b`
    Sub,
    /// `a * b`
    Mul,
    /// `a / b`
    Div,
    /// `a || b`
    Concat,
}

/// A `CASE WHEN ... THEN ... [ELSE ...] END` expression
#[derive(Debug)]
pub struct CaseExpr<'s, ID> {
    /// the opening `CASE` keyword
    pub case_token: Node<(), ID>,
    /// the case's `WHEN` branches
    pub when_branches: CaseWhens<'s, ID>,
    /// the case's `ELSE` branch
    pub else_branch: Option<CaseElse<'s, ID>>,
    /// the closing `END` keyword
    pub end_token: Node<(), ID>,
}

/// A [`CASE`'s](CaseExpr) `WHEN` branches
#[derive(Debug)]
pub enum CaseWhens<'s, ID> {
    /// An expression tested against a predefined list of value expressions
    /// e.g. `CASE <expr> WHEN <expr> ... WHEN <expr> ... END`
    Simple {
        case_expr: Expr<'s, ID>,
        whens: Vec<CaseWhenSimple<'s, ID>>,
    },
    /// Open case
    Searched {
        whens: Vec<CaseWhenSearched<'s, ID>>,
    },
}

/// A `... WHEN <expr> THEN <expr>` branch of a (simple case
/// expression)[CaseWhens::Simple]. This is a branch comparing a given
/// expression (value) against a specified target value expression.
#[derive(Debug)]
pub struct CaseWhenSimple<'s, ID> {
    pub when_token: Node<(), ID>,
    pub when_expr: Expr<'s, ID>,
    pub then_token: Node<(), ID>,
    pub return_expr: Expr<'s, ID>,
}

/// A `... WHEN <condition> THEN <expr>` branch of a [searched case
/// expressions](CaseWhens::Searched). This is a branch evaluating a
/// condition to determine whether its return expression is to be yielded.
#[derive(Debug)]
pub struct CaseWhenSearched<'s, ID> {
    pub when_token: Node<(), ID>,
    pub when_condition: Condition<'s, ID>,
    pub then_token: Node<(), ID>,
    pub return_expr: Expr<'s, ID>,
}

#[derive(Debug)]
pub struct CaseElse<'s, ID> {
    /// the `ELSE` token
    pub else_token: Node<(), ID>,
    /// the value expression to evaluate a whole `CASE` expression into if
    /// none of its `WHEN`s were met.
    pub return_expr: Expr<'s, ID>,
}