mod sql_operator;
mod sqltype;
mod table_key;
mod value;
pub use self::sqltype::SQLType;
pub use self::table_key::{AlterOperation, Key, TableKey};
pub use self::value::Value;
pub use self::sql_operator::SQLOperator;
#[derive(Debug, Clone, PartialEq)]
pub enum ASTNode {
SQLIdentifier(String),
SQLAliasedExpr(Box<ASTNode>, String),
SQLWildcard,
SQLCompoundIdentifier(Vec<String>),
SQLAssignment(SQLAssignment),
SQLIsNull(Box<ASTNode>),
SQLIsNotNull(Box<ASTNode>),
SQLBinaryExpr {
left: Box<ASTNode>,
op: SQLOperator,
right: Box<ASTNode>,
},
SQLCast {
expr: Box<ASTNode>,
data_type: SQLType,
},
SQLNested(Box<ASTNode>),
SQLUnary {
operator: SQLOperator,
expr: Box<ASTNode>,
},
SQLValue(Value),
SQLFunction { id: String, args: Vec<ASTNode> },
SQLCase {
conditions: Vec<ASTNode>,
results: Vec<ASTNode>,
else_result: Option<Box<ASTNode>>,
},
SQLSelect {
projection: Vec<ASTNode>,
relation: Option<Box<ASTNode>>,
joins: Vec<Join>,
selection: Option<Box<ASTNode>>,
order_by: Option<Vec<SQLOrderByExpr>>,
group_by: Option<Vec<ASTNode>>,
having: Option<Box<ASTNode>>,
limit: Option<Box<ASTNode>>,
},
SQLInsert {
table_name: String,
columns: Vec<String>,
values: Vec<Vec<ASTNode>>,
},
SQLCopy {
table_name: String,
columns: Vec<String>,
values: Vec<Option<String>>,
},
SQLUpdate {
table_name: String,
assignments: Vec<SQLAssignment>,
selection: Option<Box<ASTNode>>,
},
SQLDelete {
relation: Option<Box<ASTNode>>,
selection: Option<Box<ASTNode>>,
},
SQLCreateTable {
name: String,
columns: Vec<SQLColumnDef>,
},
SQLAlterTable {
name: String,
operation: AlterOperation,
},
}
impl ToString for ASTNode {
fn to_string(&self) -> String {
match self {
ASTNode::SQLAliasedExpr(ast, alias) => {
format!("{} AS {}", ast.as_ref().to_string(), alias)
}
ASTNode::SQLIdentifier(s) => s.to_string(),
ASTNode::SQLWildcard => "*".to_string(),
ASTNode::SQLCompoundIdentifier(s) => s.join("."),
ASTNode::SQLAssignment(ass) => ass.to_string(),
ASTNode::SQLIsNull(ast) => format!("{} IS NULL", ast.as_ref().to_string()),
ASTNode::SQLIsNotNull(ast) => format!("{} IS NOT NULL", ast.as_ref().to_string()),
ASTNode::SQLBinaryExpr { left, op, right } => format!(
"{} {} {}",
left.as_ref().to_string(),
op.to_string(),
right.as_ref().to_string()
),
ASTNode::SQLCast { expr, data_type } => format!(
"CAST({} AS {})",
expr.as_ref().to_string(),
data_type.to_string()
),
ASTNode::SQLNested(ast) => format!("({})", ast.as_ref().to_string()),
ASTNode::SQLUnary { operator, expr } => {
format!("{} {}", operator.to_string(), expr.as_ref().to_string())
}
ASTNode::SQLValue(v) => v.to_string(),
ASTNode::SQLFunction { id, args } => format!(
"{}({})",
id,
args.iter()
.map(|a| a.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLCase {
conditions,
results,
else_result,
} => {
let mut s = format!(
"CASE {}",
conditions
.iter()
.zip(results)
.map(|(c, r)| format!("WHEN {} THEN {}", c.to_string(), r.to_string()))
.collect::<Vec<String>>()
.join(" ")
);
if let Some(else_result) = else_result {
s += &format!(" ELSE {}", else_result.to_string())
}
s + " END"
}
ASTNode::SQLSelect {
projection,
relation,
joins,
selection,
order_by,
group_by,
having,
limit,
} => {
let mut s = format!(
"SELECT {}",
projection
.iter()
.map(|p| p.to_string())
.collect::<Vec<String>>()
.join(", ")
);
if let Some(relation) = relation {
s += &format!(" FROM {}", relation.as_ref().to_string());
}
for join in joins {
s += &join.to_string();
}
if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.as_ref().to_string());
}
if let Some(group_by) = group_by {
s += &format!(
" GROUP BY {}",
group_by
.iter()
.map(|g| g.to_string())
.collect::<Vec<String>>()
.join(", ")
);
}
if let Some(having) = having {
s += &format!(" HAVING {}", having.as_ref().to_string());
}
if let Some(order_by) = order_by {
s += &format!(
" ORDER BY {}",
order_by
.iter()
.map(|o| o.to_string())
.collect::<Vec<String>>()
.join(", ")
);
}
if let Some(limit) = limit {
s += &format!(" LIMIT {}", limit.as_ref().to_string());
}
s
}
ASTNode::SQLInsert {
table_name,
columns,
values,
} => {
let mut s = format!("INSERT INTO {}", table_name);
if columns.len() > 0 {
s += &format!(" ({})", columns.join(", "));
}
if values.len() > 0 {
s += &format!(
" VALUES({})",
values
.iter()
.map(|row| row
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", "))
.collect::<Vec<String>>()
.join(", ")
);
}
s
}
ASTNode::SQLCopy {
table_name,
columns,
values,
} => {
let mut s = format!("COPY {}", table_name);
if columns.len() > 0 {
s += &format!(
" ({})",
columns
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
);
}
s += " FROM stdin; ";
if values.len() > 0 {
s += &format!(
"\n{}",
values
.iter()
.map(|v| v.clone().unwrap_or("\\N".to_string()))
.collect::<Vec<String>>()
.join("\t")
);
}
s += "\n\\.";
s
}
ASTNode::SQLUpdate {
table_name,
assignments,
selection,
} => {
let mut s = format!("UPDATE {}", table_name);
if assignments.len() > 0 {
s += &format!(
"{}",
assignments
.iter()
.map(|ass| ass.to_string())
.collect::<Vec<String>>()
.join(", ")
);
}
if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.as_ref().to_string());
}
s
}
ASTNode::SQLDelete {
relation,
selection,
} => {
let mut s = String::from("DELETE");
if let Some(relation) = relation {
s += &format!(" FROM {}", relation.as_ref().to_string());
}
if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.as_ref().to_string());
}
s
}
ASTNode::SQLCreateTable { name, columns } => format!(
"CREATE TABLE {} ({})",
name,
columns
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLAlterTable { name, operation } => {
format!("ALTER TABLE {} {}", name, operation.to_string())
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SQLAssignment {
id: String,
value: Box<ASTNode>,
}
impl ToString for SQLAssignment {
fn to_string(&self) -> String {
format!("SET {} = {}", self.id, self.value.as_ref().to_string())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SQLOrderByExpr {
pub expr: Box<ASTNode>,
pub asc: bool,
pub nulls_first: Option<bool>,
}
impl SQLOrderByExpr {
pub fn new(expr: Box<ASTNode>, asc: bool, nulls_first: Option<bool>) -> Self {
SQLOrderByExpr {
expr,
asc,
nulls_first,
}
}
}
impl ToString for SQLOrderByExpr {
fn to_string(&self) -> String {
let s = if self.asc {
format!("{} ASC", self.expr.as_ref().to_string())
} else {
format!("{} DESC", self.expr.as_ref().to_string())
};
match self.nulls_first {
Some(true) => s + " NULLS FIRST",
Some(false) => s + " NULLS LAST",
None => s,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SQLColumnDef {
pub name: String,
pub data_type: SQLType,
pub is_primary: bool,
pub is_unique: bool,
pub default: Option<Box<ASTNode>>,
pub allow_null: bool,
}
impl ToString for SQLColumnDef {
fn to_string(&self) -> String {
let mut s = format!("{} {}", self.name, self.data_type.to_string());
if self.is_primary {
s += " PRIMARY KEY";
}
if self.is_unique {
s += " UNIQUE";
}
if let Some(ref default) = self.default {
s += &format!(" DEFAULT {}", default.as_ref().to_string());
}
if !self.allow_null {
s += " NOT NULL";
}
s
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Join {
pub relation: ASTNode,
pub join_operator: JoinOperator,
}
impl ToString for Join {
fn to_string(&self) -> String {
fn prefix(constraint: &JoinConstraint) -> String {
match constraint {
JoinConstraint::Natural => "NATURAL ".to_string(),
_ => "".to_string(),
}
}
fn suffix(constraint: &JoinConstraint) -> String {
match constraint {
JoinConstraint::On(expr) => format!("ON {}", expr.to_string()),
JoinConstraint::Using(attrs) => format!("USING({})", attrs.join(", ")),
_ => "".to_string(),
}
}
match &self.join_operator {
JoinOperator::Inner(constraint) => format!(
" {}JOIN {} {}",
prefix(constraint),
self.relation.to_string(),
suffix(constraint)
),
JoinOperator::Cross => format!(" CROSS JOIN {}", self.relation.to_string()),
JoinOperator::Implicit => format!(", {}", self.relation.to_string()),
JoinOperator::LeftOuter(constraint) => format!(
" {}LEFT JOIN {} {}",
prefix(constraint),
self.relation.to_string(),
suffix(constraint)
),
JoinOperator::RightOuter(constraint) => format!(
" {}RIGHT JOIN {} {}",
prefix(constraint),
self.relation.to_string(),
suffix(constraint)
),
JoinOperator::FullOuter(constraint) => format!(
" {}FULL JOIN {} {}",
prefix(constraint),
self.relation.to_string(),
suffix(constraint)
),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum JoinOperator {
Inner(JoinConstraint),
LeftOuter(JoinConstraint),
RightOuter(JoinConstraint),
FullOuter(JoinConstraint),
Implicit,
Cross,
}
#[derive(Debug, Clone, PartialEq)]
pub enum JoinConstraint {
On(ASTNode),
Using(Vec<String>),
Natural,
}