use std::fmt::Display;
use std::fmt::Formatter;
use derive_visitor::Drive;
use derive_visitor::DriveMut;
use educe::Educe;
use enum_as_inner::EnumAsInner;
use ethnum::i256;
use pratt::Affix;
use pratt::Associativity;
use super::ColumnRef;
use super::OrderByExpr;
use crate::ast::display_decimal_256;
use crate::ast::quote::QuotedString;
use crate::ast::write_comma_separated_list;
use crate::ast::write_dot_separated_list;
use crate::ast::Identifier;
use crate::ast::Indirection;
use crate::ast::Query;
use crate::span::merge_span;
use crate::ParseError;
use crate::Result;
use crate::Span;
#[derive(Educe, Drive, DriveMut)]
#[educe(
PartialEq(bound = false, attrs = "#[recursive::recursive]"),
Clone(bound = false, attrs = "#[recursive::recursive]"),
Debug(bound = false, attrs = "#[recursive::recursive]")
)]
pub enum Expr {
ColumnRef {
span: Span,
column: ColumnRef,
},
IsNull {
span: Span,
expr: Box<Expr>,
not: bool,
},
IsDistinctFrom {
span: Span,
left: Box<Expr>,
right: Box<Expr>,
not: bool,
},
InList {
span: Span,
expr: Box<Expr>,
list: Vec<Expr>,
not: bool,
},
InSubquery {
span: Span,
expr: Box<Expr>,
subquery: Box<Query>,
not: bool,
},
LikeSubquery {
span: Span,
expr: Box<Expr>,
subquery: Box<Query>,
modifier: SubqueryModifier,
escape: Option<String>,
},
LikeAnyWithEscape {
span: Span,
left: Box<Expr>,
right: Box<Expr>,
escape: String,
},
LikeWithEscape {
span: Span,
left: Box<Expr>,
right: Box<Expr>,
is_not: bool,
escape: String,
},
Between {
span: Span,
expr: Box<Expr>,
low: Box<Expr>,
high: Box<Expr>,
not: bool,
},
BinaryOp {
span: Span,
op: BinaryOperator,
left: Box<Expr>,
right: Box<Expr>,
},
JsonOp {
span: Span,
op: JsonOperator,
left: Box<Expr>,
right: Box<Expr>,
},
UnaryOp {
span: Span,
op: UnaryOperator,
expr: Box<Expr>,
},
Cast {
span: Span,
expr: Box<Expr>,
target_type: TypeName,
pg_style: bool,
},
TryCast {
span: Span,
expr: Box<Expr>,
target_type: TypeName,
},
Extract {
span: Span,
kind: IntervalKind,
expr: Box<Expr>,
},
DatePart {
span: Span,
kind: IntervalKind,
expr: Box<Expr>,
},
Position {
span: Span,
substr_expr: Box<Expr>,
str_expr: Box<Expr>,
},
Substring {
span: Span,
expr: Box<Expr>,
substring_from: Box<Expr>,
substring_for: Option<Box<Expr>>,
},
Trim {
span: Span,
expr: Box<Expr>,
trim_where: Option<(TrimWhere, Box<Expr>)>,
},
Literal {
span: Span,
value: Literal,
},
CountAll {
span: Span,
qualified: Vec<Indirection>,
window: Option<Window>,
},
Tuple {
span: Span,
exprs: Vec<Expr>,
},
FunctionCall {
span: Span,
func: FunctionCall,
},
Case {
span: Span,
operand: Option<Box<Expr>>,
conditions: Vec<Expr>,
results: Vec<Expr>,
else_result: Option<Box<Expr>>,
},
Exists {
span: Span,
not: bool,
subquery: Box<Query>,
},
Subquery {
span: Span,
modifier: Option<SubqueryModifier>,
subquery: Box<Query>,
},
MapAccess {
span: Span,
expr: Box<Expr>,
accessor: MapAccessor,
},
Array {
span: Span,
exprs: Vec<Expr>,
},
Map {
span: Span,
kvs: Vec<(Literal, Expr)>,
},
Interval {
span: Span,
expr: Box<Expr>,
unit: IntervalKind,
},
DateAdd {
span: Span,
unit: IntervalKind,
interval: Box<Expr>,
date: Box<Expr>,
},
DateDiff {
span: Span,
unit: IntervalKind,
date_start: Box<Expr>,
date_end: Box<Expr>,
},
DateBetween {
span: Span,
unit: IntervalKind,
date_start: Box<Expr>,
date_end: Box<Expr>,
},
DateSub {
span: Span,
unit: IntervalKind,
interval: Box<Expr>,
date: Box<Expr>,
},
DateTrunc {
span: Span,
unit: IntervalKind,
date: Box<Expr>,
},
LastDay {
span: Span,
unit: IntervalKind,
date: Box<Expr>,
},
PreviousDay {
span: Span,
unit: Weekday,
date: Box<Expr>,
},
NextDay {
span: Span,
unit: Weekday,
date: Box<Expr>,
},
Hole {
span: Span,
name: String,
},
Placeholder {
span: Span,
},
}
impl Expr {
pub fn span(&self) -> Span {
match self {
Expr::ColumnRef { span, .. }
| Expr::IsNull { span, .. }
| Expr::IsDistinctFrom { span, .. }
| Expr::InList { span, .. }
| Expr::InSubquery { span, .. }
| Expr::LikeSubquery { span, .. }
| Expr::LikeAnyWithEscape { span, .. }
| Expr::LikeWithEscape { span, .. }
| Expr::Between { span, .. }
| Expr::BinaryOp { span, .. }
| Expr::JsonOp { span, .. }
| Expr::UnaryOp { span, .. }
| Expr::Cast { span, .. }
| Expr::TryCast { span, .. }
| Expr::Extract { span, .. }
| Expr::DatePart { span, .. }
| Expr::Position { span, .. }
| Expr::Substring { span, .. }
| Expr::Trim { span, .. }
| Expr::Literal { span, .. }
| Expr::CountAll { span, .. }
| Expr::Tuple { span, .. }
| Expr::FunctionCall { span, .. }
| Expr::Case { span, .. }
| Expr::Exists { span, .. }
| Expr::Subquery { span, .. }
| Expr::MapAccess { span, .. }
| Expr::Array { span, .. }
| Expr::Map { span, .. }
| Expr::Interval { span, .. }
| Expr::DateAdd { span, .. }
| Expr::DateDiff { span, .. }
| Expr::DateBetween { span, .. }
| Expr::DateSub { span, .. }
| Expr::DateTrunc { span, .. }
| Expr::LastDay { span, .. }
| Expr::PreviousDay { span, .. }
| Expr::NextDay { span, .. }
| Expr::Hole { span, .. }
| Expr::Placeholder { span } => *span,
}
}
pub fn whole_span(&self) -> Span {
match self {
Expr::ColumnRef { span, .. } => *span,
Expr::IsNull { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::IsDistinctFrom {
span, left, right, ..
} => merge_span(merge_span(*span, left.whole_span()), right.whole_span()),
Expr::InList {
span, expr, list, ..
} => {
let mut span = merge_span(*span, expr.whole_span());
for item in list {
span = merge_span(span, item.whole_span());
}
span
}
Expr::InSubquery {
span,
expr,
subquery,
..
}
| Expr::LikeSubquery {
span,
expr,
subquery,
..
} => merge_span(merge_span(*span, expr.whole_span()), subquery.span),
Expr::Between {
span,
expr,
low,
high,
..
} => merge_span(
merge_span(*span, expr.whole_span()),
merge_span(low.whole_span(), high.whole_span()),
),
Expr::BinaryOp {
span, left, right, ..
}
| Expr::LikeWithEscape {
span, left, right, ..
}
| Expr::LikeAnyWithEscape {
span, left, right, ..
} => merge_span(merge_span(*span, left.whole_span()), right.whole_span()),
Expr::JsonOp {
span, left, right, ..
} => merge_span(merge_span(*span, left.whole_span()), right.whole_span()),
Expr::UnaryOp { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::Cast { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::TryCast { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::Extract { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::DatePart { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::Position {
span,
substr_expr,
str_expr,
..
} => merge_span(
merge_span(*span, substr_expr.whole_span()),
str_expr.whole_span(),
),
Expr::Substring {
span,
expr,
substring_from,
substring_for,
..
} => {
let mut span = merge_span(
merge_span(*span, expr.whole_span()),
substring_from.whole_span(),
);
if let Some(substring_for) = substring_for {
span = merge_span(span, substring_for.whole_span());
}
span
}
Expr::Trim { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::Literal { span, .. } => *span,
Expr::CountAll { span, .. } => *span,
Expr::Tuple { span, exprs } => {
let mut span = *span;
for expr in exprs {
span = merge_span(span, expr.whole_span());
}
span
}
Expr::FunctionCall { span, .. } => *span,
Expr::Case {
span,
operand,
conditions,
results,
else_result,
} => {
let mut span = *span;
if let Some(operand) = operand {
span = merge_span(span, operand.whole_span());
}
for (cond, res) in conditions.iter().zip(results) {
span = merge_span(merge_span(span, cond.whole_span()), res.whole_span());
}
if let Some(else_result) = else_result {
span = merge_span(span, else_result.whole_span());
}
span
}
Expr::Exists { span, subquery, .. } => merge_span(*span, subquery.span),
Expr::Subquery { span, subquery, .. } => merge_span(*span, subquery.span),
Expr::MapAccess { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::Array { span, exprs } => {
let mut span = *span;
for expr in exprs {
span = merge_span(span, expr.whole_span());
}
span
}
Expr::Map { span, kvs } => {
let mut span = *span;
for (_, v) in kvs {
span = merge_span(span, v.whole_span());
}
span
}
Expr::Interval { span, expr, .. } => merge_span(*span, expr.whole_span()),
Expr::DateAdd {
span,
interval,
date,
..
} => merge_span(merge_span(*span, interval.whole_span()), date.whole_span()),
Expr::DateDiff {
span,
date_start,
date_end,
..
} => merge_span(
merge_span(*span, date_start.whole_span()),
date_end.whole_span(),
),
Expr::DateBetween {
span,
date_start,
date_end,
..
} => merge_span(
merge_span(*span, date_start.whole_span()),
date_end.whole_span(),
),
Expr::DateSub {
span,
interval,
date,
..
} => merge_span(merge_span(*span, interval.whole_span()), date.whole_span()),
Expr::DateTrunc { span, date, .. } => merge_span(*span, date.whole_span()),
Expr::LastDay { span, date, .. } => merge_span(*span, date.whole_span()),
Expr::PreviousDay { span, date, .. } => merge_span(*span, date.whole_span()),
Expr::NextDay { span, date, .. } => merge_span(*span, date.whole_span()),
Expr::Hole { span, .. } => *span,
Expr::Placeholder { span } => *span,
}
}
pub fn all_function_like_syntaxes() -> &'static [&'static str] {
&[
"CAST",
"TRY_CAST",
"EXTRACT",
"DATE_PART",
"POSITION",
"SUBSTRING",
"TRIM",
"DATE_ADD",
"DATE_DIFF",
"DATE_SUB",
"DATE_TRUNC",
]
}
}
impl Display for Expr {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
fn needs_parentheses(parent: Option<Affix>, child: Affix, is_left: bool) -> bool {
match (parent, child) {
(Some(Affix::Infix(parent_prec, parent_assoc)), Affix::Infix(child_prec, _)) => {
if parent_prec < child_prec {
return false;
}
if parent_prec > child_prec {
return true;
}
if matches!(parent_assoc, Associativity::Left) && !is_left {
return true;
}
if matches!(parent_assoc, Associativity::Right) && is_left {
return true;
}
}
(
Some(
Affix::Infix(parent_prec, _)
| Affix::Prefix(parent_prec)
| Affix::Postfix(parent_prec),
),
Affix::Infix(child_prec, _)
| Affix::Prefix(child_prec)
| Affix::Postfix(child_prec),
) => {
return parent_prec > child_prec;
}
_ => (),
}
false
}
#[recursive::recursive]
fn write_expr(
expr: &Expr,
parent: Option<Affix>,
is_left: bool,
f: &mut Formatter,
) -> std::fmt::Result {
let affix = expr.affix();
let need_paren = needs_parentheses(parent, affix, is_left);
if need_paren {
write!(f, "(")?;
}
match expr {
Expr::ColumnRef { column, .. } => {
if f.alternate() {
write!(f, "{column:#}")?;
} else {
write!(f, "{column}")?;
}
}
Expr::IsNull { expr, not, .. } => {
write_expr(expr, Some(affix), true, f)?;
write!(f, " IS")?;
if *not {
write!(f, " NOT")?;
}
write!(f, " NULL")?;
}
Expr::IsDistinctFrom {
left, right, not, ..
} => {
write_expr(left, Some(affix), true, f)?;
write!(f, " IS")?;
if *not {
write!(f, " NOT")?;
}
write!(f, " DISTINCT FROM ")?;
write_expr(right, Some(affix), true, f)?;
}
Expr::InList {
expr, list, not, ..
} => {
write_expr(expr, Some(affix), true, f)?;
if *not {
write!(f, " NOT")?;
}
write!(f, " IN(")?;
write_comma_separated_list(f, list)?;
write!(f, ")")?;
}
Expr::InSubquery {
expr,
subquery,
not,
..
} => {
write_expr(expr, Some(affix), true, f)?;
if *not {
write!(f, " NOT")?;
}
write!(f, " IN({subquery})")?;
}
Expr::LikeSubquery {
expr,
subquery,
modifier,
escape,
..
} => {
write_expr(expr, Some(affix), true, f)?;
write!(f, " LIKE {modifier} ({subquery})")?;
if let Some(escape) = escape {
write!(f, " ESCAPE '{escape}'")?;
}
}
Expr::LikeAnyWithEscape {
left,
right,
escape,
..
} => {
write_expr(left, Some(affix), true, f)?;
write!(f, " LIKE ANY {right} ESCAPE '{escape}'")?;
}
Expr::LikeWithEscape {
left,
right,
is_not,
escape,
..
} => {
write_expr(left, Some(affix), true, f)?;
if *is_not {
write!(f, " NOT")?;
}
write!(f, " LIKE {right} ESCAPE '{escape}'")?;
}
Expr::Between {
expr,
low,
high,
not,
..
} => {
write_expr(expr, Some(affix), true, f)?;
if *not {
write!(f, " NOT")?;
}
write!(f, " BETWEEN {low} AND {high}")?;
}
Expr::UnaryOp { op, expr, .. } => {
match op {
UnaryOperator::Factorial => {
write_expr(expr, Some(affix), true, f)?;
write!(f, " {op}")?;
}
_ => {
write!(f, "{op} ")?;
write_expr(expr, Some(affix), true, f)?;
}
}
}
Expr::BinaryOp {
op, left, right, ..
} => {
write_expr(left, Some(affix), true, f)?;
write!(f, " {op} ")?;
write_expr(right, Some(affix), false, f)?;
}
Expr::JsonOp {
op, left, right, ..
} => {
write_expr(left, Some(affix), true, f)?;
write!(f, " {op} ")?;
write_expr(right, Some(affix), true, f)?;
}
Expr::Cast {
expr,
target_type,
pg_style,
..
} => {
if *pg_style {
write_expr(expr, Some(affix), true, f)?;
write!(f, "::{target_type}")?;
} else {
write!(f, "CAST({expr} AS {target_type})")?;
}
}
Expr::TryCast {
expr, target_type, ..
} => {
write!(f, "TRY_CAST({expr} AS {target_type})")?;
}
Expr::Extract {
kind: field, expr, ..
} => {
write!(f, "EXTRACT({field} FROM {expr})")?;
}
Expr::DatePart {
kind: field, expr, ..
} => {
write!(f, "DATE_PART({field}, {expr})")?;
}
Expr::Position {
substr_expr,
str_expr,
..
} => {
write!(f, "POSITION({substr_expr} IN {str_expr})")?;
}
Expr::Substring {
expr,
substring_from,
substring_for,
..
} => {
write!(f, "SUBSTRING({expr} FROM {substring_from}")?;
if let Some(substring_for) = substring_for {
write!(f, " FOR {substring_for}")?;
}
write!(f, ")")?;
}
Expr::Trim {
expr, trim_where, ..
} => {
write!(f, "TRIM(")?;
if let Some((trim_where, trim_str)) = trim_where {
write!(f, "{trim_where} {trim_str} FROM ")?;
}
write!(f, "{expr})")?;
}
Expr::Literal { value, .. } => {
write!(f, "{value}")?;
}
Expr::CountAll {
window, qualified, ..
} => {
write!(f, "COUNT(")?;
write_dot_separated_list(f, qualified)?;
write!(f, ")")?;
if let Some(window) = window {
write!(f, " OVER {window}")?;
}
}
Expr::Tuple { exprs, .. } => {
write!(f, "(")?;
write_comma_separated_list(f, exprs)?;
if exprs.len() == 1 {
write!(f, ",")?;
}
write!(f, ")")?;
}
Expr::FunctionCall { func, .. } => {
write!(f, "{func}")?;
}
Expr::Case {
operand,
conditions,
results,
else_result,
..
} => {
write!(f, "CASE")?;
if let Some(op) = operand {
write!(f, " {op} ")?;
}
for (cond, res) in conditions.iter().zip(results) {
write!(f, " WHEN {cond} THEN {res}")?;
}
if let Some(el) = else_result {
write!(f, " ELSE {el}")?;
}
write!(f, " END")?;
}
Expr::Exists { not, subquery, .. } => {
if *not {
write!(f, "NOT ")?;
}
write!(f, "EXISTS ({subquery})")?;
}
Expr::Subquery {
subquery, modifier, ..
} => {
if let Some(m) = modifier {
write!(f, "{m} ")?;
}
write!(f, "({subquery})")?;
}
Expr::MapAccess { expr, accessor, .. } => {
write_expr(expr, Some(affix), true, f)?;
match accessor {
MapAccessor::Bracket { key } => write!(f, "[{key}]")?,
MapAccessor::DotNumber { key } => write!(f, ".{key}")?,
MapAccessor::Colon { key } => write!(f, ":{key}")?,
}
}
Expr::Array { exprs, .. } => {
write!(f, "[")?;
write_comma_separated_list(f, exprs)?;
write!(f, "]")?;
}
Expr::Map { kvs, .. } => {
write!(f, "{{")?;
for (i, (k, v)) in kvs.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, "{k}:{v}")?;
}
write!(f, "}}")?;
}
Expr::Interval { expr, unit, .. } => {
write!(f, "INTERVAL {expr} {unit}")?;
}
Expr::DateAdd {
unit,
interval,
date,
..
} => {
write!(f, "DATE_ADD({unit}, {interval}, {date})")?;
}
Expr::DateDiff {
unit,
date_start,
date_end,
..
} => {
write!(f, "DATE_DIFF({unit}, {date_start}, {date_end})")?;
}
Expr::DateBetween {
unit,
date_start,
date_end,
..
} => {
write!(f, "DATE_BETWEEN({unit}, {date_start}, {date_end})")?;
}
Expr::DateSub {
unit,
interval,
date,
..
} => {
write!(f, "DATE_SUB({unit}, {interval}, {date})")?;
}
Expr::DateTrunc { unit, date, .. } => {
write!(f, "DATE_TRUNC({unit}, {date})")?;
}
Expr::LastDay { unit, date, .. } => {
write!(f, "LAST_DAY({date}, {unit})")?;
}
Expr::PreviousDay { unit, date, .. } => {
write!(f, "PREVIOUS_DAY({date}, {unit})")?;
}
Expr::NextDay { unit, date, .. } => {
write!(f, "NEXT_DAY({date}, {unit})")?;
}
Expr::Hole { name, .. } => {
write!(f, ":{name}")?;
}
Expr::Placeholder { .. } => {
write!(f, "?")?;
}
}
if need_paren {
write!(f, ")")?;
}
Ok(())
}
write_expr(self, None, true, f)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Drive, DriveMut)]
pub enum Weekday {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
}
impl Display for Weekday {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(match self {
Weekday::Sunday => "SUNDAY",
Weekday::Monday => "MONDAY",
Weekday::Tuesday => "TUESDAY",
Weekday::Wednesday => "WEDNESDAY",
Weekday::Thursday => "THURSDAY",
Weekday::Friday => "FRIDAY",
Weekday::Saturday => "SATURDAY",
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Drive, DriveMut)]
pub enum IntervalKind {
ISOYear,
Year,
Quarter,
Month,
Day,
Hour,
Minute,
Second,
Doy,
Week,
ISOWeek,
Dow,
Epoch,
MicroSecond,
ISODow,
YearWeek,
Millennium,
UnknownIntervalKind,
}
impl Display for IntervalKind {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(match self {
IntervalKind::ISOYear => "ISOYEAR",
IntervalKind::Year => "YEAR",
IntervalKind::Quarter => "QUARTER",
IntervalKind::Month => "MONTH",
IntervalKind::Day => "DAY",
IntervalKind::Hour => "HOUR",
IntervalKind::Minute => "MINUTE",
IntervalKind::Second => "SECOND",
IntervalKind::Doy => "DOY",
IntervalKind::Dow => "DOW",
IntervalKind::ISODow => "ISODOW",
IntervalKind::YearWeek => "YEARWEEK",
IntervalKind::Millennium => "MILLENNIUM",
IntervalKind::Week => "WEEK",
IntervalKind::ISOWeek => "ISOWEEK",
IntervalKind::Epoch => "EPOCH",
IntervalKind::MicroSecond => "MICROSECOND",
IntervalKind::UnknownIntervalKind => "UNKNOWNINTERVALKIND",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub enum SubqueryModifier {
Any,
All,
Some,
}
impl Display for SubqueryModifier {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
SubqueryModifier::Any => write!(f, "ANY"),
SubqueryModifier::All => write!(f, "ALL"),
SubqueryModifier::Some => write!(f, "SOME"),
}
}
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub enum Literal {
UInt64(u64),
Float64(f64),
Decimal256 {
#[drive(skip)]
value: i256,
precision: u8,
scale: u8,
},
String(String),
Boolean(bool),
Null,
}
impl Literal {
pub fn as_double(&self) -> Result<f64> {
match self {
Literal::UInt64(val) => Ok(*val as f64),
Literal::Float64(val) => Ok(*val),
Literal::Decimal256 { value, scale, .. } => {
let div = 10_f64.powi(*scale as i32);
Ok(value.as_f64() / div)
}
_ => Err(ParseError(
None,
format!("Cannot convert {:?} to double", self),
)),
}
}
}
impl Display for Literal {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
Literal::UInt64(val) => {
write!(f, "{val}")
}
Literal::Decimal256 { value, scale, .. } => {
write!(f, "{}", display_decimal_256(*value, *scale))
}
Literal::Float64(val) => {
if val.is_infinite() {
if val.is_sign_positive() {
write!(f, "'+INFINITY'::FLOAT64")
} else {
write!(f, "'-INFINITY'::FLOAT64")
}
} else if val.is_nan() {
write!(f, "'NaN'::FLOAT64")
} else {
write!(f, "{val}")
}
}
Literal::String(val) => {
write!(f, "{}", QuotedString(val, '\''))
}
Literal::Boolean(val) => {
if *val {
write!(f, "TRUE")
} else {
write!(f, "FALSE")
}
}
Literal::Null => {
write!(f, "NULL")
}
}
}
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub struct FunctionCall {
pub distinct: bool,
pub name: Identifier,
pub args: Vec<Expr>,
pub params: Vec<Expr>,
pub order_by: Vec<OrderByExpr>,
pub window: Option<WindowDesc>,
pub lambda: Option<Lambda>,
}
impl Default for FunctionCall {
fn default() -> Self {
Self {
distinct: false,
name: Identifier::from_name(None, ""),
args: vec![],
params: vec![],
order_by: vec![],
window: None,
lambda: None,
}
}
}
impl Display for FunctionCall {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let FunctionCall {
distinct,
name,
args,
params,
order_by,
window,
lambda,
} = self;
write!(f, "{name}")?;
if !params.is_empty() {
write!(f, "(")?;
write_comma_separated_list(f, params)?;
write!(f, ")")?;
}
write!(f, "(")?;
if *distinct {
write!(f, "DISTINCT ")?;
}
write_comma_separated_list(f, args)?;
if let Some(lambda) = lambda {
write!(f, ", {lambda}")?;
}
write!(f, ")")?;
if !order_by.is_empty() {
write!(f, " WITHIN GROUP ( ORDER BY ")?;
write_comma_separated_list(f, &self.order_by)?;
write!(f, " )")?;
}
if let Some(window) = window {
if let Some(ignore_null) = window.ignore_nulls {
if ignore_null {
write!(f, " IGNORE NULLS")?;
} else {
write!(f, " RESPECT NULLS")?;
}
}
write!(f, " OVER {}", window.window)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub enum MapAccessor {
Bracket { key: Box<Expr> },
DotNumber { key: u64 },
Colon { key: Identifier },
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub enum TypeName {
Boolean,
UInt8,
UInt16,
UInt32,
UInt64,
Int8,
Int16,
Int32,
Int64,
Float32,
Float64,
Decimal {
precision: u8,
scale: u8,
},
Date,
Timestamp,
Binary,
String,
Array(Box<TypeName>),
Map {
key_type: Box<TypeName>,
val_type: Box<TypeName>,
},
Bitmap,
Tuple {
fields_name: Option<Vec<Identifier>>,
fields_type: Vec<TypeName>,
},
Variant,
Geometry,
Geography,
Interval,
Vector(u64),
Nullable(Box<TypeName>),
NotNull(Box<TypeName>),
}
impl TypeName {
pub fn is_nullable(&self) -> bool {
matches!(self, TypeName::Nullable(_))
}
pub fn wrap_nullable(self) -> Self {
if !self.is_nullable() {
Self::Nullable(Box::new(self))
} else {
self
}
}
pub fn wrap_not_null(self) -> Self {
match self {
Self::NotNull(_) => self,
_ => Self::NotNull(Box::new(self)),
}
}
}
impl Display for TypeName {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
TypeName::Boolean => {
write!(f, "BOOLEAN")?;
}
TypeName::UInt8 => {
write!(f, "UInt8")?;
}
TypeName::UInt16 => {
write!(f, "UInt16")?;
}
TypeName::UInt32 => {
write!(f, "UInt32")?;
}
TypeName::UInt64 => {
write!(f, "UInt64")?;
}
TypeName::Int8 => {
write!(f, "Int8")?;
}
TypeName::Int16 => {
write!(f, "Int16")?;
}
TypeName::Int32 => {
write!(f, "Int32")?;
}
TypeName::Int64 => {
write!(f, "Int64")?;
}
TypeName::Float32 => {
write!(f, "Float32")?;
}
TypeName::Float64 => {
write!(f, "Float64")?;
}
TypeName::Decimal { precision, scale } => {
write!(f, "Decimal({}, {})", precision, scale)?;
}
TypeName::Date => {
write!(f, "DATE")?;
}
TypeName::Timestamp => {
write!(f, "TIMESTAMP")?;
}
TypeName::Binary => {
write!(f, "BINARY")?;
}
TypeName::String => {
write!(f, "STRING")?;
}
TypeName::Array(ty) => {
write!(f, "ARRAY({})", ty)?;
}
TypeName::Map { key_type, val_type } => {
write!(f, "MAP({}, {})", key_type, val_type)?;
}
TypeName::Bitmap => {
write!(f, "BITMAP")?;
}
TypeName::Tuple {
fields_name,
fields_type,
} => {
write!(f, "TUPLE(")?;
let mut first = true;
match fields_name {
Some(fields_name) => {
for (name, ty) in fields_name.iter().zip(fields_type.iter()) {
if !first {
write!(f, ", ")?;
}
first = false;
write!(f, "{} {}", name, ty)?;
}
}
None => {
for ty in fields_type.iter() {
if !first {
write!(f, ", ")?;
}
first = false;
write!(f, "{}", ty)?;
}
}
}
write!(f, ")")?;
}
TypeName::Variant => {
write!(f, "VARIANT")?;
}
TypeName::Geometry => {
write!(f, "GEOMETRY")?;
}
TypeName::Geography => {
write!(f, "GEOGRAPHY")?;
}
TypeName::Nullable(ty) => {
write!(f, "{} NULL", ty)?;
}
TypeName::NotNull(ty) => {
write!(f, "{} NOT NULL", ty)?;
}
TypeName::Interval => {
write!(f, "INTERVAL")?;
}
TypeName::Vector(dimension) => {
write!(f, "VECTOR({dimension})")?;
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub enum TrimWhere {
Both,
Leading,
Trailing,
}
impl Display for TrimWhere {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str(match self {
TrimWhere::Both => "BOTH",
TrimWhere::Leading => "LEADING",
TrimWhere::Trailing => "TRAILING",
})
}
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub struct WindowDesc {
pub ignore_nulls: Option<bool>,
pub window: Window,
}
#[derive(Debug, Clone, PartialEq, EnumAsInner, Drive, DriveMut)]
pub enum Window {
WindowReference(WindowRef),
WindowSpec(WindowSpec),
}
impl Display for Window {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match *self {
Window::WindowReference(ref window_ref) => write!(f, "{}", window_ref),
Window::WindowSpec(ref window_spec) => write!(f, "{}", window_spec),
}
}
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub struct WindowDefinition {
pub name: Identifier,
pub spec: WindowSpec,
}
impl Display for WindowDefinition {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{} AS {}", self.name, self.spec)
}
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub struct WindowRef {
pub window_name: Identifier,
}
impl Display for WindowRef {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self.window_name)
}
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub struct WindowSpec {
pub existing_window_name: Option<Identifier>,
pub partition_by: Vec<Expr>,
pub order_by: Vec<OrderByExpr>,
pub window_frame: Option<WindowFrame>,
}
impl Display for WindowSpec {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "(")?;
let mut write = false;
if let Some(existing_window_name) = &self.existing_window_name {
write!(f, "{existing_window_name}")?;
write = true;
}
if !self.partition_by.is_empty() {
if write {
write!(f, " ")?;
}
write = true;
write!(f, "PARTITION BY ")?;
write_comma_separated_list(f, &self.partition_by)?;
}
if !self.order_by.is_empty() {
if write {
write!(f, " ")?;
}
write = true;
write!(f, "ORDER BY ")?;
write_comma_separated_list(f, &self.order_by)?;
}
if let Some(frame) = &self.window_frame {
if write {
write!(f, " ")?;
}
match frame.units {
WindowFrameUnits::Rows => {
write!(f, "ROWS")?;
}
WindowFrameUnits::Range => {
write!(f, "RANGE")?;
}
}
let format_frame = |frame: &WindowFrameBound| -> String {
match frame {
WindowFrameBound::CurrentRow => "CURRENT ROW".to_string(),
WindowFrameBound::Preceding(None) => "UNBOUNDED PRECEDING".to_string(),
WindowFrameBound::Following(None) => "UNBOUNDED FOLLOWING".to_string(),
WindowFrameBound::Preceding(Some(n)) => format!("{} PRECEDING", n),
WindowFrameBound::Following(Some(n)) => format!("{} FOLLOWING", n),
}
};
write!(
f,
" BETWEEN {} AND {}",
format_frame(&frame.start_bound),
format_frame(&frame.end_bound)
)?
}
write!(f, ")")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub struct WindowFrame {
pub units: WindowFrameUnits,
pub start_bound: WindowFrameBound,
pub end_bound: WindowFrameBound,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumAsInner, Drive, DriveMut)]
pub enum WindowFrameUnits {
Rows,
Range,
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub enum WindowFrameBound {
CurrentRow,
Preceding(Option<Box<Expr>>),
Following(Option<Box<Expr>>),
}
#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
pub struct Lambda {
pub params: Vec<Identifier>,
pub expr: Box<Expr>,
}
impl Display for Lambda {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
if self.params.len() == 1 {
write!(f, "{}", self.params[0])?;
} else {
write!(f, "(")?;
write_comma_separated_list(f, self.params.clone())?;
write!(f, ")")?;
}
write!(f, " -> {}", self.expr)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub enum BinaryOperator {
Plus,
Minus,
Multiply,
Div,
Divide,
IntDiv,
Modulo,
StringConcat,
Gt,
Lt,
Gte,
Lte,
Eq,
NotEq,
Caret,
And,
Or,
Xor,
Like(Option<String>),
NotLike(Option<String>),
LikeAny(Option<String>),
Regexp,
RLike,
NotRegexp,
NotRLike,
SoundsLike,
BitwiseOr,
BitwiseAnd,
BitwiseXor,
BitwiseShiftLeft,
BitwiseShiftRight,
CosineDistance,
L1Distance,
L2Distance,
}
impl BinaryOperator {
pub fn to_contrary(&self) -> Result<Self> {
match &self {
BinaryOperator::Gt => Ok(BinaryOperator::Lte),
BinaryOperator::Lt => Ok(BinaryOperator::Gte),
BinaryOperator::Gte => Ok(BinaryOperator::Lt),
BinaryOperator::Lte => Ok(BinaryOperator::Gt),
BinaryOperator::Eq => Ok(BinaryOperator::NotEq),
BinaryOperator::NotEq => Ok(BinaryOperator::Eq),
_ => Err(ParseError(
None,
format!("Converting {self} to its contrary is not currently supported"),
)),
}
}
pub fn to_func_name(&self) -> String {
match self {
BinaryOperator::StringConcat => "concat".to_string(),
BinaryOperator::BitwiseOr => "bit_or".to_string(),
BinaryOperator::BitwiseAnd => "bit_and".to_string(),
BinaryOperator::BitwiseXor => "bit_xor".to_string(),
BinaryOperator::BitwiseShiftLeft => "bit_shift_left".to_string(),
BinaryOperator::BitwiseShiftRight => "bit_shift_right".to_string(),
BinaryOperator::Caret => "pow".to_string(),
BinaryOperator::CosineDistance => "cosine_distance".to_string(),
BinaryOperator::L1Distance => "l1_distance".to_string(),
BinaryOperator::L2Distance => "l2_distance".to_string(),
BinaryOperator::LikeAny(_) => "like_any".to_string(),
BinaryOperator::Like(_) => "like".to_string(),
_ => {
let name = format!("{:?}", self);
name.to_lowercase()
}
}
}
}
impl Display for BinaryOperator {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
BinaryOperator::Plus => {
write!(f, "+")
}
BinaryOperator::Minus => {
write!(f, "-")
}
BinaryOperator::Multiply => {
write!(f, "*")
}
BinaryOperator::Div => {
write!(f, "DIV")
}
BinaryOperator::Divide => {
write!(f, "/")
}
BinaryOperator::IntDiv => {
write!(f, "//")
}
BinaryOperator::Modulo => {
write!(f, "%")
}
BinaryOperator::StringConcat => {
write!(f, "||")
}
BinaryOperator::Gt => {
write!(f, ">")
}
BinaryOperator::Lt => {
write!(f, "<")
}
BinaryOperator::Gte => {
write!(f, ">=")
}
BinaryOperator::Lte => {
write!(f, "<=")
}
BinaryOperator::Eq => {
write!(f, "=")
}
BinaryOperator::NotEq => {
write!(f, "<>")
}
BinaryOperator::Caret => {
write!(f, "^")
}
BinaryOperator::And => {
write!(f, "AND")
}
BinaryOperator::Or => {
write!(f, "OR")
}
BinaryOperator::Xor => {
write!(f, "XOR")
}
BinaryOperator::Like(_) => {
write!(f, "LIKE")
}
BinaryOperator::LikeAny(_) => {
write!(f, "LIKE ANY")
}
BinaryOperator::NotLike(_) => {
write!(f, "NOT LIKE")
}
BinaryOperator::Regexp => {
write!(f, "REGEXP")
}
BinaryOperator::RLike => {
write!(f, "RLIKE")
}
BinaryOperator::NotRegexp => {
write!(f, "NOT REGEXP")
}
BinaryOperator::NotRLike => {
write!(f, "NOT RLIKE")
}
BinaryOperator::SoundsLike => {
write!(f, "SOUNDS LIKE")
}
BinaryOperator::BitwiseOr => {
write!(f, "|")
}
BinaryOperator::BitwiseAnd => {
write!(f, "&")
}
BinaryOperator::BitwiseXor => {
write!(f, "#")
}
BinaryOperator::BitwiseShiftLeft => {
write!(f, "<<")
}
BinaryOperator::BitwiseShiftRight => {
write!(f, ">>")
}
BinaryOperator::CosineDistance => {
write!(f, "<=>")
}
BinaryOperator::L1Distance => {
write!(f, "<+>")
}
BinaryOperator::L2Distance => {
write!(f, "<->")
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub enum JsonOperator {
Arrow,
LongArrow,
HashArrow,
HashLongArrow,
Question,
QuestionOr,
QuestionAnd,
AtArrow,
ArrowAt,
AtQuestion,
AtAt,
HashMinus,
}
impl JsonOperator {
pub fn to_func_name(&self) -> String {
match self {
JsonOperator::Arrow => "get".to_string(),
JsonOperator::LongArrow => "get_string".to_string(),
JsonOperator::HashArrow => "get_by_keypath".to_string(),
JsonOperator::HashLongArrow => "get_by_keypath_string".to_string(),
JsonOperator::Question => "json_exists_key".to_string(),
JsonOperator::QuestionOr => "json_exists_any_keys".to_string(),
JsonOperator::QuestionAnd => "json_exists_all_keys".to_string(),
JsonOperator::AtArrow => "json_contains_in_left".to_string(),
JsonOperator::ArrowAt => "json_contains_in_right".to_string(),
JsonOperator::AtQuestion => "json_path_exists".to_string(),
JsonOperator::AtAt => "json_path_match".to_string(),
JsonOperator::HashMinus => "delete_by_keypath".to_string(),
}
}
}
impl Display for JsonOperator {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
JsonOperator::Arrow => {
write!(f, "->")
}
JsonOperator::LongArrow => {
write!(f, "->>")
}
JsonOperator::HashArrow => {
write!(f, "#>")
}
JsonOperator::HashLongArrow => {
write!(f, "#>>")
}
JsonOperator::Question => {
write!(f, "?")
}
JsonOperator::QuestionOr => {
write!(f, "?|")
}
JsonOperator::QuestionAnd => {
write!(f, "?&")
}
JsonOperator::AtArrow => {
write!(f, "@>")
}
JsonOperator::ArrowAt => {
write!(f, "<@")
}
JsonOperator::AtQuestion => {
write!(f, "@?")
}
JsonOperator::AtAt => {
write!(f, "@@")
}
JsonOperator::HashMinus => {
write!(f, "#-")
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
pub enum UnaryOperator {
Plus,
Minus,
Not,
Factorial,
SquareRoot,
CubeRoot,
Abs,
BitwiseNot,
}
impl Display for UnaryOperator {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
UnaryOperator::Plus => {
write!(f, "+")
}
UnaryOperator::Minus => {
write!(f, "-")
}
UnaryOperator::Not => {
write!(f, "NOT")
}
UnaryOperator::SquareRoot => {
write!(f, "|/")
}
UnaryOperator::CubeRoot => {
write!(f, "||/")
}
UnaryOperator::Factorial => {
write!(f, "!")
}
UnaryOperator::Abs => {
write!(f, "@")
}
UnaryOperator::BitwiseNot => {
write!(f, "~")
}
}
}
}
impl UnaryOperator {
pub fn to_func_name(&self) -> String {
match self {
UnaryOperator::SquareRoot => "sqrt".to_string(),
UnaryOperator::CubeRoot => "cbrt".to_string(),
UnaryOperator::BitwiseNot => "bit_not".to_string(),
_ => {
let name = format!("{:?}", self);
name.to_lowercase()
}
}
}
}
pub fn split_conjunctions_expr(expr: &Expr) -> Vec<Expr> {
match expr {
Expr::BinaryOp {
op, left, right, ..
} if op == &BinaryOperator::And => {
let mut result = split_conjunctions_expr(left);
result.extend(split_conjunctions_expr(right));
result
}
_ => vec![expr.clone()],
}
}
pub fn split_equivalent_predicate_expr(expr: &Expr) -> Option<(Expr, Expr)> {
match expr {
Expr::BinaryOp {
op, left, right, ..
} if op == &BinaryOperator::Eq => Some((*left.clone(), *right.clone())),
_ => None,
}
}