mod data_type;
mod ddl;
mod operator;
mod query;
mod value;
use std::fmt;
pub use self::data_type::DataType;
pub use self::ddl::{
AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, TableConstraint,
};
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
Cte, Fetch, Join, JoinConstraint, JoinOperator, OrderByExpr, Query, Select, SelectItem,
SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Values,
};
pub use self::value::{DateTimeField, Value};
struct DisplaySeparated<'a, T>
where
T: fmt::Display,
{
slice: &'a [T],
sep: &'static str,
}
impl<'a, T> fmt::Display for DisplaySeparated<'a, T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut delim = "";
for t in self.slice {
write!(f, "{}", delim)?;
delim = self.sep;
write!(f, "{}", t)?;
}
Ok(())
}
}
fn display_separated<'a, T>(slice: &'a [T], sep: &'static str) -> DisplaySeparated<'a, T>
where
T: fmt::Display,
{
DisplaySeparated { slice, sep }
}
fn display_comma_separated<T>(slice: &[T]) -> DisplaySeparated<'_, T>
where
T: fmt::Display,
{
DisplaySeparated { slice, sep: ", " }
}
pub type Ident = String;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ObjectName(pub Vec<Ident>);
impl fmt::Display for ObjectName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", display_separated(&self.0, "."))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
Identifier(Ident),
Wildcard,
QualifiedWildcard(Vec<Ident>),
CompoundIdentifier(Vec<Ident>),
IsNull(Box<Expr>),
IsNotNull(Box<Expr>),
InList {
expr: Box<Expr>,
list: Vec<Expr>,
negated: bool,
},
InSubquery {
expr: Box<Expr>,
subquery: Box<Query>,
negated: bool,
},
Between {
expr: Box<Expr>,
negated: bool,
low: Box<Expr>,
high: Box<Expr>,
},
BinaryOp {
left: Box<Expr>,
op: BinaryOperator,
right: Box<Expr>,
},
UnaryOp { op: UnaryOperator, expr: Box<Expr> },
Cast {
expr: Box<Expr>,
data_type: DataType,
},
Extract {
field: DateTimeField,
expr: Box<Expr>,
},
Collate {
expr: Box<Expr>,
collation: ObjectName,
},
Nested(Box<Expr>),
Value(Value),
Function(Function),
Case {
operand: Option<Box<Expr>>,
conditions: Vec<Expr>,
results: Vec<Expr>,
else_result: Option<Box<Expr>>,
},
Exists(Box<Query>),
Subquery(Box<Query>),
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Identifier(s) => f.write_str(s),
Expr::Wildcard => f.write_str("*"),
Expr::QualifiedWildcard(q) => write!(f, "{}.*", display_separated(q, ".")),
Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")),
Expr::IsNull(ast) => write!(f, "{} IS NULL", ast),
Expr::IsNotNull(ast) => write!(f, "{} IS NOT NULL", ast),
Expr::InList {
expr,
list,
negated,
} => write!(
f,
"{} {}IN ({})",
expr,
if *negated { "NOT " } else { "" },
display_comma_separated(list)
),
Expr::InSubquery {
expr,
subquery,
negated,
} => write!(
f,
"{} {}IN ({})",
expr,
if *negated { "NOT " } else { "" },
subquery
),
Expr::Between {
expr,
negated,
low,
high,
} => write!(
f,
"{} {}BETWEEN {} AND {}",
expr,
if *negated { "NOT " } else { "" },
low,
high
),
Expr::BinaryOp { left, op, right } => write!(f, "{} {} {}", left, op, right),
Expr::UnaryOp { op, expr } => write!(f, "{} {}", op, expr),
Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type),
Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr),
Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation),
Expr::Nested(ast) => write!(f, "({})", ast),
Expr::Value(v) => write!(f, "{}", v),
Expr::Function(fun) => write!(f, "{}", fun),
Expr::Case {
operand,
conditions,
results,
else_result,
} => {
f.write_str("CASE")?;
if let Some(operand) = operand {
write!(f, " {}", operand)?;
}
for (c, r) in conditions.iter().zip(results) {
write!(f, " WHEN {} THEN {}", c, r)?;
}
if let Some(else_result) = else_result {
write!(f, " ELSE {}", else_result)?;
}
f.write_str(" END")
}
Expr::Exists(s) => write!(f, "EXISTS ({})", s),
Expr::Subquery(s) => write!(f, "({})", s),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct WindowSpec {
pub partition_by: Vec<Expr>,
pub order_by: Vec<OrderByExpr>,
pub window_frame: Option<WindowFrame>,
}
impl fmt::Display for WindowSpec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut delim = "";
if !self.partition_by.is_empty() {
delim = " ";
write!(
f,
"PARTITION BY {}",
display_comma_separated(&self.partition_by)
)?;
}
if !self.order_by.is_empty() {
f.write_str(delim)?;
delim = " ";
write!(f, "ORDER BY {}", display_comma_separated(&self.order_by))?;
}
if let Some(window_frame) = &self.window_frame {
if let Some(end_bound) = &window_frame.end_bound {
f.write_str(delim)?;
write!(
f,
"{} BETWEEN {} AND {}",
window_frame.units, window_frame.start_bound, end_bound
)?;
} else {
f.write_str(delim)?;
write!(f, "{} {}", window_frame.units, window_frame.start_bound)?;
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct WindowFrame {
pub units: WindowFrameUnits,
pub start_bound: WindowFrameBound,
pub end_bound: Option<WindowFrameBound>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum WindowFrameUnits {
Rows,
Range,
Groups,
}
impl fmt::Display for WindowFrameUnits {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
WindowFrameUnits::Rows => "ROWS",
WindowFrameUnits::Range => "RANGE",
WindowFrameUnits::Groups => "GROUPS",
})
}
}
impl FromStr for WindowFrameUnits {
type Err = ParserError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ROWS" => Ok(WindowFrameUnits::Rows),
"RANGE" => Ok(WindowFrameUnits::Range),
"GROUPS" => Ok(WindowFrameUnits::Groups),
_ => Err(ParserError::ParserError(format!(
"Expected ROWS, RANGE, or GROUPS, found: {}",
s
))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum WindowFrameBound {
CurrentRow,
Preceding(Option<u64>),
Following(Option<u64>),
}
impl fmt::Display for WindowFrameBound {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
WindowFrameBound::CurrentRow => f.write_str("CURRENT ROW"),
WindowFrameBound::Preceding(None) => f.write_str("UNBOUNDED PRECEDING"),
WindowFrameBound::Following(None) => f.write_str("UNBOUNDED FOLLOWING"),
WindowFrameBound::Preceding(Some(n)) => write!(f, "{} PRECEDING", n),
WindowFrameBound::Following(Some(n)) => write!(f, "{} FOLLOWING", n),
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Statement {
Query(Box<Query>),
Insert {
table_name: ObjectName,
columns: Vec<Ident>,
source: Box<Query>,
},
Copy {
table_name: ObjectName,
columns: Vec<Ident>,
values: Vec<Option<String>>,
},
Update {
table_name: ObjectName,
assignments: Vec<Assignment>,
selection: Option<Expr>,
},
Delete {
table_name: ObjectName,
selection: Option<Expr>,
},
CreateView {
name: ObjectName,
columns: Vec<Ident>,
query: Box<Query>,
materialized: bool,
with_options: Vec<SqlOption>,
},
CreateTable {
name: ObjectName,
columns: Vec<ColumnDef>,
constraints: Vec<TableConstraint>,
with_options: Vec<SqlOption>,
external: bool,
file_format: Option<FileFormat>,
location: Option<String>,
},
AlterTable {
name: ObjectName,
operation: AlterTableOperation,
},
Drop {
object_type: ObjectType,
if_exists: bool,
names: Vec<ObjectName>,
cascade: bool,
},
SetVariable {
local: bool,
variable: Ident,
value: SetVariableValue,
},
ShowVariable { variable: Ident },
ShowColumns {
extended: bool,
full: bool,
table_name: ObjectName,
filter: Option<ShowStatementFilter>,
},
StartTransaction { modes: Vec<TransactionMode> },
SetTransaction { modes: Vec<TransactionMode> },
Commit { chain: bool },
Rollback { chain: bool },
}
impl fmt::Display for Statement {
#[allow(clippy::cognitive_complexity)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Statement::Query(s) => write!(f, "{}", s),
Statement::Insert {
table_name,
columns,
source,
} => {
write!(f, "INSERT INTO {} ", table_name)?;
if !columns.is_empty() {
write!(f, "({}) ", display_comma_separated(columns))?;
}
write!(f, "{}", source)
}
Statement::Copy {
table_name,
columns,
values,
} => {
write!(f, "COPY {}", table_name)?;
if !columns.is_empty() {
write!(f, " ({})", display_comma_separated(columns))?;
}
write!(f, " FROM stdin; ")?;
if !values.is_empty() {
writeln!(f)?;
let mut delim = "";
for v in values {
write!(f, "{}", delim)?;
delim = "\t";
if let Some(v) = v {
write!(f, "{}", v)?;
} else {
write!(f, "\\N")?;
}
}
}
write!(f, "\n\\.")
}
Statement::Update {
table_name,
assignments,
selection,
} => {
write!(f, "UPDATE {}", table_name)?;
if !assignments.is_empty() {
write!(f, " SET ")?;
write!(f, "{}", display_comma_separated(assignments))?;
}
if let Some(selection) = selection {
write!(f, " WHERE {}", selection)?;
}
Ok(())
}
Statement::Delete {
table_name,
selection,
} => {
write!(f, "DELETE FROM {}", table_name)?;
if let Some(selection) = selection {
write!(f, " WHERE {}", selection)?;
}
Ok(())
}
Statement::CreateView {
name,
columns,
query,
materialized,
with_options,
} => {
write!(f, "CREATE")?;
if *materialized {
write!(f, " MATERIALIZED")?;
}
write!(f, " VIEW {}", name)?;
if !with_options.is_empty() {
write!(f, " WITH ({})", display_comma_separated(with_options))?;
}
if !columns.is_empty() {
write!(f, " ({})", display_comma_separated(columns))?;
}
write!(f, " AS {}", query)
}
Statement::CreateTable {
name,
columns,
constraints,
with_options,
external,
file_format,
location,
} => {
write!(
f,
"CREATE {}TABLE {} ({}",
if *external { "EXTERNAL " } else { "" },
name,
display_comma_separated(columns)
)?;
if !constraints.is_empty() {
write!(f, ", {}", display_comma_separated(constraints))?;
}
write!(f, ")")?;
if *external {
write!(
f,
" STORED AS {} LOCATION '{}'",
file_format.as_ref().unwrap(),
location.as_ref().unwrap()
)?;
}
if !with_options.is_empty() {
write!(f, " WITH ({})", display_comma_separated(with_options))?;
}
Ok(())
}
Statement::AlterTable { name, operation } => {
write!(f, "ALTER TABLE {} {}", name, operation)
}
Statement::Drop {
object_type,
if_exists,
names,
cascade,
} => write!(
f,
"DROP {}{} {}{}",
object_type,
if *if_exists { " IF EXISTS" } else { "" },
display_comma_separated(names),
if *cascade { " CASCADE" } else { "" },
),
Statement::SetVariable {
local,
variable,
value,
} => {
f.write_str("SET ")?;
if *local {
f.write_str("LOCAL ")?;
}
write!(f, "{} = {}", variable, value)
}
Statement::ShowVariable { variable } => write!(f, "SHOW {}", variable),
Statement::ShowColumns {
extended,
full,
table_name,
filter,
} => {
f.write_str("SHOW ")?;
if *extended {
f.write_str("EXTENDED ")?;
}
if *full {
f.write_str("FULL ")?;
}
write!(f, "COLUMNS FROM {}", table_name)?;
if let Some(filter) = filter {
write!(f, " {}", filter)?;
}
Ok(())
}
Statement::StartTransaction { modes } => {
write!(f, "START TRANSACTION")?;
if !modes.is_empty() {
write!(f, " {}", display_comma_separated(modes))?;
}
Ok(())
}
Statement::SetTransaction { modes } => {
write!(f, "SET TRANSACTION")?;
if !modes.is_empty() {
write!(f, " {}", display_comma_separated(modes))?;
}
Ok(())
}
Statement::Commit { chain } => {
write!(f, "COMMIT{}", if *chain { " AND CHAIN" } else { "" },)
}
Statement::Rollback { chain } => {
write!(f, "ROLLBACK{}", if *chain { " AND CHAIN" } else { "" },)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Assignment {
pub id: Ident,
pub value: Expr,
}
impl fmt::Display for Assignment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} = {}", self.id, self.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Function {
pub name: ObjectName,
pub args: Vec<Expr>,
pub over: Option<WindowSpec>,
pub distinct: bool,
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}({}{})",
self.name,
if self.distinct { "DISTINCT " } else { "" },
display_comma_separated(&self.args),
)?;
if let Some(o) = &self.over {
write!(f, " OVER ({})", o)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum FileFormat {
TEXTFILE,
SEQUENCEFILE,
ORC,
PARQUET,
AVRO,
RCFILE,
JSONFILE,
}
impl fmt::Display for FileFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::FileFormat::*;
f.write_str(match self {
TEXTFILE => "TEXTFILE",
SEQUENCEFILE => "SEQUENCEFILE",
ORC => "ORC",
PARQUET => "PARQUET",
AVRO => "AVRO",
RCFILE => "RCFILE",
JSONFILE => "TEXTFILE",
})
}
}
use crate::parser::ParserError;
use std::str::FromStr;
impl FromStr for FileFormat {
type Err = ParserError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::FileFormat::*;
match s {
"TEXTFILE" => Ok(TEXTFILE),
"SEQUENCEFILE" => Ok(SEQUENCEFILE),
"ORC" => Ok(ORC),
"PARQUET" => Ok(PARQUET),
"AVRO" => Ok(AVRO),
"RCFILE" => Ok(RCFILE),
"JSONFILE" => Ok(JSONFILE),
_ => Err(ParserError::ParserError(format!(
"Unexpected file format: {}",
s
))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ObjectType {
Table,
View,
}
impl fmt::Display for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
ObjectType::Table => "TABLE",
ObjectType::View => "VIEW",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SqlOption {
pub name: Ident,
pub value: Value,
}
impl fmt::Display for SqlOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} = {}", self.name, self.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TransactionMode {
AccessMode(TransactionAccessMode),
IsolationLevel(TransactionIsolationLevel),
}
impl fmt::Display for TransactionMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionMode::*;
match self {
AccessMode(access_mode) => write!(f, "{}", access_mode),
IsolationLevel(iso_level) => write!(f, "ISOLATION LEVEL {}", iso_level),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TransactionAccessMode {
ReadOnly,
ReadWrite,
}
impl fmt::Display for TransactionAccessMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionAccessMode::*;
f.write_str(match self {
ReadOnly => "READ ONLY",
ReadWrite => "READ WRITE",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TransactionIsolationLevel {
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
}
impl fmt::Display for TransactionIsolationLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionIsolationLevel::*;
f.write_str(match self {
ReadUncommitted => "READ UNCOMMITTED",
ReadCommitted => "READ COMMITTED",
RepeatableRead => "REPEATABLE READ",
Serializable => "SERIALIZABLE",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ShowStatementFilter {
Like(String),
Where(Expr),
}
impl fmt::Display for ShowStatementFilter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ShowStatementFilter::*;
match self {
Like(pattern) => write!(f, "LIKE '{}'", value::escape_single_quote_string(pattern)),
Where(expr) => write!(f, "WHERE {}", expr),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SetVariableValue {
Ident(Ident),
Literal(Value),
}
impl fmt::Display for SetVariableValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use SetVariableValue::*;
match self {
Ident(ident) => f.write_str(ident),
Literal(literal) => write!(f, "{}", literal),
}
}
}