use crate::value::Value;
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlStatement {
Select(SqlSelectStatement),
Delete(SqlDeleteStatement),
Insert(SqlInsertStatement),
Update(SqlUpdateStatement),
Explain(SqlExplainStatement),
Describe(SqlDescribeStatement),
ShowIndexes(SqlShowIndexesStatement),
ShowColumns(SqlShowColumnsStatement),
ShowEntities(SqlShowEntitiesStatement),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlProjection {
All,
Items(Vec<SqlSelectItem>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlSelectItem {
Field(String),
Aggregate(SqlAggregateCall),
TextFunction(SqlTextFunctionCall),
Arithmetic(SqlArithmeticProjectionCall),
Round(SqlRoundProjectionCall),
Expr(SqlExpr),
}
#[allow(
dead_code,
reason = "0.91 introduces the SQL expression boundary before searched CASE parser admission"
)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SqlExprUnaryOp {
Not,
}
#[allow(
dead_code,
reason = "0.91 introduces the SQL expression boundary before searched CASE parser admission"
)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SqlExprBinaryOp {
Or,
And,
Eq,
Ne,
Lt,
Lte,
Gt,
Gte,
Add,
Sub,
Mul,
Div,
}
#[allow(
dead_code,
reason = "0.91 introduces the SQL expression boundary before searched CASE parser admission"
)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlCaseArm {
pub(crate) condition: SqlExpr,
pub(crate) result: SqlExpr,
}
#[allow(
dead_code,
reason = "0.91 introduces the SQL expression boundary before searched CASE parser admission"
)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlExpr {
Field(String),
Aggregate(SqlAggregateCall),
Literal(Value),
TextFunction(SqlTextFunctionCall),
Membership {
expr: Box<Self>,
values: Vec<Value>,
negated: bool,
},
NullTest {
expr: Box<Self>,
negated: bool,
},
FunctionCall {
function: SqlTextFunction,
args: Vec<Self>,
},
Round(SqlRoundProjectionCall),
Unary {
op: SqlExprUnaryOp,
expr: Box<Self>,
},
Binary {
op: SqlExprBinaryOp,
left: Box<Self>,
right: Box<Self>,
},
Case {
arms: Vec<SqlCaseArm>,
else_expr: Option<Box<Self>>,
},
}
impl SqlExpr {
#[must_use]
pub(crate) fn from_select_item(item: &SqlSelectItem) -> Self {
match item {
SqlSelectItem::Field(field) => Self::Field(field.clone()),
SqlSelectItem::Aggregate(aggregate) => Self::Aggregate(aggregate.clone()),
SqlSelectItem::TextFunction(call) => Self::TextFunction(call.clone()),
SqlSelectItem::Arithmetic(call) => Self::from_arithmetic_call(call),
SqlSelectItem::Round(call) => Self::Round(call.clone()),
SqlSelectItem::Expr(expr) => expr.clone(),
}
}
#[must_use]
pub(crate) fn from_projection_operand(operand: &SqlProjectionOperand) -> Self {
match operand {
SqlProjectionOperand::Field(field) => Self::Field(field.clone()),
SqlProjectionOperand::Aggregate(aggregate) => Self::Aggregate(aggregate.clone()),
SqlProjectionOperand::Literal(literal) => Self::Literal(literal.clone()),
SqlProjectionOperand::Arithmetic(call) => Self::from_arithmetic_call(call.as_ref()),
}
}
#[must_use]
pub(crate) fn from_aggregate_input_expr(expr: &SqlAggregateInputExpr) -> Self {
match expr {
SqlAggregateInputExpr::Field(field) => Self::Field(field.clone()),
SqlAggregateInputExpr::Literal(literal) => Self::Literal(literal.clone()),
SqlAggregateInputExpr::Arithmetic(call) => Self::from_arithmetic_call(call),
SqlAggregateInputExpr::Round(call) => Self::Round(call.clone()),
SqlAggregateInputExpr::Expr(expr) => expr.clone(),
}
}
#[must_use]
pub(crate) fn contains_aggregate(&self) -> bool {
match self {
Self::Aggregate(_) => true,
Self::Field(_) | Self::Literal(_) | Self::TextFunction(_) => false,
Self::Membership { expr, .. }
| Self::NullTest { expr, .. }
| Self::Unary { expr, .. } => expr.contains_aggregate(),
Self::FunctionCall { args, .. } => args.iter().any(Self::contains_aggregate),
Self::Round(call) => match &call.input {
SqlRoundProjectionInput::Operand(operand) => Self::from_projection_operand(operand),
SqlRoundProjectionInput::Arithmetic(call) => Self::from_arithmetic_call(call),
}
.contains_aggregate(),
Self::Binary { left, right, .. } => {
left.contains_aggregate() || right.contains_aggregate()
}
Self::Case { arms, else_expr } => {
arms.iter().any(|arm| {
arm.condition.contains_aggregate() || arm.result.contains_aggregate()
}) || else_expr
.as_ref()
.is_some_and(|else_expr| else_expr.contains_aggregate())
}
}
}
fn from_arithmetic_call(call: &SqlArithmeticProjectionCall) -> Self {
Self::Binary {
op: match call.op {
SqlArithmeticProjectionOp::Add => SqlExprBinaryOp::Add,
SqlArithmeticProjectionOp::Sub => SqlExprBinaryOp::Sub,
SqlArithmeticProjectionOp::Mul => SqlExprBinaryOp::Mul,
SqlArithmeticProjectionOp::Div => SqlExprBinaryOp::Div,
},
left: Box::new(Self::from_projection_operand(&call.left)),
right: Box::new(Self::from_projection_operand(&call.right)),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SqlArithmeticProjectionOp {
Add,
Sub,
Mul,
Div,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlArithmeticProjectionCall {
pub(crate) left: SqlProjectionOperand,
pub(crate) op: SqlArithmeticProjectionOp,
pub(crate) right: SqlProjectionOperand,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlProjectionOperand {
Field(String),
Aggregate(SqlAggregateCall),
Literal(Value),
Arithmetic(Box<SqlArithmeticProjectionCall>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlRoundProjectionInput {
Operand(SqlProjectionOperand),
Arithmetic(SqlArithmeticProjectionCall),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlRoundProjectionCall {
pub(crate) input: SqlRoundProjectionInput,
pub(crate) scale: Value,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SqlAggregateKind {
Count,
Sum,
Avg,
Min,
Max,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlAggregateCall {
pub(crate) kind: SqlAggregateKind,
pub(crate) input: Option<Box<SqlAggregateInputExpr>>,
pub(crate) filter_expr: Option<Box<SqlExpr>>,
pub(crate) distinct: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlAggregateInputExpr {
Field(String),
Literal(Value),
Arithmetic(SqlArithmeticProjectionCall),
Round(SqlRoundProjectionCall),
Expr(SqlExpr),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SqlTextFunction {
Trim,
Ltrim,
Rtrim,
Lower,
Upper,
Length,
Left,
Right,
StartsWith,
EndsWith,
Contains,
Position,
Replace,
Substring,
}
impl SqlTextFunction {
#[must_use]
pub(crate) fn from_identifier(identifier: &str) -> Option<Self> {
const SUPPORTED_TEXT_FUNCTIONS: [(&str, SqlTextFunction); 14] = [
("trim", SqlTextFunction::Trim),
("ltrim", SqlTextFunction::Ltrim),
("rtrim", SqlTextFunction::Rtrim),
("lower", SqlTextFunction::Lower),
("upper", SqlTextFunction::Upper),
("length", SqlTextFunction::Length),
("left", SqlTextFunction::Left),
("right", SqlTextFunction::Right),
("starts_with", SqlTextFunction::StartsWith),
("ends_with", SqlTextFunction::EndsWith),
("contains", SqlTextFunction::Contains),
("position", SqlTextFunction::Position),
("replace", SqlTextFunction::Replace),
("substring", SqlTextFunction::Substring),
];
for (name, function) in SUPPORTED_TEXT_FUNCTIONS {
if identifier.eq_ignore_ascii_case(name) {
return Some(function);
}
}
None
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlTextFunctionCall {
pub(crate) function: SqlTextFunction,
pub(crate) field: String,
pub(crate) literal: Option<Value>,
pub(crate) literal2: Option<Value>,
pub(crate) literal3: Option<Value>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SqlOrderDirection {
Asc,
Desc,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlOrderTerm {
pub(crate) field: SqlExpr,
pub(crate) direction: SqlOrderDirection,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlSelectStatement {
pub(crate) entity: String,
pub(crate) projection: SqlProjection,
pub(crate) projection_aliases: Vec<Option<String>>,
pub(crate) predicate: Option<SqlExpr>,
pub(crate) distinct: bool,
pub(crate) group_by: Vec<String>,
pub(crate) having: Vec<SqlExpr>,
pub(crate) order_by: Vec<SqlOrderTerm>,
pub(crate) limit: Option<u32>,
pub(crate) offset: Option<u32>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlReturningProjection {
All,
Fields(Vec<String>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlDeleteStatement {
pub(crate) entity: String,
pub(crate) predicate: Option<SqlExpr>,
pub(crate) order_by: Vec<SqlOrderTerm>,
pub(crate) limit: Option<u32>,
pub(crate) offset: Option<u32>,
pub(crate) returning: Option<SqlReturningProjection>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlInsertSource {
Values(Vec<Vec<Value>>),
Select(Box<SqlSelectStatement>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlInsertStatement {
pub(crate) entity: String,
pub(crate) columns: Vec<String>,
pub(crate) source: SqlInsertSource,
pub(crate) returning: Option<SqlReturningProjection>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlAssignment {
pub(crate) field: String,
pub(crate) value: Value,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlUpdateStatement {
pub(crate) entity: String,
pub(crate) assignments: Vec<SqlAssignment>,
pub(crate) predicate: Option<SqlExpr>,
pub(crate) order_by: Vec<SqlOrderTerm>,
pub(crate) limit: Option<u32>,
pub(crate) offset: Option<u32>,
pub(crate) returning: Option<SqlReturningProjection>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SqlExplainMode {
Plan,
Execution,
Json,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum SqlExplainTarget {
Select(SqlSelectStatement),
Delete(SqlDeleteStatement),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlExplainStatement {
pub(crate) mode: SqlExplainMode,
pub(crate) statement: SqlExplainTarget,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlDescribeStatement {
pub(crate) entity: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlShowIndexesStatement {
pub(crate) entity: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlShowColumnsStatement {
pub(crate) entity: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct SqlShowEntitiesStatement;