datafusion-sqlparse/src/sqlast/mod.rs
Nickolay Ponomarev f30ab89ad2 Re-run cargo fmt
2019-03-08 15:46:40 +03:00

431 lines
14 KiB
Rust

// Copyright 2018 Grove Enterprises LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! SQL Abstract Syntax Tree (AST) types
mod query;
mod sql_operator;
mod sqltype;
mod table_key;
mod value;
pub use self::query::{
Cte, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect, SQLSelectItem,
SQLSetExpr, SQLSetOperator, TableFactor,
};
pub use self::sqltype::SQLType;
pub use self::table_key::{AlterOperation, Key, TableKey};
pub use self::value::Value;
pub use self::sql_operator::SQLOperator;
/// Identifier name, in the originally quoted form (e.g. `"id"`)
pub type SQLIdent = String;
/// Represents a parsed SQL expression, which is a common building
/// block of SQL statements (the part after SELECT, WHERE, etc.)
#[derive(Debug, Clone, PartialEq)]
pub enum ASTNode {
/// Identifier e.g. table name or column name
SQLIdentifier(SQLIdent),
/// Unqualified wildcard (`*`). SQL allows this in limited contexts (such as right
/// after `SELECT` or as part of an aggregate function, e.g. `COUNT(*)`, but we
/// currently accept it in contexts where it doesn't make sense, such as `* + *`
SQLWildcard,
/// Qualified wildcard, e.g. `alias.*` or `schema.table.*`.
/// (Same caveats apply to SQLQualifiedWildcard as to SQLWildcard.)
SQLQualifiedWildcard(Vec<SQLIdent>),
/// Multi part identifier e.g. `myschema.dbo.mytable`
SQLCompoundIdentifier(Vec<SQLIdent>),
/// `IS NULL` expression
SQLIsNull(Box<ASTNode>),
/// `IS NOT NULL` expression
SQLIsNotNull(Box<ASTNode>),
/// `[ NOT ] IN (val1, val2, ...)`
SQLInList {
expr: Box<ASTNode>,
list: Vec<ASTNode>,
negated: bool,
},
/// `[ NOT ] IN (SELECT ...)`
SQLInSubquery {
expr: Box<ASTNode>,
subquery: Box<SQLQuery>,
negated: bool,
},
/// <expr> [ NOT ] BETWEEN <low> AND <high>
SQLBetween {
expr: Box<ASTNode>,
negated: bool,
low: Box<ASTNode>,
high: Box<ASTNode>,
},
/// Binary expression e.g. `1 + 1` or `foo > bar`
SQLBinaryExpr {
left: Box<ASTNode>,
op: SQLOperator,
right: Box<ASTNode>,
},
/// CAST an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))`
SQLCast {
expr: Box<ASTNode>,
data_type: SQLType,
},
/// Nested expression e.g. `(foo > bar)` or `(1)`
SQLNested(Box<ASTNode>),
/// Unary expression
SQLUnary {
operator: SQLOperator,
expr: Box<ASTNode>,
},
/// SQLValue
SQLValue(Value),
/// Scalar function call e.g. `LEFT(foo, 5)`
/// TODO: this can be a compound SQLObjectName as well (for UDFs)
SQLFunction { id: SQLIdent, args: Vec<ASTNode> },
/// CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END
SQLCase {
// TODO: support optional operand for "simple case"
conditions: Vec<ASTNode>,
results: Vec<ASTNode>,
else_result: Option<Box<ASTNode>>,
},
/// A parenthesized subquery `(SELECT ...)`, used in expression like
/// `SELECT (subquery) AS x` or `WHERE (subquery) = x`
SQLSubquery(Box<SQLQuery>),
}
impl ToString for ASTNode {
fn to_string(&self) -> String {
match self {
ASTNode::SQLIdentifier(s) => s.to_string(),
ASTNode::SQLWildcard => "*".to_string(),
ASTNode::SQLQualifiedWildcard(q) => q.join(".") + "*",
ASTNode::SQLCompoundIdentifier(s) => s.join("."),
ASTNode::SQLIsNull(ast) => format!("{} IS NULL", ast.as_ref().to_string()),
ASTNode::SQLIsNotNull(ast) => format!("{} IS NOT NULL", ast.as_ref().to_string()),
ASTNode::SQLInList {
expr,
list,
negated,
} => format!(
"{} {}IN ({})",
expr.as_ref().to_string(),
if *negated { "NOT " } else { "" },
list.iter()
.map(|a| a.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLInSubquery {
expr,
subquery,
negated,
} => format!(
"{} {}IN ({})",
expr.as_ref().to_string(),
if *negated { "NOT " } else { "" },
subquery.to_string()
),
ASTNode::SQLBetween {
expr,
negated,
low,
high,
} => format!(
"{} {}BETWEEN {} AND {}",
expr.to_string(),
if *negated { "NOT " } else { "" },
low.to_string(),
high.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::SQLSubquery(s) => format!("({})", s.to_string()),
}
}
}
/// A top-level statement (SELECT, INSERT, CREATE, etc.)
#[derive(Debug, Clone, PartialEq)]
pub enum SQLStatement {
/// SELECT
SQLSelect(SQLQuery),
/// INSERT
SQLInsert {
/// TABLE
table_name: SQLObjectName,
/// COLUMNS
columns: Vec<SQLIdent>,
/// VALUES (vector of rows to insert)
values: Vec<Vec<ASTNode>>,
},
SQLCopy {
/// TABLE
table_name: SQLObjectName,
/// COLUMNS
columns: Vec<SQLIdent>,
/// VALUES a vector of values to be copied
values: Vec<Option<String>>,
},
/// UPDATE
SQLUpdate {
/// TABLE
table_name: SQLObjectName,
/// Column assignments
assignments: Vec<SQLAssignment>,
/// WHERE
selection: Option<ASTNode>,
},
/// DELETE
SQLDelete {
/// FROM
table_name: SQLObjectName,
/// WHERE
selection: Option<ASTNode>,
},
/// CREATE VIEW
SQLCreateView {
/// View name
name: SQLObjectName,
query: SQLQuery,
materialized: bool,
},
/// CREATE TABLE
SQLCreateTable {
/// Table name
name: SQLObjectName,
/// Optional schema
columns: Vec<SQLColumnDef>,
},
/// ALTER TABLE
SQLAlterTable {
/// Table name
name: SQLObjectName,
operation: AlterOperation,
},
}
impl ToString for SQLStatement {
fn to_string(&self) -> String {
match self {
SQLStatement::SQLSelect(s) => s.to_string(),
SQLStatement::SQLInsert {
table_name,
columns,
values,
} => {
let mut s = format!("INSERT INTO {}", table_name.to_string());
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
}
SQLStatement::SQLCopy {
table_name,
columns,
values,
} => {
let mut s = format!("COPY {}", table_name.to_string());
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
}
SQLStatement::SQLUpdate {
table_name,
assignments,
selection,
} => {
let mut s = format!("UPDATE {}", table_name.to_string());
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.to_string());
}
s
}
SQLStatement::SQLDelete {
table_name,
selection,
} => {
let mut s = format!("DELETE FROM {}", table_name.to_string());
if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.to_string());
}
s
}
SQLStatement::SQLCreateView {
name,
query,
materialized,
} => {
let modifier = if *materialized { " MATERIALIZED" } else { "" };
format!(
"CREATE{} VIEW {} AS {}",
modifier,
name.to_string(),
query.to_string()
)
}
SQLStatement::SQLCreateTable { name, columns } => format!(
"CREATE TABLE {} ({})",
name.to_string(),
columns
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
),
SQLStatement::SQLAlterTable { name, operation } => {
format!("ALTER TABLE {} {}", name.to_string(), operation.to_string())
}
}
}
}
/// A name of a table, view, custom type, etc., possibly multi-part, i.e. db.schema.obj
#[derive(Debug, Clone, PartialEq)]
pub struct SQLObjectName(pub Vec<SQLIdent>);
impl ToString for SQLObjectName {
fn to_string(&self) -> String {
self.0.join(".")
}
}
/// SQL assignment `foo = expr` as used in SQLUpdate
#[derive(Debug, Clone, PartialEq)]
pub struct SQLAssignment {
id: SQLIdent,
value: ASTNode,
}
impl ToString for SQLAssignment {
fn to_string(&self) -> String {
format!("SET {} = {}", self.id, self.value.to_string())
}
}
/// SQL column definition
#[derive(Debug, Clone, PartialEq)]
pub struct SQLColumnDef {
pub name: SQLIdent,
pub data_type: SQLType,
pub is_primary: bool,
pub is_unique: bool,
pub default: Option<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.to_string());
}
if !self.allow_null {
s += " NOT NULL";
}
s
}
}