use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CommentType {
Line,
Block,
Hash,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub enum QuoteStyle {
#[default]
None,
DoubleQuote,
Backtick,
Bracket,
}
impl QuoteStyle {
#[must_use]
pub fn for_dialect(dialect: crate::dialects::Dialect) -> Self {
use crate::dialects::Dialect;
match dialect {
Dialect::Tsql | Dialect::Fabric => QuoteStyle::Bracket,
Dialect::Mysql
| Dialect::BigQuery
| Dialect::Hive
| Dialect::Spark
| Dialect::Databricks
| Dialect::Doris
| Dialect::SingleStore
| Dialect::StarRocks => QuoteStyle::Backtick,
_ => QuoteStyle::DoubleQuote,
}
}
#[must_use]
pub fn is_quoted(self) -> bool {
!matches!(self, QuoteStyle::None)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Statement {
Select(SelectStatement),
Insert(InsertStatement),
Update(UpdateStatement),
Delete(DeleteStatement),
CreateTable(CreateTableStatement),
DropTable(DropTableStatement),
SetOperation(SetOperationStatement),
AlterTable(AlterTableStatement),
CreateView(CreateViewStatement),
DropView(DropViewStatement),
Truncate(TruncateStatement),
Transaction(TransactionStatement),
Explain(ExplainStatement),
Use(UseStatement),
Merge(MergeStatement),
Expression(Expr),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SelectStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub ctes: Vec<Cte>,
pub distinct: bool,
pub top: Option<Box<Expr>>,
pub columns: Vec<SelectItem>,
pub from: Option<FromClause>,
pub joins: Vec<JoinClause>,
pub where_clause: Option<Expr>,
pub group_by: Vec<Expr>,
pub having: Option<Expr>,
pub order_by: Vec<OrderByItem>,
pub limit: Option<Expr>,
pub offset: Option<Expr>,
pub fetch_first: Option<Expr>,
pub qualify: Option<Expr>,
pub window_definitions: Vec<WindowDefinition>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cte {
pub name: String,
#[serde(default)]
pub name_quote_style: QuoteStyle,
pub columns: Vec<String>,
pub query: Box<Statement>,
pub materialized: Option<bool>,
pub recursive: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WindowDefinition {
pub name: String,
pub spec: WindowSpec,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SetOperationStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub op: SetOperationType,
pub all: bool,
pub left: Box<Statement>,
pub right: Box<Statement>,
pub order_by: Vec<OrderByItem>,
pub limit: Option<Expr>,
pub offset: Option<Expr>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SetOperationType {
Union,
Intersect,
Except,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SelectItem {
Wildcard,
QualifiedWildcard { table: String },
Expr {
expr: Expr,
alias: Option<String>,
#[serde(default)]
alias_quote_style: QuoteStyle,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FromClause {
pub source: TableSource,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TableSource {
Table(TableRef),
Subquery {
query: Box<Statement>,
alias: Option<String>,
#[serde(default)]
alias_quote_style: QuoteStyle,
},
TableFunction {
name: String,
args: Vec<Expr>,
alias: Option<String>,
#[serde(default)]
alias_quote_style: QuoteStyle,
},
Lateral {
source: Box<TableSource>,
},
Unnest {
expr: Box<Expr>,
alias: Option<String>,
#[serde(default)]
alias_quote_style: QuoteStyle,
with_offset: bool,
},
Pivot {
source: Box<TableSource>,
aggregate: Box<Expr>,
for_column: String,
in_values: Vec<PivotValue>,
alias: Option<String>,
#[serde(default)]
alias_quote_style: QuoteStyle,
},
Unpivot {
source: Box<TableSource>,
value_column: String,
for_column: String,
in_columns: Vec<PivotValue>,
alias: Option<String>,
#[serde(default)]
alias_quote_style: QuoteStyle,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PivotValue {
pub value: Expr,
pub alias: Option<String>,
#[serde(default)]
pub alias_quote_style: QuoteStyle,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TableRef {
pub catalog: Option<String>,
pub schema: Option<String>,
pub name: String,
pub alias: Option<String>,
#[serde(default)]
pub name_quote_style: QuoteStyle,
#[serde(default)]
pub alias_quote_style: QuoteStyle,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JoinClause {
pub join_type: JoinType,
pub table: TableSource,
pub on: Option<Expr>,
pub using: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum JoinType {
Inner,
Left,
Right,
Full,
Cross,
Natural,
Lateral,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OrderByItem {
pub expr: Expr,
pub ascending: bool,
pub nulls_first: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Expr {
Column {
table: Option<String>,
name: String,
#[serde(default)]
quote_style: QuoteStyle,
#[serde(default)]
table_quote_style: QuoteStyle,
},
Number(String),
StringLiteral(String),
Boolean(bool),
Null,
BinaryOp {
left: Box<Expr>,
op: BinaryOperator,
right: Box<Expr>,
},
UnaryOp { op: UnaryOperator, expr: Box<Expr> },
Function {
name: String,
args: Vec<Expr>,
distinct: bool,
filter: Option<Box<Expr>>,
over: Option<WindowSpec>,
},
Between {
expr: Box<Expr>,
low: Box<Expr>,
high: Box<Expr>,
negated: bool,
},
InList {
expr: Box<Expr>,
list: Vec<Expr>,
negated: bool,
},
InSubquery {
expr: Box<Expr>,
subquery: Box<Statement>,
negated: bool,
},
AnyOp {
expr: Box<Expr>,
op: BinaryOperator,
right: Box<Expr>,
},
AllOp {
expr: Box<Expr>,
op: BinaryOperator,
right: Box<Expr>,
},
IsNull { expr: Box<Expr>, negated: bool },
IsBool {
expr: Box<Expr>,
value: bool,
negated: bool,
},
Like {
expr: Box<Expr>,
pattern: Box<Expr>,
negated: bool,
escape: Option<Box<Expr>>,
},
ILike {
expr: Box<Expr>,
pattern: Box<Expr>,
negated: bool,
escape: Option<Box<Expr>>,
},
Case {
operand: Option<Box<Expr>>,
when_clauses: Vec<(Expr, Expr)>,
else_clause: Option<Box<Expr>>,
},
Nested(Box<Expr>),
Wildcard,
Subquery(Box<Statement>),
Exists {
subquery: Box<Statement>,
negated: bool,
},
Cast {
expr: Box<Expr>,
data_type: DataType,
},
TryCast {
expr: Box<Expr>,
data_type: DataType,
},
Extract {
field: DateTimeField,
expr: Box<Expr>,
},
Interval {
value: Box<Expr>,
unit: Option<DateTimeField>,
},
ArrayLiteral(Vec<Expr>),
Tuple(Vec<Expr>),
Coalesce(Vec<Expr>),
If {
condition: Box<Expr>,
true_val: Box<Expr>,
false_val: Option<Box<Expr>>,
},
NullIf { expr: Box<Expr>, r#else: Box<Expr> },
Collate { expr: Box<Expr>, collation: String },
Parameter(String),
TypeExpr(DataType),
QualifiedWildcard { table: String },
Star,
Alias { expr: Box<Expr>, name: String },
ArrayIndex { expr: Box<Expr>, index: Box<Expr> },
JsonAccess {
expr: Box<Expr>,
path: Box<Expr>,
as_text: bool,
},
Lambda {
params: Vec<String>,
body: Box<Expr>,
},
Default,
Cube { exprs: Vec<Expr> },
Rollup { exprs: Vec<Expr> },
GroupingSets { sets: Vec<Expr> },
TypedFunction {
func: TypedFunction,
filter: Option<Box<Expr>>,
over: Option<WindowSpec>,
},
Commented {
expr: Box<Expr>,
comments: Vec<String>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WindowSpec {
pub window_ref: Option<String>,
pub partition_by: Vec<Expr>,
pub order_by: Vec<OrderByItem>,
pub frame: Option<WindowFrame>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WindowFrame {
pub kind: WindowFrameKind,
pub start: WindowFrameBound,
pub end: Option<WindowFrameBound>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum WindowFrameKind {
Rows,
Range,
Groups,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum WindowFrameBound {
CurrentRow,
Preceding(Option<Box<Expr>>), Following(Option<Box<Expr>>), }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DateTimeField {
Year,
Quarter,
Month,
Week,
Day,
DayOfWeek,
DayOfYear,
Hour,
Minute,
Second,
Millisecond,
Microsecond,
Nanosecond,
Epoch,
Timezone,
TimezoneHour,
TimezoneMinute,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TrimType {
Leading,
Trailing,
Both,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TypedFunction {
DateAdd {
expr: Box<Expr>,
interval: Box<Expr>,
unit: Option<DateTimeField>,
},
DateDiff {
start: Box<Expr>,
end: Box<Expr>,
unit: Option<DateTimeField>,
},
DateTrunc {
unit: DateTimeField,
expr: Box<Expr>,
},
DateSub {
expr: Box<Expr>,
interval: Box<Expr>,
unit: Option<DateTimeField>,
},
CurrentDate,
CurrentTimestamp,
StrToTime { expr: Box<Expr>, format: Box<Expr> },
TimeToStr { expr: Box<Expr>, format: Box<Expr> },
TsOrDsToDate { expr: Box<Expr> },
Year { expr: Box<Expr> },
Month { expr: Box<Expr> },
Day { expr: Box<Expr> },
Trim {
expr: Box<Expr>,
trim_type: TrimType,
trim_chars: Option<Box<Expr>>,
},
Substring {
expr: Box<Expr>,
start: Box<Expr>,
length: Option<Box<Expr>>,
},
Upper { expr: Box<Expr> },
Lower { expr: Box<Expr> },
RegexpLike {
expr: Box<Expr>,
pattern: Box<Expr>,
flags: Option<Box<Expr>>,
},
RegexpExtract {
expr: Box<Expr>,
pattern: Box<Expr>,
group_index: Option<Box<Expr>>,
},
RegexpReplace {
expr: Box<Expr>,
pattern: Box<Expr>,
replacement: Box<Expr>,
flags: Option<Box<Expr>>,
},
ConcatWs {
separator: Box<Expr>,
exprs: Vec<Expr>,
},
Split {
expr: Box<Expr>,
delimiter: Box<Expr>,
},
Initcap { expr: Box<Expr> },
Length { expr: Box<Expr> },
Replace {
expr: Box<Expr>,
from: Box<Expr>,
to: Box<Expr>,
},
Reverse { expr: Box<Expr> },
Left { expr: Box<Expr>, n: Box<Expr> },
Right { expr: Box<Expr>, n: Box<Expr> },
Lpad {
expr: Box<Expr>,
length: Box<Expr>,
pad: Option<Box<Expr>>,
},
Rpad {
expr: Box<Expr>,
length: Box<Expr>,
pad: Option<Box<Expr>>,
},
Count { expr: Box<Expr>, distinct: bool },
Sum { expr: Box<Expr>, distinct: bool },
Avg { expr: Box<Expr>, distinct: bool },
Min { expr: Box<Expr> },
Max { expr: Box<Expr> },
ArrayAgg { expr: Box<Expr>, distinct: bool },
ApproxDistinct { expr: Box<Expr> },
Variance { expr: Box<Expr> },
Stddev { expr: Box<Expr> },
ArrayConcat { arrays: Vec<Expr> },
ArrayContains {
array: Box<Expr>,
element: Box<Expr>,
},
ArraySize { expr: Box<Expr> },
Explode { expr: Box<Expr> },
GenerateSeries {
start: Box<Expr>,
stop: Box<Expr>,
step: Option<Box<Expr>>,
},
Flatten { expr: Box<Expr> },
JSONExtract { expr: Box<Expr>, path: Box<Expr> },
JSONExtractScalar { expr: Box<Expr>, path: Box<Expr> },
ParseJSON { expr: Box<Expr> },
JSONFormat { expr: Box<Expr> },
RowNumber,
Rank,
DenseRank,
NTile { n: Box<Expr> },
Lead {
expr: Box<Expr>,
offset: Option<Box<Expr>>,
default: Option<Box<Expr>>,
},
Lag {
expr: Box<Expr>,
offset: Option<Box<Expr>>,
default: Option<Box<Expr>>,
},
FirstValue { expr: Box<Expr> },
LastValue { expr: Box<Expr> },
Abs { expr: Box<Expr> },
Ceil { expr: Box<Expr> },
Floor { expr: Box<Expr> },
Round {
expr: Box<Expr>,
decimals: Option<Box<Expr>>,
},
Log {
expr: Box<Expr>,
base: Option<Box<Expr>>,
},
Ln { expr: Box<Expr> },
Pow {
base: Box<Expr>,
exponent: Box<Expr>,
},
Sqrt { expr: Box<Expr> },
Greatest { exprs: Vec<Expr> },
Least { exprs: Vec<Expr> },
Mod { left: Box<Expr>, right: Box<Expr> },
Hex { expr: Box<Expr> },
Unhex { expr: Box<Expr> },
Md5 { expr: Box<Expr> },
Sha { expr: Box<Expr> },
Sha2 {
expr: Box<Expr>,
bit_length: Box<Expr>,
},
}
impl TypedFunction {
pub fn walk_children<F>(&self, visitor: &mut F)
where
F: FnMut(&Expr) -> bool,
{
match self {
TypedFunction::DateAdd { expr, interval, .. }
| TypedFunction::DateSub { expr, interval, .. } => {
expr.walk(visitor);
interval.walk(visitor);
}
TypedFunction::DateDiff { start, end, .. } => {
start.walk(visitor);
end.walk(visitor);
}
TypedFunction::DateTrunc { expr, .. } => expr.walk(visitor),
TypedFunction::CurrentDate | TypedFunction::CurrentTimestamp => {}
TypedFunction::StrToTime { expr, format }
| TypedFunction::TimeToStr { expr, format } => {
expr.walk(visitor);
format.walk(visitor);
}
TypedFunction::TsOrDsToDate { expr }
| TypedFunction::Year { expr }
| TypedFunction::Month { expr }
| TypedFunction::Day { expr } => expr.walk(visitor),
TypedFunction::Trim {
expr, trim_chars, ..
} => {
expr.walk(visitor);
if let Some(c) = trim_chars {
c.walk(visitor);
}
}
TypedFunction::Substring {
expr,
start,
length,
} => {
expr.walk(visitor);
start.walk(visitor);
if let Some(l) = length {
l.walk(visitor);
}
}
TypedFunction::Upper { expr }
| TypedFunction::Lower { expr }
| TypedFunction::Initcap { expr }
| TypedFunction::Length { expr }
| TypedFunction::Reverse { expr } => expr.walk(visitor),
TypedFunction::RegexpLike {
expr,
pattern,
flags,
} => {
expr.walk(visitor);
pattern.walk(visitor);
if let Some(f) = flags {
f.walk(visitor);
}
}
TypedFunction::RegexpExtract {
expr,
pattern,
group_index,
} => {
expr.walk(visitor);
pattern.walk(visitor);
if let Some(g) = group_index {
g.walk(visitor);
}
}
TypedFunction::RegexpReplace {
expr,
pattern,
replacement,
flags,
} => {
expr.walk(visitor);
pattern.walk(visitor);
replacement.walk(visitor);
if let Some(f) = flags {
f.walk(visitor);
}
}
TypedFunction::ConcatWs { separator, exprs } => {
separator.walk(visitor);
for e in exprs {
e.walk(visitor);
}
}
TypedFunction::Split { expr, delimiter } => {
expr.walk(visitor);
delimiter.walk(visitor);
}
TypedFunction::Replace { expr, from, to } => {
expr.walk(visitor);
from.walk(visitor);
to.walk(visitor);
}
TypedFunction::Left { expr, n } | TypedFunction::Right { expr, n } => {
expr.walk(visitor);
n.walk(visitor);
}
TypedFunction::Lpad { expr, length, pad }
| TypedFunction::Rpad { expr, length, pad } => {
expr.walk(visitor);
length.walk(visitor);
if let Some(p) = pad {
p.walk(visitor);
}
}
TypedFunction::Count { expr, .. }
| TypedFunction::Sum { expr, .. }
| TypedFunction::Avg { expr, .. }
| TypedFunction::Min { expr }
| TypedFunction::Max { expr }
| TypedFunction::ArrayAgg { expr, .. }
| TypedFunction::ApproxDistinct { expr }
| TypedFunction::Variance { expr }
| TypedFunction::Stddev { expr } => expr.walk(visitor),
TypedFunction::ArrayConcat { arrays } => {
for a in arrays {
a.walk(visitor);
}
}
TypedFunction::ArrayContains { array, element } => {
array.walk(visitor);
element.walk(visitor);
}
TypedFunction::ArraySize { expr }
| TypedFunction::Explode { expr }
| TypedFunction::Flatten { expr } => expr.walk(visitor),
TypedFunction::GenerateSeries { start, stop, step } => {
start.walk(visitor);
stop.walk(visitor);
if let Some(s) = step {
s.walk(visitor);
}
}
TypedFunction::JSONExtract { expr, path }
| TypedFunction::JSONExtractScalar { expr, path } => {
expr.walk(visitor);
path.walk(visitor);
}
TypedFunction::ParseJSON { expr } | TypedFunction::JSONFormat { expr } => {
expr.walk(visitor)
}
TypedFunction::RowNumber | TypedFunction::Rank | TypedFunction::DenseRank => {}
TypedFunction::NTile { n } => n.walk(visitor),
TypedFunction::Lead {
expr,
offset,
default,
}
| TypedFunction::Lag {
expr,
offset,
default,
} => {
expr.walk(visitor);
if let Some(o) = offset {
o.walk(visitor);
}
if let Some(d) = default {
d.walk(visitor);
}
}
TypedFunction::FirstValue { expr } | TypedFunction::LastValue { expr } => {
expr.walk(visitor)
}
TypedFunction::Abs { expr }
| TypedFunction::Ceil { expr }
| TypedFunction::Floor { expr }
| TypedFunction::Ln { expr }
| TypedFunction::Sqrt { expr } => expr.walk(visitor),
TypedFunction::Round { expr, decimals } => {
expr.walk(visitor);
if let Some(d) = decimals {
d.walk(visitor);
}
}
TypedFunction::Log { expr, base } => {
expr.walk(visitor);
if let Some(b) = base {
b.walk(visitor);
}
}
TypedFunction::Pow { base, exponent } => {
base.walk(visitor);
exponent.walk(visitor);
}
TypedFunction::Greatest { exprs } | TypedFunction::Least { exprs } => {
for e in exprs {
e.walk(visitor);
}
}
TypedFunction::Mod { left, right } => {
left.walk(visitor);
right.walk(visitor);
}
TypedFunction::Hex { expr }
| TypedFunction::Unhex { expr }
| TypedFunction::Md5 { expr }
| TypedFunction::Sha { expr } => expr.walk(visitor),
TypedFunction::Sha2 { expr, bit_length } => {
expr.walk(visitor);
bit_length.walk(visitor);
}
}
}
#[must_use]
pub fn transform_children<F>(self, func: &F) -> TypedFunction
where
F: Fn(Expr) -> Expr,
{
match self {
TypedFunction::DateAdd {
expr,
interval,
unit,
} => TypedFunction::DateAdd {
expr: Box::new(expr.transform(func)),
interval: Box::new(interval.transform(func)),
unit,
},
TypedFunction::DateDiff { start, end, unit } => TypedFunction::DateDiff {
start: Box::new(start.transform(func)),
end: Box::new(end.transform(func)),
unit,
},
TypedFunction::DateTrunc { unit, expr } => TypedFunction::DateTrunc {
unit,
expr: Box::new(expr.transform(func)),
},
TypedFunction::DateSub {
expr,
interval,
unit,
} => TypedFunction::DateSub {
expr: Box::new(expr.transform(func)),
interval: Box::new(interval.transform(func)),
unit,
},
TypedFunction::CurrentDate => TypedFunction::CurrentDate,
TypedFunction::CurrentTimestamp => TypedFunction::CurrentTimestamp,
TypedFunction::StrToTime { expr, format } => TypedFunction::StrToTime {
expr: Box::new(expr.transform(func)),
format: Box::new(format.transform(func)),
},
TypedFunction::TimeToStr { expr, format } => TypedFunction::TimeToStr {
expr: Box::new(expr.transform(func)),
format: Box::new(format.transform(func)),
},
TypedFunction::TsOrDsToDate { expr } => TypedFunction::TsOrDsToDate {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Year { expr } => TypedFunction::Year {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Month { expr } => TypedFunction::Month {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Day { expr } => TypedFunction::Day {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Trim {
expr,
trim_type,
trim_chars,
} => TypedFunction::Trim {
expr: Box::new(expr.transform(func)),
trim_type,
trim_chars: trim_chars.map(|c| Box::new(c.transform(func))),
},
TypedFunction::Substring {
expr,
start,
length,
} => TypedFunction::Substring {
expr: Box::new(expr.transform(func)),
start: Box::new(start.transform(func)),
length: length.map(|l| Box::new(l.transform(func))),
},
TypedFunction::Upper { expr } => TypedFunction::Upper {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Lower { expr } => TypedFunction::Lower {
expr: Box::new(expr.transform(func)),
},
TypedFunction::RegexpLike {
expr,
pattern,
flags,
} => TypedFunction::RegexpLike {
expr: Box::new(expr.transform(func)),
pattern: Box::new(pattern.transform(func)),
flags: flags.map(|f| Box::new(f.transform(func))),
},
TypedFunction::RegexpExtract {
expr,
pattern,
group_index,
} => TypedFunction::RegexpExtract {
expr: Box::new(expr.transform(func)),
pattern: Box::new(pattern.transform(func)),
group_index: group_index.map(|g| Box::new(g.transform(func))),
},
TypedFunction::RegexpReplace {
expr,
pattern,
replacement,
flags,
} => TypedFunction::RegexpReplace {
expr: Box::new(expr.transform(func)),
pattern: Box::new(pattern.transform(func)),
replacement: Box::new(replacement.transform(func)),
flags: flags.map(|f| Box::new(f.transform(func))),
},
TypedFunction::ConcatWs { separator, exprs } => TypedFunction::ConcatWs {
separator: Box::new(separator.transform(func)),
exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
},
TypedFunction::Split { expr, delimiter } => TypedFunction::Split {
expr: Box::new(expr.transform(func)),
delimiter: Box::new(delimiter.transform(func)),
},
TypedFunction::Initcap { expr } => TypedFunction::Initcap {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Length { expr } => TypedFunction::Length {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Replace { expr, from, to } => TypedFunction::Replace {
expr: Box::new(expr.transform(func)),
from: Box::new(from.transform(func)),
to: Box::new(to.transform(func)),
},
TypedFunction::Reverse { expr } => TypedFunction::Reverse {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Left { expr, n } => TypedFunction::Left {
expr: Box::new(expr.transform(func)),
n: Box::new(n.transform(func)),
},
TypedFunction::Right { expr, n } => TypedFunction::Right {
expr: Box::new(expr.transform(func)),
n: Box::new(n.transform(func)),
},
TypedFunction::Lpad { expr, length, pad } => TypedFunction::Lpad {
expr: Box::new(expr.transform(func)),
length: Box::new(length.transform(func)),
pad: pad.map(|p| Box::new(p.transform(func))),
},
TypedFunction::Rpad { expr, length, pad } => TypedFunction::Rpad {
expr: Box::new(expr.transform(func)),
length: Box::new(length.transform(func)),
pad: pad.map(|p| Box::new(p.transform(func))),
},
TypedFunction::Count { expr, distinct } => TypedFunction::Count {
expr: Box::new(expr.transform(func)),
distinct,
},
TypedFunction::Sum { expr, distinct } => TypedFunction::Sum {
expr: Box::new(expr.transform(func)),
distinct,
},
TypedFunction::Avg { expr, distinct } => TypedFunction::Avg {
expr: Box::new(expr.transform(func)),
distinct,
},
TypedFunction::Min { expr } => TypedFunction::Min {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Max { expr } => TypedFunction::Max {
expr: Box::new(expr.transform(func)),
},
TypedFunction::ArrayAgg { expr, distinct } => TypedFunction::ArrayAgg {
expr: Box::new(expr.transform(func)),
distinct,
},
TypedFunction::ApproxDistinct { expr } => TypedFunction::ApproxDistinct {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Variance { expr } => TypedFunction::Variance {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Stddev { expr } => TypedFunction::Stddev {
expr: Box::new(expr.transform(func)),
},
TypedFunction::ArrayConcat { arrays } => TypedFunction::ArrayConcat {
arrays: arrays.into_iter().map(|a| a.transform(func)).collect(),
},
TypedFunction::ArrayContains { array, element } => TypedFunction::ArrayContains {
array: Box::new(array.transform(func)),
element: Box::new(element.transform(func)),
},
TypedFunction::ArraySize { expr } => TypedFunction::ArraySize {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Explode { expr } => TypedFunction::Explode {
expr: Box::new(expr.transform(func)),
},
TypedFunction::GenerateSeries { start, stop, step } => TypedFunction::GenerateSeries {
start: Box::new(start.transform(func)),
stop: Box::new(stop.transform(func)),
step: step.map(|s| Box::new(s.transform(func))),
},
TypedFunction::Flatten { expr } => TypedFunction::Flatten {
expr: Box::new(expr.transform(func)),
},
TypedFunction::JSONExtract { expr, path } => TypedFunction::JSONExtract {
expr: Box::new(expr.transform(func)),
path: Box::new(path.transform(func)),
},
TypedFunction::JSONExtractScalar { expr, path } => TypedFunction::JSONExtractScalar {
expr: Box::new(expr.transform(func)),
path: Box::new(path.transform(func)),
},
TypedFunction::ParseJSON { expr } => TypedFunction::ParseJSON {
expr: Box::new(expr.transform(func)),
},
TypedFunction::JSONFormat { expr } => TypedFunction::JSONFormat {
expr: Box::new(expr.transform(func)),
},
TypedFunction::RowNumber => TypedFunction::RowNumber,
TypedFunction::Rank => TypedFunction::Rank,
TypedFunction::DenseRank => TypedFunction::DenseRank,
TypedFunction::NTile { n } => TypedFunction::NTile {
n: Box::new(n.transform(func)),
},
TypedFunction::Lead {
expr,
offset,
default,
} => TypedFunction::Lead {
expr: Box::new(expr.transform(func)),
offset: offset.map(|o| Box::new(o.transform(func))),
default: default.map(|d| Box::new(d.transform(func))),
},
TypedFunction::Lag {
expr,
offset,
default,
} => TypedFunction::Lag {
expr: Box::new(expr.transform(func)),
offset: offset.map(|o| Box::new(o.transform(func))),
default: default.map(|d| Box::new(d.transform(func))),
},
TypedFunction::FirstValue { expr } => TypedFunction::FirstValue {
expr: Box::new(expr.transform(func)),
},
TypedFunction::LastValue { expr } => TypedFunction::LastValue {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Abs { expr } => TypedFunction::Abs {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Ceil { expr } => TypedFunction::Ceil {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Floor { expr } => TypedFunction::Floor {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Round { expr, decimals } => TypedFunction::Round {
expr: Box::new(expr.transform(func)),
decimals: decimals.map(|d| Box::new(d.transform(func))),
},
TypedFunction::Log { expr, base } => TypedFunction::Log {
expr: Box::new(expr.transform(func)),
base: base.map(|b| Box::new(b.transform(func))),
},
TypedFunction::Ln { expr } => TypedFunction::Ln {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Pow { base, exponent } => TypedFunction::Pow {
base: Box::new(base.transform(func)),
exponent: Box::new(exponent.transform(func)),
},
TypedFunction::Sqrt { expr } => TypedFunction::Sqrt {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Greatest { exprs } => TypedFunction::Greatest {
exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
},
TypedFunction::Least { exprs } => TypedFunction::Least {
exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
},
TypedFunction::Mod { left, right } => TypedFunction::Mod {
left: Box::new(left.transform(func)),
right: Box::new(right.transform(func)),
},
TypedFunction::Hex { expr } => TypedFunction::Hex {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Unhex { expr } => TypedFunction::Unhex {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Md5 { expr } => TypedFunction::Md5 {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Sha { expr } => TypedFunction::Sha {
expr: Box::new(expr.transform(func)),
},
TypedFunction::Sha2 { expr, bit_length } => TypedFunction::Sha2 {
expr: Box::new(expr.transform(func)),
bit_length: Box::new(bit_length.transform(func)),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum BinaryOperator {
Plus,
Minus,
Multiply,
Divide,
Modulo,
Eq,
Neq,
Lt,
Gt,
LtEq,
GtEq,
And,
Or,
Xor,
Concat,
BitwiseAnd,
BitwiseOr,
BitwiseXor,
ShiftLeft,
ShiftRight,
Arrow,
DoubleArrow,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum UnaryOperator {
Not,
Minus,
Plus,
BitwiseNot,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InsertStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub table: TableRef,
pub columns: Vec<String>,
pub source: InsertSource,
pub on_conflict: Option<OnConflict>,
pub returning: Vec<SelectItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum InsertSource {
Values(Vec<Vec<Expr>>),
Query(Box<Statement>),
Default,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OnConflict {
pub columns: Vec<String>,
pub action: ConflictAction,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ConflictAction {
DoNothing,
DoUpdate(Vec<(String, Expr)>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UpdateStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub table: TableRef,
pub assignments: Vec<(String, Expr)>,
pub from: Option<FromClause>,
pub where_clause: Option<Expr>,
pub returning: Vec<SelectItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DeleteStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub table: TableRef,
pub using: Option<FromClause>,
pub where_clause: Option<Expr>,
pub returning: Vec<SelectItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MergeStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub target: TableRef,
pub source: TableSource,
pub on: Expr,
pub clauses: Vec<MergeClause>,
pub output: Vec<SelectItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MergeClause {
pub kind: MergeClauseKind,
pub condition: Option<Expr>,
pub action: MergeAction,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MergeClauseKind {
Matched,
NotMatched,
NotMatchedBySource,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum MergeAction {
Update(Vec<(String, Expr)>),
Insert {
columns: Vec<String>,
values: Vec<Expr>,
},
InsertRow,
Delete,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateTableStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub if_not_exists: bool,
pub temporary: bool,
pub table: TableRef,
pub columns: Vec<ColumnDef>,
pub constraints: Vec<TableConstraint>,
pub as_select: Option<Box<Statement>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TableConstraint {
PrimaryKey {
name: Option<String>,
columns: Vec<String>,
},
Unique {
name: Option<String>,
columns: Vec<String>,
},
ForeignKey {
name: Option<String>,
columns: Vec<String>,
ref_table: TableRef,
ref_columns: Vec<String>,
on_delete: Option<ReferentialAction>,
on_update: Option<ReferentialAction>,
},
Check {
name: Option<String>,
expr: Expr,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ReferentialAction {
Cascade,
Restrict,
NoAction,
SetNull,
SetDefault,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ColumnDef {
pub name: String,
pub data_type: DataType,
pub nullable: Option<bool>,
pub default: Option<Expr>,
pub primary_key: bool,
pub unique: bool,
pub auto_increment: bool,
pub collation: Option<String>,
pub comment: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AlterTableStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub table: TableRef,
pub actions: Vec<AlterTableAction>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AlterTableAction {
AddColumn(ColumnDef),
DropColumn { name: String, if_exists: bool },
RenameColumn { old_name: String, new_name: String },
AlterColumnType { name: String, data_type: DataType },
AddConstraint(TableConstraint),
DropConstraint { name: String },
RenameTable { new_name: String },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateViewStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub name: TableRef,
pub columns: Vec<String>,
pub query: Box<Statement>,
pub or_replace: bool,
pub materialized: bool,
pub if_not_exists: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DropViewStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub name: TableRef,
pub if_exists: bool,
pub materialized: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TruncateStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub table: TableRef,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TransactionStatement {
Begin,
Commit,
Rollback,
Savepoint(String),
ReleaseSavepoint(String),
RollbackTo(String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ExplainStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub analyze: bool,
pub statement: Box<Statement>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UseStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DropTableStatement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub comments: Vec<String>,
pub if_exists: bool,
pub table: TableRef,
pub cascade: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DataType {
TinyInt,
SmallInt,
Int,
BigInt,
Float,
Double,
Decimal {
precision: Option<u32>,
scale: Option<u32>,
},
Numeric {
precision: Option<u32>,
scale: Option<u32>,
},
Real,
Varchar(Option<u32>),
Char(Option<u32>),
Text,
String,
Binary(Option<u32>),
Varbinary(Option<u32>),
Boolean,
Date,
Time {
precision: Option<u32>,
},
Timestamp {
precision: Option<u32>,
with_tz: bool,
},
Interval,
DateTime,
Blob,
Bytea,
Bytes,
Json,
Jsonb,
Uuid,
Array(Option<Box<DataType>>),
Map {
key: Box<DataType>,
value: Box<DataType>,
},
Struct(Vec<(String, DataType)>),
Tuple(Vec<DataType>),
Null,
Unknown(String),
Variant,
Object,
Xml,
Inet,
Cidr,
Macaddr,
Bit(Option<u32>),
Money,
Serial,
BigSerial,
SmallSerial,
Regclass,
Regtype,
Hstore,
Geography,
Geometry,
Super,
}
impl Expr {
pub fn walk<F>(&self, visitor: &mut F)
where
F: FnMut(&Expr) -> bool,
{
if !visitor(self) {
return;
}
match self {
Expr::BinaryOp { left, right, .. } => {
left.walk(visitor);
right.walk(visitor);
}
Expr::UnaryOp { expr, .. } => expr.walk(visitor),
Expr::Function { args, filter, .. } => {
for arg in args {
arg.walk(visitor);
}
if let Some(f) = filter {
f.walk(visitor);
}
}
Expr::Between {
expr, low, high, ..
} => {
expr.walk(visitor);
low.walk(visitor);
high.walk(visitor);
}
Expr::InList { expr, list, .. } => {
expr.walk(visitor);
for item in list {
item.walk(visitor);
}
}
Expr::InSubquery { expr, .. } => {
expr.walk(visitor);
}
Expr::IsNull { expr, .. } => expr.walk(visitor),
Expr::IsBool { expr, .. } => expr.walk(visitor),
Expr::AnyOp { expr, right, .. } | Expr::AllOp { expr, right, .. } => {
expr.walk(visitor);
right.walk(visitor);
}
Expr::Like { expr, pattern, .. } | Expr::ILike { expr, pattern, .. } => {
expr.walk(visitor);
pattern.walk(visitor);
}
Expr::Case {
operand,
when_clauses,
else_clause,
} => {
if let Some(op) = operand {
op.walk(visitor);
}
for (cond, result) in when_clauses {
cond.walk(visitor);
result.walk(visitor);
}
if let Some(el) = else_clause {
el.walk(visitor);
}
}
Expr::Nested(inner) => inner.walk(visitor),
Expr::Cast { expr, .. } | Expr::TryCast { expr, .. } => expr.walk(visitor),
Expr::Extract { expr, .. } => expr.walk(visitor),
Expr::Interval { value, .. } => value.walk(visitor),
Expr::ArrayLiteral(items) | Expr::Tuple(items) | Expr::Coalesce(items) => {
for item in items {
item.walk(visitor);
}
}
Expr::If {
condition,
true_val,
false_val,
} => {
condition.walk(visitor);
true_val.walk(visitor);
if let Some(fv) = false_val {
fv.walk(visitor);
}
}
Expr::NullIf { expr, r#else } => {
expr.walk(visitor);
r#else.walk(visitor);
}
Expr::Collate { expr, .. } => expr.walk(visitor),
Expr::Alias { expr, .. } => expr.walk(visitor),
Expr::ArrayIndex { expr, index } => {
expr.walk(visitor);
index.walk(visitor);
}
Expr::JsonAccess { expr, path, .. } => {
expr.walk(visitor);
path.walk(visitor);
}
Expr::Lambda { body, .. } => body.walk(visitor),
Expr::TypedFunction { func, filter, .. } => {
func.walk_children(visitor);
if let Some(f) = filter {
f.walk(visitor);
}
}
Expr::Cube { exprs } | Expr::Rollup { exprs } => {
for item in exprs {
item.walk(visitor);
}
}
Expr::GroupingSets { sets } => {
for item in sets {
item.walk(visitor);
}
}
Expr::Commented { expr, .. } => expr.walk(visitor),
Expr::Column { .. }
| Expr::Number(_)
| Expr::StringLiteral(_)
| Expr::Boolean(_)
| Expr::Null
| Expr::Wildcard
| Expr::Star
| Expr::Parameter(_)
| Expr::TypeExpr(_)
| Expr::QualifiedWildcard { .. }
| Expr::Default
| Expr::Subquery(_)
| Expr::Exists { .. } => {}
}
}
#[must_use]
pub fn find<F>(&self, predicate: &F) -> Option<&Expr>
where
F: Fn(&Expr) -> bool,
{
let mut result = None;
self.walk(&mut |expr| {
if result.is_some() {
return false;
}
if predicate(expr) {
result = Some(expr as *const Expr);
false
} else {
true
}
});
result.map(|p| unsafe { &*p })
}
#[must_use]
pub fn find_all<F>(&self, predicate: &F) -> Vec<&Expr>
where
F: Fn(&Expr) -> bool,
{
let mut results: Vec<*const Expr> = Vec::new();
self.walk(&mut |expr| {
if predicate(expr) {
results.push(expr as *const Expr);
}
true
});
results.into_iter().map(|p| unsafe { &*p }).collect()
}
#[must_use]
pub fn transform<F>(self, func: &F) -> Expr
where
F: Fn(Expr) -> Expr,
{
let transformed = match self {
Expr::BinaryOp { left, op, right } => Expr::BinaryOp {
left: Box::new(left.transform(func)),
op,
right: Box::new(right.transform(func)),
},
Expr::UnaryOp { op, expr } => Expr::UnaryOp {
op,
expr: Box::new(expr.transform(func)),
},
Expr::Function {
name,
args,
distinct,
filter,
over,
} => Expr::Function {
name,
args: args.into_iter().map(|a| a.transform(func)).collect(),
distinct,
filter: filter.map(|f| Box::new(f.transform(func))),
over,
},
Expr::Nested(inner) => Expr::Nested(Box::new(inner.transform(func))),
Expr::Cast { expr, data_type } => Expr::Cast {
expr: Box::new(expr.transform(func)),
data_type,
},
Expr::Between {
expr,
low,
high,
negated,
} => Expr::Between {
expr: Box::new(expr.transform(func)),
low: Box::new(low.transform(func)),
high: Box::new(high.transform(func)),
negated,
},
Expr::Case {
operand,
when_clauses,
else_clause,
} => Expr::Case {
operand: operand.map(|o| Box::new(o.transform(func))),
when_clauses: when_clauses
.into_iter()
.map(|(c, r)| (c.transform(func), r.transform(func)))
.collect(),
else_clause: else_clause.map(|e| Box::new(e.transform(func))),
},
Expr::IsBool {
expr,
value,
negated,
} => Expr::IsBool {
expr: Box::new(expr.transform(func)),
value,
negated,
},
Expr::AnyOp { expr, op, right } => Expr::AnyOp {
expr: Box::new(expr.transform(func)),
op,
right: Box::new(right.transform(func)),
},
Expr::AllOp { expr, op, right } => Expr::AllOp {
expr: Box::new(expr.transform(func)),
op,
right: Box::new(right.transform(func)),
},
Expr::TypedFunction {
func: tf,
filter,
over,
} => Expr::TypedFunction {
func: tf.transform_children(func),
filter: filter.map(|f| Box::new(f.transform(func))),
over,
},
Expr::InList {
expr,
list,
negated,
} => Expr::InList {
expr: Box::new(expr.transform(func)),
list: list.into_iter().map(|e| e.transform(func)).collect(),
negated,
},
Expr::InSubquery {
expr,
subquery,
negated,
} => Expr::InSubquery {
expr: Box::new(expr.transform(func)),
subquery, negated,
},
Expr::IsNull { expr, negated } => Expr::IsNull {
expr: Box::new(expr.transform(func)),
negated,
},
Expr::Like {
expr,
pattern,
negated,
escape,
} => Expr::Like {
expr: Box::new(expr.transform(func)),
pattern: Box::new(pattern.transform(func)),
negated,
escape: escape.map(|e| Box::new(e.transform(func))),
},
Expr::ILike {
expr,
pattern,
negated,
escape,
} => Expr::ILike {
expr: Box::new(expr.transform(func)),
pattern: Box::new(pattern.transform(func)),
negated,
escape: escape.map(|e| Box::new(e.transform(func))),
},
Expr::TryCast { expr, data_type } => Expr::TryCast {
expr: Box::new(expr.transform(func)),
data_type,
},
Expr::Extract { field, expr } => Expr::Extract {
field,
expr: Box::new(expr.transform(func)),
},
Expr::Interval { value, unit } => Expr::Interval {
value: Box::new(value.transform(func)),
unit,
},
Expr::ArrayLiteral(elems) => {
Expr::ArrayLiteral(elems.into_iter().map(|e| e.transform(func)).collect())
}
Expr::Tuple(elems) => {
Expr::Tuple(elems.into_iter().map(|e| e.transform(func)).collect())
}
Expr::Coalesce(elems) => {
Expr::Coalesce(elems.into_iter().map(|e| e.transform(func)).collect())
}
Expr::If {
condition,
true_val,
false_val,
} => Expr::If {
condition: Box::new(condition.transform(func)),
true_val: Box::new(true_val.transform(func)),
false_val: false_val.map(|f| Box::new(f.transform(func))),
},
Expr::NullIf { expr, r#else } => Expr::NullIf {
expr: Box::new(expr.transform(func)),
r#else: Box::new(r#else.transform(func)),
},
Expr::Collate { expr, collation } => Expr::Collate {
expr: Box::new(expr.transform(func)),
collation,
},
Expr::Alias { expr, name } => Expr::Alias {
expr: Box::new(expr.transform(func)),
name,
},
Expr::ArrayIndex { expr, index } => Expr::ArrayIndex {
expr: Box::new(expr.transform(func)),
index: Box::new(index.transform(func)),
},
Expr::JsonAccess {
expr,
path,
as_text,
} => Expr::JsonAccess {
expr: Box::new(expr.transform(func)),
path: Box::new(path.transform(func)),
as_text,
},
Expr::Lambda { params, body } => Expr::Lambda {
params,
body: Box::new(body.transform(func)),
},
Expr::Cube { exprs } => Expr::Cube {
exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
},
Expr::Rollup { exprs } => Expr::Rollup {
exprs: exprs.into_iter().map(|e| e.transform(func)).collect(),
},
Expr::GroupingSets { sets } => Expr::GroupingSets {
sets: sets.into_iter().map(|e| e.transform(func)).collect(),
},
Expr::Commented { expr, comments } => Expr::Commented {
expr: Box::new(expr.transform(func)),
comments,
},
other => other,
};
func(transformed)
}
#[must_use]
pub fn is_column(&self) -> bool {
matches!(self, Expr::Column { .. })
}
#[must_use]
pub fn is_literal(&self) -> bool {
matches!(
self,
Expr::Number(_) | Expr::StringLiteral(_) | Expr::Boolean(_) | Expr::Null
)
}
#[must_use]
pub fn sql(&self) -> String {
use crate::generator::Generator;
Generator::expr_to_sql(self)
}
}
#[must_use]
pub fn find_columns(expr: &Expr) -> Vec<&Expr> {
expr.find_all(&|e| matches!(e, Expr::Column { .. }))
}
#[must_use]
pub fn find_tables(statement: &Statement) -> Vec<&TableRef> {
match statement {
Statement::Select(sel) => {
let mut tables = Vec::new();
if let Some(from) = &sel.from {
collect_table_refs_from_source(&from.source, &mut tables);
}
for join in &sel.joins {
collect_table_refs_from_source(&join.table, &mut tables);
}
tables
}
Statement::Insert(ins) => vec![&ins.table],
Statement::Update(upd) => vec![&upd.table],
Statement::Delete(del) => vec![&del.table],
Statement::CreateTable(ct) => vec![&ct.table],
Statement::DropTable(dt) => vec![&dt.table],
_ => vec![],
}
}
fn collect_table_refs_from_source<'a>(source: &'a TableSource, tables: &mut Vec<&'a TableRef>) {
match source {
TableSource::Table(table_ref) => tables.push(table_ref),
TableSource::Subquery { .. } => {}
TableSource::TableFunction { .. } => {}
TableSource::Lateral { source } => collect_table_refs_from_source(source, tables),
TableSource::Pivot { source, .. } | TableSource::Unpivot { source, .. } => {
collect_table_refs_from_source(source, tables);
}
TableSource::Unnest { .. } => {}
}
}