#[derive(Debug, Clone, PartialEq)]
pub struct Comment {
pub text: String,
pub is_line_comment: bool,
}
impl Comment {
pub fn line(text: String) -> Self {
Self {
text,
is_line_comment: true,
}
}
pub fn block(text: String) -> Self {
Self {
text,
is_line_comment: false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum QuoteStyle {
None,
DoubleQuotes,
Brackets,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ColumnRef {
pub name: String,
pub quote_style: QuoteStyle,
pub table_prefix: Option<String>,
}
impl ColumnRef {
pub fn unquoted(name: String) -> Self {
Self {
name,
quote_style: QuoteStyle::None,
table_prefix: None,
}
}
pub fn quoted(name: String) -> Self {
Self {
name,
quote_style: QuoteStyle::DoubleQuotes,
table_prefix: None,
}
}
pub fn qualified(table: String, name: String) -> Self {
Self {
name,
quote_style: QuoteStyle::None,
table_prefix: Some(table),
}
}
pub fn to_qualified_string(&self) -> String {
match &self.table_prefix {
Some(table) => format!("{}.{}", table, self.name),
None => self.name.clone(),
}
}
pub fn bracketed(name: String) -> Self {
Self {
name,
quote_style: QuoteStyle::Brackets,
table_prefix: None,
}
}
pub fn to_sql(&self) -> String {
let column_part = match self.quote_style {
QuoteStyle::None => self.name.clone(),
QuoteStyle::DoubleQuotes => format!("\"{}\"", self.name),
QuoteStyle::Brackets => format!("[{}]", self.name),
};
match &self.table_prefix {
Some(table) => format!("{}.{}", table, column_part),
None => column_part,
}
}
}
impl PartialEq<str> for ColumnRef {
fn eq(&self, other: &str) -> bool {
self.name == other
}
}
impl PartialEq<&str> for ColumnRef {
fn eq(&self, other: &&str) -> bool {
self.name == *other
}
}
impl std::fmt::Display for ColumnRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_sql())
}
}
#[derive(Debug, Clone)]
pub enum SqlExpression {
Column(ColumnRef),
StringLiteral(String),
NumberLiteral(String),
BooleanLiteral(bool),
Null, DateTimeConstructor {
year: i32,
month: u32,
day: u32,
hour: Option<u32>,
minute: Option<u32>,
second: Option<u32>,
},
DateTimeToday {
hour: Option<u32>,
minute: Option<u32>,
second: Option<u32>,
},
MethodCall {
object: String,
method: String,
args: Vec<SqlExpression>,
},
ChainedMethodCall {
base: Box<SqlExpression>,
method: String,
args: Vec<SqlExpression>,
},
FunctionCall {
name: String,
args: Vec<SqlExpression>,
distinct: bool, },
WindowFunction {
name: String,
args: Vec<SqlExpression>,
window_spec: WindowSpec,
},
BinaryOp {
left: Box<SqlExpression>,
op: String,
right: Box<SqlExpression>,
},
InList {
expr: Box<SqlExpression>,
values: Vec<SqlExpression>,
},
NotInList {
expr: Box<SqlExpression>,
values: Vec<SqlExpression>,
},
Between {
expr: Box<SqlExpression>,
lower: Box<SqlExpression>,
upper: Box<SqlExpression>,
},
Not {
expr: Box<SqlExpression>,
},
CaseExpression {
when_branches: Vec<WhenBranch>,
else_branch: Option<Box<SqlExpression>>,
},
SimpleCaseExpression {
expr: Box<SqlExpression>,
when_branches: Vec<SimpleWhenBranch>,
else_branch: Option<Box<SqlExpression>>,
},
ScalarSubquery {
query: Box<SelectStatement>,
},
InSubquery {
expr: Box<SqlExpression>,
subquery: Box<SelectStatement>,
},
Unnest {
column: Box<SqlExpression>,
delimiter: String,
},
NotInSubquery {
expr: Box<SqlExpression>,
subquery: Box<SelectStatement>,
},
InSubqueryTuple {
exprs: Vec<SqlExpression>,
subquery: Box<SelectStatement>,
},
NotInSubqueryTuple {
exprs: Vec<SqlExpression>,
subquery: Box<SelectStatement>,
},
}
#[derive(Debug, Clone)]
pub struct WhenBranch {
pub condition: Box<SqlExpression>,
pub result: Box<SqlExpression>,
}
#[derive(Debug, Clone)]
pub struct SimpleWhenBranch {
pub value: Box<SqlExpression>,
pub result: Box<SqlExpression>,
}
#[derive(Debug, Clone)]
pub struct WhereClause {
pub conditions: Vec<Condition>,
}
#[derive(Debug, Clone)]
pub struct Condition {
pub expr: SqlExpression,
pub connector: Option<LogicalOp>, }
#[derive(Debug, Clone)]
pub enum LogicalOp {
And,
Or,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SortDirection {
Asc,
Desc,
}
impl SortDirection {
pub fn as_u8(&self) -> u8 {
match self {
SortDirection::Asc => 0,
SortDirection::Desc => 1,
}
}
}
#[derive(Debug, Clone)]
pub struct OrderByColumn {
pub column: String,
pub direction: SortDirection,
}
#[derive(Debug, Clone)]
pub struct OrderByItem {
pub expr: SqlExpression,
pub direction: SortDirection,
}
impl OrderByItem {
pub fn from_column_name(name: String, direction: SortDirection) -> Self {
Self {
expr: SqlExpression::Column(ColumnRef {
name,
quote_style: QuoteStyle::None,
table_prefix: None,
}),
direction,
}
}
pub fn from_expression(expr: SqlExpression, direction: SortDirection) -> Self {
Self { expr, direction }
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FrameBound {
UnboundedPreceding,
CurrentRow,
Preceding(i64),
Following(i64),
UnboundedFollowing,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FrameUnit {
Rows,
Range,
}
impl FrameUnit {
pub fn as_u8(&self) -> u8 {
match self {
FrameUnit::Rows => 0,
FrameUnit::Range => 1,
}
}
}
#[derive(Debug, Clone)]
pub struct WindowFrame {
pub unit: FrameUnit,
pub start: FrameBound,
pub end: Option<FrameBound>, }
#[derive(Debug, Clone)]
pub struct WindowSpec {
pub partition_by: Vec<String>,
pub order_by: Vec<OrderByItem>,
pub frame: Option<WindowFrame>, }
impl WindowSpec {
pub fn compute_hash(&self) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
for col in &self.partition_by {
col.hash(&mut hasher);
}
for item in &self.order_by {
format!("{:?}", item.expr).hash(&mut hasher);
item.direction.as_u8().hash(&mut hasher);
}
if let Some(ref frame) = self.frame {
frame.unit.as_u8().hash(&mut hasher);
format!("{:?}", frame.start).hash(&mut hasher);
if let Some(ref end) = frame.end {
format!("{:?}", end).hash(&mut hasher);
}
}
hasher.finish()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SetOperation {
UnionAll,
Union,
Intersect,
Except,
}
#[derive(Debug, Clone)]
pub enum SelectItem {
Column {
column: ColumnRef,
leading_comments: Vec<Comment>,
trailing_comment: Option<Comment>,
},
Expression {
expr: SqlExpression,
alias: String,
leading_comments: Vec<Comment>,
trailing_comment: Option<Comment>,
},
Star {
table_prefix: Option<String>, leading_comments: Vec<Comment>,
trailing_comment: Option<Comment>,
},
StarExclude {
table_prefix: Option<String>,
excluded_columns: Vec<String>,
leading_comments: Vec<Comment>,
trailing_comment: Option<Comment>,
},
}
#[derive(Debug, Clone)]
pub struct SelectStatement {
pub distinct: bool, pub columns: Vec<String>, pub select_items: Vec<SelectItem>,
pub from_source: Option<TableSource>,
#[deprecated(note = "Use from_source instead")]
pub from_table: Option<String>,
#[deprecated(note = "Use from_source instead")]
pub from_subquery: Option<Box<SelectStatement>>, #[deprecated(note = "Use from_source instead")]
pub from_function: Option<TableFunction>, #[deprecated(note = "Use from_source instead")]
pub from_alias: Option<String>,
pub joins: Vec<JoinClause>, pub where_clause: Option<WhereClause>,
pub order_by: Option<Vec<OrderByItem>>, pub group_by: Option<Vec<SqlExpression>>, pub having: Option<SqlExpression>, pub qualify: Option<SqlExpression>, pub limit: Option<usize>,
pub offset: Option<usize>,
pub ctes: Vec<CTE>, pub into_table: Option<IntoTable>, pub set_operations: Vec<(SetOperation, Box<SelectStatement>)>,
pub leading_comments: Vec<Comment>, pub trailing_comment: Option<Comment>, }
impl Default for SelectStatement {
fn default() -> Self {
SelectStatement {
distinct: false,
columns: Vec::new(),
select_items: Vec::new(),
from_source: None,
#[allow(deprecated)]
from_table: None,
#[allow(deprecated)]
from_subquery: None,
#[allow(deprecated)]
from_function: None,
#[allow(deprecated)]
from_alias: None,
joins: Vec::new(),
where_clause: None,
order_by: None,
group_by: None,
having: None,
qualify: None,
limit: None,
offset: None,
ctes: Vec::new(),
into_table: None,
set_operations: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IntoTable {
pub name: String,
}
#[derive(Debug, Clone)]
pub enum TableFunction {
Generator {
name: String,
args: Vec<SqlExpression>,
},
}
#[derive(Debug, Clone)]
pub struct CTE {
pub name: String,
pub column_list: Option<Vec<String>>, pub cte_type: CTEType,
}
#[derive(Debug, Clone)]
pub enum CTEType {
Standard(SelectStatement),
Web(WebCTESpec),
File(FileCTESpec),
}
#[derive(Debug, Clone)]
pub struct WebCTESpec {
pub url: String,
pub format: Option<DataFormat>, pub headers: Vec<(String, String)>, pub cache_seconds: Option<u64>, pub method: Option<HttpMethod>, pub body: Option<String>, pub json_path: Option<String>, pub form_files: Vec<(String, String)>, pub form_fields: Vec<(String, String)>, pub template_vars: Vec<TemplateVar>, }
#[derive(Debug, Clone)]
pub struct TemplateVar {
pub placeholder: String, pub table_name: String, pub column: Option<String>, pub index: Option<usize>, }
#[derive(Debug, Clone)]
pub enum HttpMethod {
GET,
POST,
PUT,
DELETE,
PATCH,
}
#[derive(Debug, Clone)]
pub enum DataFormat {
CSV,
JSON,
Auto, }
#[derive(Debug, Clone)]
pub struct FileCTESpec {
pub path: String,
pub recursive: bool,
pub glob: Option<String>,
pub max_depth: Option<usize>,
pub max_files: Option<usize>,
pub follow_links: bool,
pub include_hidden: bool,
}
#[derive(Debug, Clone)]
pub struct PivotAggregate {
pub function: String, pub column: String, }
#[derive(Debug, Clone)]
pub enum TableSource {
Table(String), DerivedTable {
query: Box<SelectStatement>,
alias: String, },
Pivot {
source: Box<TableSource>, aggregate: PivotAggregate, pivot_column: String, pivot_values: Vec<String>, alias: Option<String>, },
}
#[derive(Debug, Clone, PartialEq)]
pub enum JoinType {
Inner,
Left,
Right,
Full,
Cross,
}
#[derive(Debug, Clone, PartialEq)]
pub enum JoinOperator {
Equal,
NotEqual,
LessThan,
GreaterThan,
LessThanOrEqual,
GreaterThanOrEqual,
}
#[derive(Debug, Clone)]
pub struct SingleJoinCondition {
pub left_expr: SqlExpression, pub operator: JoinOperator, pub right_expr: SqlExpression, }
#[derive(Debug, Clone)]
pub struct JoinCondition {
pub conditions: Vec<SingleJoinCondition>, }
#[derive(Debug, Clone)]
pub struct JoinClause {
pub join_type: JoinType,
pub table: TableSource, pub alias: Option<String>, pub condition: JoinCondition, }