ran cargo fmt

This commit is contained in:
Andy Grove 2018-10-06 09:39:26 -06:00
parent 9daba3ab49
commit 722ea7a91b
11 changed files with 662 additions and 615 deletions

View file

@ -32,5 +32,3 @@ pub trait Dialect {
/// Determine if a character is a valid identifier character /// Determine if a character is a valid identifier character
fn is_identifier_part(&self, ch: char) -> bool; fn is_identifier_part(&self, ch: char) -> bool;
} }

View file

@ -6,67 +6,12 @@ pub struct GenericSqlDialect {}
impl Dialect for GenericSqlDialect { impl Dialect for GenericSqlDialect {
fn keywords(&self) -> Vec<&'static str> { fn keywords(&self) -> Vec<&'static str> {
return vec![ return vec![
SELECT, SELECT, FROM, WHERE, LIMIT, ORDER, GROUP, BY, HAVING, UNION, ALL, INSERT, INTO, UPDATE,
FROM, DELETE, IN, IS, NULL, SET, CREATE, EXTERNAL, TABLE, ASC, DESC, AND, OR, NOT, AS,
WHERE, STORED, CSV, PARQUET, LOCATION, WITH, WITHOUT, HEADER, ROW, // SQL types
LIMIT, CHAR, CHARACTER, VARYING, LARGE, OBJECT, VARCHAR, CLOB, BINARY, VARBINARY, BLOB, FLOAT,
ORDER, REAL, DOUBLE, PRECISION, INT, INTEGER, SMALLINT, BIGINT, NUMERIC, DECIMAL, DEC,
GROUP, BOOLEAN, DATE, TIME, TIMESTAMP,
BY,
HAVING,
UNION,
ALL,
INSERT,
INTO,
UPDATE,
DELETE,
IN,
IS,
NULL,
SET,
CREATE,
EXTERNAL,
TABLE,
ASC,
DESC,
AND,
OR,
NOT,
AS,
STORED,
CSV,
PARQUET,
LOCATION,
WITH,
WITHOUT,
HEADER,
ROW,
// SQL types
CHAR,
CHARACTER,
VARYING,
LARGE,
OBJECT,
VARCHAR,
CLOB,
BINARY,
VARBINARY,
BLOB,
FLOAT,
REAL,
DOUBLE,
PRECISION,
INT,
INTEGER,
SMALLINT,
BIGINT,
NUMERIC,
DECIMAL,
DEC,
BOOLEAN,
DATE,
TIME,
TIMESTAMP,
]; ];
} }
@ -82,4 +27,3 @@ impl Dialect for GenericSqlDialect {
|| ch == '_' || ch == '_'
} }
} }

View file

@ -7,83 +7,13 @@ pub struct PostgreSqlDialect {}
impl Dialect for PostgreSqlDialect { impl Dialect for PostgreSqlDialect {
fn keywords(&self) -> Vec<&'static str> { fn keywords(&self) -> Vec<&'static str> {
return vec![ return vec![
ALTER, ALTER, ONLY, SELECT, FROM, WHERE, LIMIT, ORDER, GROUP, BY, HAVING, UNION, ALL, INSERT,
ONLY, INTO, UPDATE, DELETE, IN, IS, NULL, SET, CREATE, EXTERNAL, TABLE, ASC, DESC, AND, OR,
SELECT, NOT, AS, STORED, CSV, WITH, WITHOUT, ROW, // SQL types
FROM, CHAR, CHARACTER, VARYING, LARGE, VARCHAR, CLOB, BINARY, VARBINARY, BLOB, FLOAT, REAL,
WHERE, DOUBLE, PRECISION, INT, INTEGER, SMALLINT, BIGINT, NUMERIC, DECIMAL, DEC, BOOLEAN,
LIMIT, DATE, TIME, TIMESTAMP, VALUES, DEFAULT, ZONE, REGCLASS, TEXT, BYTEA, TRUE, FALSE, COPY,
ORDER, STDIN, PRIMARY, KEY, UNIQUE, UUID, ADD, CONSTRAINT, FOREIGN, REFERENCES,
GROUP,
BY,
HAVING,
UNION,
ALL,
INSERT,
INTO,
UPDATE,
DELETE,
IN,
IS,
NULL,
SET,
CREATE,
EXTERNAL,
TABLE,
ASC,
DESC,
AND,
OR,
NOT,
AS,
STORED,
CSV,
WITH,
WITHOUT,
ROW,
// SQL types
CHAR,
CHARACTER,
VARYING,
LARGE,
VARCHAR,
CLOB,
BINARY,
VARBINARY,
BLOB,
FLOAT,
REAL,
DOUBLE,
PRECISION,
INT,
INTEGER,
SMALLINT,
BIGINT,
NUMERIC,
DECIMAL,
DEC,
BOOLEAN,
DATE,
TIME,
TIMESTAMP,
VALUES,
DEFAULT,
ZONE,
REGCLASS,
TEXT,
BYTEA,
TRUE,
FALSE,
COPY,
STDIN,
PRIMARY,
KEY,
UNIQUE,
UUID,
ADD,
CONSTRAINT,
FOREIGN,
REFERENCES,
]; ];
} }

View file

@ -14,31 +14,22 @@
//! SQL Abstract Syntax Tree (AST) types //! SQL Abstract Syntax Tree (AST) types
//! //!
use chrono::{NaiveDate, use chrono::{
NaiveDateTime, offset::{FixedOffset, TimeZone},
NaiveTime, DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc,
offset::{FixedOffset,
TimeZone,
},
DateTime,
Utc,
}; };
use uuid::Uuid;
pub use self::value::Value;
pub use self::sqltype::SQLType; pub use self::sqltype::SQLType;
pub use self::table_key::{ pub use self::table_key::{AlterOperation, Key, TableKey};
AlterOperation, pub use self::value::Value;
TableKey, use uuid::Uuid;
Key
};
pub use self::sql_operator::SQLOperator; pub use self::sql_operator::SQLOperator;
mod value; mod sql_operator;
mod sqltype; mod sqltype;
mod table_key; mod table_key;
mod sql_operator; mod value;
/// SQL Abstract Syntax Tree (AST) /// SQL Abstract Syntax Tree (AST)
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -139,12 +130,10 @@ pub enum ASTNode {
/// Table name /// Table name
name: String, name: String,
operation: AlterOperation, operation: AlterOperation,
},
} }
}
impl ToString for ASTNode { impl ToString for ASTNode {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
ASTNode::SQLIdentifier(s) => s.to_string(), ASTNode::SQLIdentifier(s) => s.to_string(),
@ -153,18 +142,30 @@ impl ToString for ASTNode{
ASTNode::SQLAssignment(ass) => ass.to_string(), ASTNode::SQLAssignment(ass) => ass.to_string(),
ASTNode::SQLIsNull(ast) => format!("{} IS NULL", ast.as_ref().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::SQLIsNotNull(ast) => format!("{} IS NOT NULL", ast.as_ref().to_string()),
ASTNode::SQLBinaryExpr{left, op, right} => { ASTNode::SQLBinaryExpr { left, op, right } => format!(
format!("{} {} {}", left.as_ref().to_string(), op.to_string(), right.as_ref().to_string()) "{} {} {}",
} left.as_ref().to_string(),
ASTNode::SQLCast{expr, data_type} => { op.to_string(),
format!("CAST({} AS {})", expr.as_ref().to_string(), data_type.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::SQLNested(ast) => format!("({})", ast.as_ref().to_string()),
ASTNode::SQLUnary { operator, rex } => { ASTNode::SQLUnary { operator, rex } => {
format!("{} {}", operator.to_string(), rex.as_ref().to_string()) format!("{} {}", operator.to_string(), rex.as_ref().to_string())
} }
ASTNode::SQLValue(v) => v.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::SQLFunction { id, args } => format!(
"{}({})",
id,
args.iter()
.map(|a| a.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLSelect { ASTNode::SQLSelect {
projection, projection,
relation, relation,
@ -174,7 +175,14 @@ impl ToString for ASTNode{
having, having,
limit, limit,
} => { } => {
let mut s = format!("SELECT {}", projection.iter().map(|p|p.to_string()).collect::<Vec<String>>().join(", ")); let mut s = format!(
"SELECT {}",
projection
.iter()
.map(|p| p.to_string())
.collect::<Vec<String>>()
.join(", ")
);
if let Some(relation) = relation { if let Some(relation) = relation {
s += &format!(" FROM {}", relation.as_ref().to_string()); s += &format!(" FROM {}", relation.as_ref().to_string());
} }
@ -182,61 +190,113 @@ impl ToString for ASTNode{
s += &format!(" WHERE {}", selection.as_ref().to_string()); s += &format!(" WHERE {}", selection.as_ref().to_string());
} }
if let Some(group_by) = group_by { if let Some(group_by) = group_by {
s += &format!(" GROUP BY {}", group_by.iter().map(|g|g.to_string()).collect::<Vec<String>>().join(", ")); s += &format!(
" GROUP BY {}",
group_by
.iter()
.map(|g| g.to_string())
.collect::<Vec<String>>()
.join(", ")
);
} }
if let Some(having) = having { if let Some(having) = having {
s += &format!(" HAVING {}", having.as_ref().to_string()); s += &format!(" HAVING {}", having.as_ref().to_string());
} }
if let Some(order_by) = order_by { if let Some(order_by) = order_by {
s += &format!(" ORDER BY {}", order_by.iter().map(|o|o.to_string()).collect::<Vec<String>>().join(", ")); s += &format!(
" ORDER BY {}",
order_by
.iter()
.map(|o| o.to_string())
.collect::<Vec<String>>()
.join(", ")
);
} }
if let Some(limit) = limit { if let Some(limit) = limit {
s += &format!(" LIMIT {}", limit.as_ref().to_string()); s += &format!(" LIMIT {}", limit.as_ref().to_string());
} }
s s
} }
ASTNode::SQLInsert{ table_name, columns, values } => { ASTNode::SQLInsert {
table_name,
columns,
values,
} => {
let mut s = format!("INSERT INTO {}", table_name); let mut s = format!("INSERT INTO {}", table_name);
if columns.len() > 0 { if columns.len() > 0 {
s += &format!(" ({})", columns.join(", ")); s += &format!(" ({})", columns.join(", "));
} }
if values.len() > 0 { if values.len() > 0 {
s += &format!(" VALUES({})", s += &format!(
values.iter() " VALUES({})",
.map(|row|row.iter().map(|c|c.to_string()) values
.collect::<Vec<String>>().join(", ") .iter()
).collect::<Vec<String>>().join(", ") .map(|row| row
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", "))
.collect::<Vec<String>>()
.join(", ")
); );
} }
s s
} }
ASTNode::SQLCopy{table_name, columns, values} => { ASTNode::SQLCopy {
table_name,
columns,
values,
} => {
let mut s = format!("COPY {}", table_name); let mut s = format!("COPY {}", table_name);
if columns.len() > 0 { if columns.len() > 0 {
s += &format!(" ({})", columns.iter().map(|c|c.to_string()).collect::<Vec<String>>().join(", ")); s += &format!(
" ({})",
columns
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
);
} }
s += " FROM stdin; "; s += " FROM stdin; ";
if values.len() > 0 { if values.len() > 0 {
s += &format!("\n{}", s += &format!(
values.iter() "\n{}",
values
.iter()
.map(|v| v.to_string()) .map(|v| v.to_string())
.collect::<Vec<String>>().join("\t") .collect::<Vec<String>>()
.join("\t")
); );
} }
s += "\n\\."; s += "\n\\.";
s s
} }
ASTNode::SQLUpdate{table_name, assignments, selection} => { ASTNode::SQLUpdate {
table_name,
assignments,
selection,
} => {
let mut s = format!("UPDATE {}", table_name); let mut s = format!("UPDATE {}", table_name);
if assignments.len() > 0 { if assignments.len() > 0 {
s += &format!("{}", assignments.iter().map(|ass|ass.to_string()).collect::<Vec<String>>().join(", ")); s += &format!(
"{}",
assignments
.iter()
.map(|ass| ass.to_string())
.collect::<Vec<String>>()
.join(", ")
);
} }
if let Some(selection) = selection { if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.as_ref().to_string()); s += &format!(" WHERE {}", selection.as_ref().to_string());
} }
s s
} }
ASTNode::SQLDelete{relation, selection} => { ASTNode::SQLDelete {
relation,
selection,
} => {
let mut s = String::from("DELETE"); let mut s = String::from("DELETE");
if let Some(relation) = relation { if let Some(relation) = relation {
s += &format!(" FROM {}", relation.as_ref().to_string()); s += &format!(" FROM {}", relation.as_ref().to_string());
@ -246,9 +306,15 @@ impl ToString for ASTNode{
} }
s s
} }
ASTNode::SQLCreateTable{name, columns} => { ASTNode::SQLCreateTable { name, columns } => format!(
format!("CREATE TABLE {} ({})", name, columns.iter().map(|c|c.to_string()).collect::<Vec<String>>().join(", ")) "CREATE TABLE {} ({})",
} name,
columns
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", ")
),
ASTNode::SQLAlterTable { name, operation } => { ASTNode::SQLAlterTable { name, operation } => {
format!("ALTER TABLE {} {}", name, operation.to_string()) format!("ALTER TABLE {} {}", name, operation.to_string())
} }
@ -265,7 +331,6 @@ pub struct SQLAssignment {
} }
impl ToString for SQLAssignment { impl ToString for SQLAssignment {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!("SET {} = {}", self.id, self.value.as_ref().to_string()) format!("SET {} = {}", self.id, self.value.as_ref().to_string())
} }
@ -305,9 +370,7 @@ pub struct SQLColumnDef {
pub allow_null: bool, pub allow_null: bool,
} }
impl ToString for SQLColumnDef { impl ToString for SQLColumnDef {
fn to_string(&self) -> String { fn to_string(&self) -> String {
let mut s = format!("{} {}", self.name, self.data_type.to_string()); let mut s = format!("{} {}", self.name, self.data_type.to_string());
if self.is_primary { if self.is_primary {
@ -325,4 +388,3 @@ impl ToString for SQLColumnDef{
s s
} }
} }

View file

@ -1,4 +1,3 @@
/// SQL Operator /// SQL Operator
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum SQLOperator { pub enum SQLOperator {
@ -18,7 +17,6 @@ pub enum SQLOperator {
} }
impl ToString for SQLOperator { impl ToString for SQLOperator {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
SQLOperator::Plus => "+".to_string(), SQLOperator::Plus => "+".to_string(),
@ -37,4 +35,3 @@ impl ToString for SQLOperator{
} }
} }
} }

View file

@ -1,4 +1,3 @@
/// SQL datatypes for literals in SQL statements /// SQL datatypes for literals in SQL statements
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum SQLType { pub enum SQLType {
@ -51,37 +50,44 @@ pub enum SQLType {
} }
impl ToString for SQLType { impl ToString for SQLType {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
SQLType::Char(size) => if let Some(size) = size { SQLType::Char(size) => {
if let Some(size) = size {
format!("char({})", size) format!("char({})", size)
} else { } else {
"char".to_string() "char".to_string()
} }
SQLType::Varchar(size) => if let Some(size) = size{ }
SQLType::Varchar(size) => {
if let Some(size) = size {
format!("character varying({})", size) format!("character varying({})", size)
} else { } else {
"character varying".to_string() "character varying".to_string()
} }
}
SQLType::Uuid => "uuid".to_string(), SQLType::Uuid => "uuid".to_string(),
SQLType::Clob(size) => format!("clob({})", size), SQLType::Clob(size) => format!("clob({})", size),
SQLType::Binary(size) => format!("binary({})", size), SQLType::Binary(size) => format!("binary({})", size),
SQLType::Varbinary(size) => format!("varbinary({})", size), SQLType::Varbinary(size) => format!("varbinary({})", size),
SQLType::Blob(size) => format!("blob({})", size), SQLType::Blob(size) => format!("blob({})", size),
SQLType::Decimal(precision, scale) => if let Some(scale) = scale{ SQLType::Decimal(precision, scale) => {
if let Some(scale) = scale {
format!("numeric({},{})", precision, scale) format!("numeric({},{})", precision, scale)
} else { } else {
format!("numeric({})", precision) format!("numeric({})", precision)
}, }
}
SQLType::SmallInt => "smallint".to_string(), SQLType::SmallInt => "smallint".to_string(),
SQLType::Int => "int".to_string(), SQLType::Int => "int".to_string(),
SQLType::BigInt => "bigint".to_string(), SQLType::BigInt => "bigint".to_string(),
SQLType::Float(size) => if let Some(size) = size{ SQLType::Float(size) => {
if let Some(size) = size {
format!("float({})", size) format!("float({})", size)
} else { } else {
"float".to_string() "float".to_string()
}, }
}
SQLType::Real => "real".to_string(), SQLType::Real => "real".to_string(),
SQLType::Double => "double".to_string(), SQLType::Double => "double".to_string(),
SQLType::Boolean => "boolean".to_string(), SQLType::Boolean => "boolean".to_string(),

View file

@ -1,23 +1,20 @@
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum AlterOperation { pub enum AlterOperation {
AddConstraint(TableKey), AddConstraint(TableKey),
RemoveConstraint{ RemoveConstraint { name: String },
name: String,
}
} }
impl ToString for AlterOperation { impl ToString for AlterOperation {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
AlterOperation::AddConstraint(table_key) => format!("ADD CONSTRAINT {}", table_key.to_string()), AlterOperation::AddConstraint(table_key) => {
format!("ADD CONSTRAINT {}", table_key.to_string())
}
AlterOperation::RemoveConstraint { name } => format!("REMOVE CONSTRAINT {}", name), AlterOperation::RemoveConstraint { name } => format!("REMOVE CONSTRAINT {}", name),
} }
} }
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Key { pub struct Key {
pub name: String, pub name: String,
@ -33,12 +30,10 @@ pub enum TableKey{
key: Key, key: Key,
foreign_table: String, foreign_table: String,
referred_columns: Vec<String>, referred_columns: Vec<String>,
},
} }
}
impl ToString for TableKey { impl ToString for TableKey {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
TableKey::PrimaryKey(ref key) => { TableKey::PrimaryKey(ref key) => {
@ -47,12 +42,18 @@ impl ToString for TableKey{
TableKey::UniqueKey(ref key) => { TableKey::UniqueKey(ref key) => {
format!("{} UNIQUE KEY ({})", key.name, key.columns.join(", ")) format!("{} UNIQUE KEY ({})", key.name, key.columns.join(", "))
} }
TableKey::Key(ref key) => { TableKey::Key(ref key) => format!("{} KEY ({})", key.name, key.columns.join(", ")),
format!("{} KEY ({})", key.name, key.columns.join(", ")) TableKey::ForeignKey {
} key,
TableKey::ForeignKey{key, foreign_table, referred_columns} => { foreign_table,
format!("{} FOREIGN KEY ({}) REFERENCES {}({})", key.name, key.columns.join(", "), foreign_table, referred_columns.join(", ")) referred_columns,
} } => format!(
"{} FOREIGN KEY ({}) REFERENCES {}({})",
key.name,
key.columns.join(", "),
foreign_table,
referred_columns.join(", ")
),
} }
} }
} }

View file

@ -1,12 +1,6 @@
use chrono::{
use chrono::{NaiveDate, offset::{FixedOffset, TimeZone},
NaiveDateTime, DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc,
NaiveTime,
offset::{FixedOffset,
TimeZone,
},
DateTime,
Utc,
}; };
use uuid::Uuid; use uuid::Uuid;
@ -40,9 +34,7 @@ pub enum Value{
Null, Null,
} }
impl ToString for Value { impl ToString for Value {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
Value::Long(v) => v.to_string(), Value::Long(v) => v.to_string(),

View file

@ -17,14 +17,9 @@
use super::dialect::Dialect; use super::dialect::Dialect;
use super::sqlast::*; use super::sqlast::*;
use super::sqltokenizer::*; use super::sqltokenizer::*;
use chrono::{NaiveDate, use chrono::{
NaiveDateTime, offset::{FixedOffset, TimeZone},
NaiveTime, DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc,
offset::{FixedOffset,
TimeZone,
},
DateTime,
Utc,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -95,8 +90,7 @@ impl Parser {
/// Parse an expression prefix /// Parse an expression prefix
pub fn parse_prefix(&mut self) -> Result<ASTNode, ParserError> { pub fn parse_prefix(&mut self) -> Result<ASTNode, ParserError> {
match self.next_token() { match self.next_token() {
Some(t) => { Some(t) => match t {
match t {
Token::Keyword(k) => match k.to_uppercase().as_ref() { Token::Keyword(k) => match k.to_uppercase().as_ref() {
"SELECT" => Ok(self.parse_select()?), "SELECT" => Ok(self.parse_select()?),
"CREATE" => Ok(self.parse_create()?), "CREATE" => Ok(self.parse_create()?),
@ -124,9 +118,7 @@ impl Parser {
self.parse_cast_expression() self.parse_cast_expression()
} else { } else {
match self.peek_token() { match self.peek_token() {
Some(Token::LParen) => { Some(Token::LParen) => self.parse_function_or_pg_cast(&id),
self.parse_function_or_pg_cast(&id)
}
Some(Token::Period) => { Some(Token::Period) => {
let mut id_parts: Vec<String> = vec![id]; let mut id_parts: Vec<String> = vec![id];
while self.peek_token() == Some(Token::Period) { while self.peek_token() == Some(Token::Period) {
@ -149,7 +141,7 @@ impl Parser {
Token::Number(ref n) => { Token::Number(ref n) => {
self.prev_token(); self.prev_token();
self.parse_sql_value() self.parse_sql_value()
}, }
Token::String(ref s) => { Token::String(ref s) => {
self.prev_token(); self.prev_token();
self.parse_sql_value() self.parse_sql_value()
@ -166,8 +158,7 @@ impl Parser {
"Prefix parser expected a keyword but found {:?}", "Prefix parser expected a keyword but found {:?}",
t t
)), )),
} },
}
None => parser_err!(format!("Prefix parser expected a keyword but hit EOF")), None => parser_err!(format!("Prefix parser expected a keyword but hit EOF")),
} }
} }
@ -184,11 +175,17 @@ impl Parser {
pub fn parse_function(&mut self, id: &str) -> Result<ASTNode, ParserError> { pub fn parse_function(&mut self, id: &str) -> Result<ASTNode, ParserError> {
self.consume_token(&Token::LParen)?; self.consume_token(&Token::LParen)?;
if let Ok(true) = self.consume_token(&Token::RParen) { if let Ok(true) = self.consume_token(&Token::RParen) {
Ok(ASTNode::SQLFunction { id: id.to_string(), args: vec![] }) Ok(ASTNode::SQLFunction {
id: id.to_string(),
args: vec![],
})
} else { } else {
let args = self.parse_expr_list()?; let args = self.parse_expr_list()?;
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
Ok(ASTNode::SQLFunction { id: id.to_string(), args }) Ok(ASTNode::SQLFunction {
id: id.to_string(),
args,
})
} }
} }
@ -205,7 +202,6 @@ impl Parser {
}) })
} }
/// Parse a postgresql casting style which is in the form or expr::datatype /// Parse a postgresql casting style which is in the form or expr::datatype
pub fn parse_pg_cast(&mut self, expr: ASTNode) -> Result<ASTNode, ParserError> { pub fn parse_pg_cast(&mut self, expr: ASTNode) -> Result<ASTNode, ParserError> {
let ast = self.consume_token(&Token::DoubleColon)?; let ast = self.consume_token(&Token::DoubleColon)?;
@ -236,7 +232,8 @@ impl Parser {
debug!("parsing infix"); debug!("parsing infix");
match self.next_token() { match self.next_token() {
Some(tok) => match tok { Some(tok) => match tok {
Token::Keyword(ref k) => if k == "IS" { Token::Keyword(ref k) => {
if k == "IS" {
if self.parse_keywords(vec!["NULL"]) { if self.parse_keywords(vec!["NULL"]) {
Ok(Some(ASTNode::SQLIsNull(Box::new(expr)))) Ok(Some(ASTNode::SQLIsNull(Box::new(expr))))
} else if self.parse_keywords(vec!["NOT", "NULL"]) { } else if self.parse_keywords(vec!["NOT", "NULL"]) {
@ -250,7 +247,8 @@ impl Parser {
op: self.to_sql_operator(&tok)?, op: self.to_sql_operator(&tok)?,
right: Box::new(self.parse_expr(precedence)?), right: Box::new(self.parse_expr(precedence)?),
})) }))
}, }
}
Token::Eq Token::Eq
| Token::Neq | Token::Neq
| Token::Gt | Token::Gt
@ -266,10 +264,10 @@ impl Parser {
op: self.to_sql_operator(&tok)?, op: self.to_sql_operator(&tok)?,
right: Box::new(self.parse_expr(precedence)?), right: Box::new(self.parse_expr(precedence)?),
})), })),
| Token::DoubleColon => { Token::DoubleColon => {
let pg_cast = self.parse_pg_cast(expr)?; let pg_cast = self.parse_pg_cast(expr)?;
Ok(Some(pg_cast)) Ok(Some(pg_cast))
}, }
_ => parser_err!(format!("No infix parser for token {:?}", tok)), _ => parser_err!(format!("No infix parser for token {:?}", tok)),
}, },
None => Ok(None), None => Ok(None),
@ -327,7 +325,6 @@ impl Parser {
self.peek_token_skip_whitespace() self.peek_token_skip_whitespace()
} }
pub fn skip_whitespace(&mut self) -> Option<Token> { pub fn skip_whitespace(&mut self) -> Option<Token> {
loop { loop {
match self.next_token_no_skip() { match self.next_token_no_skip() {
@ -376,7 +373,6 @@ impl Parser {
} }
} }
/// Get the next token skipping whitespace and increment the token index /// Get the next token skipping whitespace and increment the token index
pub fn next_token(&mut self) -> Option<Token> { pub fn next_token(&mut self) -> Option<Token> {
self.skip_whitespace() self.skip_whitespace()
@ -401,7 +397,7 @@ impl Parser {
} }
token => { token => {
return token; return token;
}, }
} }
} }
} }
@ -454,20 +450,17 @@ impl Parser {
/// Consume the next token if it matches the expected token, otherwise return an error /// Consume the next token if it matches the expected token, otherwise return an error
pub fn consume_token(&mut self, expected: &Token) -> Result<bool, ParserError> { pub fn consume_token(&mut self, expected: &Token) -> Result<bool, ParserError> {
match self.peek_token() { match self.peek_token() {
Some(ref t) => if *t == *expected { Some(ref t) => {
if *t == *expected {
self.next_token(); self.next_token();
Ok(true) Ok(true)
} else { } else {
Ok(false) Ok(false)
},
other => parser_err!(format!(
"expected token {:?} but was {:?}",
expected,
other,
)),
} }
} }
other => parser_err!(format!("expected token {:?} but was {:?}", expected, other,)),
}
}
/// Parse a SQL CREATE statement /// Parse a SQL CREATE statement
pub fn parse_create(&mut self) -> Result<ASTNode, ParserError> { pub fn parse_create(&mut self) -> Result<ASTNode, ParserError> {
@ -527,16 +520,20 @@ impl Parser {
} }
} }
} else { } else {
return parser_err!( return parser_err!(format!(
format!("Error parsing data type in column definition near: {:?}", self.peek_token()) "Error parsing data type in column definition near: {:?}",
); self.peek_token()
));
} }
} else { } else {
return parser_err!("Error parsing column name"); return parser_err!("Error parsing column name");
} }
} }
} }
Ok(ASTNode::SQLCreateTable { name: table_name, columns }) Ok(ASTNode::SQLCreateTable {
name: table_name,
columns,
})
} else { } else {
parser_err!(format!( parser_err!(format!(
"Unexpected token after CREATE: {:?}", "Unexpected token after CREATE: {:?}",
@ -554,15 +551,13 @@ impl Parser {
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
let key = Key { let key = Key {
name: constraint_name.to_string(), name: constraint_name.to_string(),
columns: column_names columns: column_names,
}; };
if is_primary_key { if is_primary_key {
Ok(TableKey::PrimaryKey(key)) Ok(TableKey::PrimaryKey(key))
} } else if is_unique_key {
else if is_unique_key{
Ok(TableKey::UniqueKey(key)) Ok(TableKey::UniqueKey(key))
} } else if is_foreign_key {
else if is_foreign_key{
if self.parse_keyword("REFERENCES") { if self.parse_keyword("REFERENCES") {
let foreign_table = self.parse_tablename()?; let foreign_table = self.parse_tablename()?;
self.consume_token(&Token::LParen)?; self.consume_token(&Token::LParen)?;
@ -577,7 +572,10 @@ impl Parser {
parser_err!("Expecting references") parser_err!("Expecting references")
} }
} else { } else {
parser_err!(format!("Expecting primary key, unique key, or foreign key, found: {:?}", self.peek_token())) parser_err!(format!(
"Expecting primary key, unique key, or foreign key, found: {:?}",
self.peek_token()
))
} }
} }
@ -585,29 +583,38 @@ impl Parser {
if self.parse_keyword("TABLE") { if self.parse_keyword("TABLE") {
let is_only = self.parse_keyword("ONLY"); let is_only = self.parse_keyword("ONLY");
let table_name = self.parse_tablename()?; let table_name = self.parse_tablename()?;
let operation:Result<AlterOperation,ParserError> = if self.parse_keywords(vec!["ADD", "CONSTRAINT"]){ let operation: Result<AlterOperation, ParserError> =
if self.parse_keywords(vec!["ADD", "CONSTRAINT"]) {
match self.next_token() { match self.next_token() {
Some(Token::Identifier(ref id)) => { Some(Token::Identifier(ref id)) => {
let table_key = self.parse_table_key(id)?; let table_key = self.parse_table_key(id)?;
Ok(AlterOperation::AddConstraint(table_key)) Ok(AlterOperation::AddConstraint(table_key))
} }
_ => { _ => {
return parser_err!(format!("Expecting identifier, found : {:?}", self.peek_token())); return parser_err!(format!(
"Expecting identifier, found : {:?}",
self.peek_token()
));
} }
} }
} else { } else {
return parser_err!(format!("Expecting ADD CONSTRAINT, found :{:?}", self.peek_token())); return parser_err!(format!(
"Expecting ADD CONSTRAINT, found :{:?}",
self.peek_token()
));
}; };
Ok(ASTNode::SQLAlterTable { Ok(ASTNode::SQLAlterTable {
name: table_name, name: table_name,
operation: operation?, operation: operation?,
}) })
} else { } else {
parser_err!(format!("Expecting TABLE after ALTER, found {:?}", self.peek_token())) parser_err!(format!(
"Expecting TABLE after ALTER, found {:?}",
self.peek_token()
))
} }
} }
/// Parse a copy statement /// Parse a copy statement
pub fn parse_copy(&mut self) -> Result<ASTNode, ParserError> { pub fn parse_copy(&mut self) -> Result<ASTNode, ParserError> {
let table_name = self.parse_tablename()?; let table_name = self.parse_tablename()?;
@ -622,7 +629,11 @@ impl Parser {
self.parse_keyword("STDIN"); self.parse_keyword("STDIN");
self.consume_token(&Token::SemiColon); self.consume_token(&Token::SemiColon);
let values = self.parse_tsv()?; let values = self.parse_tsv()?;
Ok(ASTNode::SQLCopy{table_name, columns, values}) Ok(ASTNode::SQLCopy {
table_name,
columns,
values,
})
} }
/// Parse a tab separated values in /// Parse a tab separated values in
@ -630,7 +641,6 @@ impl Parser {
fn parse_tsv(&mut self) -> Result<Vec<Value>, ParserError> { fn parse_tsv(&mut self) -> Result<Vec<Value>, ParserError> {
let values = self.parse_tab_value()?; let values = self.parse_tab_value()?;
Ok(values) Ok(values)
} }
fn parse_sql_value(&mut self) -> Result<ASTNode, ParserError> { fn parse_sql_value(&mut self) -> Result<ASTNode, ParserError> {
@ -653,7 +663,8 @@ impl Parser {
Token::Backslash => { Token::Backslash => {
if let Ok(true) = self.consume_token(&Token::Period) { if let Ok(true) = self.consume_token(&Token::Period) {
return Ok(values); return Ok(values);
}if let Some(token) = self.next_token(){ }
if let Some(token) = self.next_token() {
if token == Token::Identifier("N".to_string()) { if token == Token::Identifier("N".to_string()) {
values.push(Value::Null); values.push(Value::Null);
} }
@ -707,8 +718,12 @@ impl Parser {
}, },
Token::Identifier(id) => Ok(Value::String(id.to_string())), Token::Identifier(id) => Ok(Value::String(id.to_string())),
Token::String(ref s) => Ok(Value::String(s.to_string())), Token::String(ref s) => Ok(Value::String(s.to_string())),
Token::SingleQuotedString(ref s) => Ok(Value::SingleQuotedString(s.to_string())), Token::SingleQuotedString(ref s) => {
Token::DoubleQuotedString(ref s) => Ok(Value::DoubleQuotedString(s.to_string())), Ok(Value::SingleQuotedString(s.to_string()))
}
Token::DoubleQuotedString(ref s) => {
Ok(Value::DoubleQuotedString(s.to_string()))
}
other => parser_err!(format!("Unsupported value: {:?}", self.peek_token())), other => parser_err!(format!("Unsupported value: {:?}", self.peek_token())),
} }
} }
@ -744,7 +759,6 @@ impl Parser {
} }
} }
pub fn parse_timezone_offset(&mut self) -> Result<i8, ParserError> { pub fn parse_timezone_offset(&mut self) -> Result<i8, ParserError> {
match self.next_token() { match self.next_token() {
Some(Token::Plus) => { Some(Token::Plus) => {
@ -755,11 +769,13 @@ impl Parser {
let n = self.parse_literal_int()?; let n = self.parse_literal_int()?;
Ok(-n as i8) Ok(-n as i8)
} }
other => parser_err!(format!("Expecting `+` or `-` in timezone, but found {:?}", other)), other => parser_err!(format!(
"Expecting `+` or `-` in timezone, but found {:?}",
other
)),
} }
} }
pub fn parse_timestamp_value(&mut self) -> Result<Value, ParserError> { pub fn parse_timestamp_value(&mut self) -> Result<Value, ParserError> {
let year = self.parse_literal_int()?; let year = self.parse_literal_int()?;
let date = self.parse_date(year)?; let date = self.parse_date(year)?;
@ -777,7 +793,10 @@ impl Parser {
_ => Ok(Value::DateTime(date_time)), _ => Ok(Value::DateTime(date_time)),
} }
} else { } else {
parser_err!(format!("Expecting time after date, but found {:?}", self.peek_token())) parser_err!(format!(
"Expecting time after date, but found {:?}",
self.peek_token()
))
} }
} }
@ -789,12 +808,17 @@ impl Parser {
let date = NaiveDate::from_ymd(year as i32, month as u32, day as u32); let date = NaiveDate::from_ymd(year as i32, month as u32, day as u32);
Ok(date) Ok(date)
} else { } else {
parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token())) parser_err!(format!(
"Expecting `-` for date separator, found {:?}",
self.peek_token()
))
} }
} else { } else {
parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token())) parser_err!(format!(
"Expecting `-` for date separator, found {:?}",
self.peek_token()
))
} }
} }
pub fn parse_time(&mut self) -> Result<NaiveTime, ParserError> { pub fn parse_time(&mut self) -> Result<NaiveTime, ParserError> {
@ -806,7 +830,12 @@ impl Parser {
let ms = (sec.fract() * 1000.0).round(); let ms = (sec.fract() * 1000.0).round();
if let Ok(true) = self.consume_token(&Token::Period) { if let Ok(true) = self.consume_token(&Token::Period) {
let ms = self.parse_literal_int()?; let ms = self.parse_literal_int()?;
Ok(NaiveTime::from_hms_milli(hour as u32, min as u32, sec as u32, ms as u32)) Ok(NaiveTime::from_hms_milli(
hour as u32,
min as u32,
sec as u32,
ms as u32,
))
} else { } else {
Ok(NaiveTime::from_hms(hour as u32, min as u32, sec as u32)) Ok(NaiveTime::from_hms(hour as u32, min as u32, sec as u32))
} }
@ -819,11 +848,13 @@ impl Parser {
"BOOLEAN" => Ok(SQLType::Boolean), "BOOLEAN" => Ok(SQLType::Boolean),
"FLOAT" => Ok(SQLType::Float(self.parse_optional_precision()?)), "FLOAT" => Ok(SQLType::Float(self.parse_optional_precision()?)),
"REAL" => Ok(SQLType::Real), "REAL" => Ok(SQLType::Real),
"DOUBLE" => if self.parse_keyword("PRECISION"){ "DOUBLE" => {
if self.parse_keyword("PRECISION") {
Ok(SQLType::Double) Ok(SQLType::Double)
} else { } else {
Ok(SQLType::Double) Ok(SQLType::Double)
} }
}
"SMALLINT" => Ok(SQLType::SmallInt), "SMALLINT" => Ok(SQLType::SmallInt),
"INT" | "INTEGER" => Ok(SQLType::Int), "INT" | "INTEGER" => Ok(SQLType::Int),
"BIGINT" => Ok(SQLType::BigInt), "BIGINT" => Ok(SQLType::BigInt),
@ -837,36 +868,52 @@ impl Parser {
} }
"UUID" => Ok(SQLType::Uuid), "UUID" => Ok(SQLType::Uuid),
"DATE" => Ok(SQLType::Date), "DATE" => Ok(SQLType::Date),
"TIMESTAMP" => if self.parse_keyword("WITH"){ "TIMESTAMP" => {
if self.parse_keyword("WITH") {
if self.parse_keywords(vec!["TIME", "ZONE"]) { if self.parse_keywords(vec!["TIME", "ZONE"]) {
Ok(SQLType::Timestamp) Ok(SQLType::Timestamp)
} else { } else {
parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) parser_err!(format!(
"Expecting 'time zone', found: {:?}",
self.peek_token()
))
} }
} else if self.parse_keyword("WITHOUT") { } else if self.parse_keyword("WITHOUT") {
if self.parse_keywords(vec!["TIME", "ZONE"]) { if self.parse_keywords(vec!["TIME", "ZONE"]) {
Ok(SQLType::Timestamp) Ok(SQLType::Timestamp)
} else { } else {
parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) parser_err!(format!(
"Expecting 'time zone', found: {:?}",
self.peek_token()
))
} }
} else { } else {
Ok(SQLType::Timestamp) Ok(SQLType::Timestamp)
} }
"TIME" => if self.parse_keyword("WITH"){ }
"TIME" => {
if self.parse_keyword("WITH") {
if self.parse_keywords(vec!["TIME", "ZONE"]) { if self.parse_keywords(vec!["TIME", "ZONE"]) {
Ok(SQLType::Time) Ok(SQLType::Time)
} else { } else {
parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) parser_err!(format!(
"Expecting 'time zone', found: {:?}",
self.peek_token()
))
} }
} else if self.parse_keyword("WITHOUT") { } else if self.parse_keyword("WITHOUT") {
if self.parse_keywords(vec!["TIME", "ZONE"]) { if self.parse_keywords(vec!["TIME", "ZONE"]) {
Ok(SQLType::Time) Ok(SQLType::Time)
} else { } else {
parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) parser_err!(format!(
"Expecting 'time zone', found: {:?}",
self.peek_token()
))
} }
} else { } else {
Ok(SQLType::Timestamp) Ok(SQLType::Timestamp)
} }
}
"REGCLASS" => Ok(SQLType::Regclass), "REGCLASS" => Ok(SQLType::Regclass),
"TEXT" => { "TEXT" => {
if let Ok(true) = self.consume_token(&Token::LBracket) { if let Ok(true) = self.consume_token(&Token::LBracket) {
@ -895,7 +942,6 @@ impl Parser {
} }
} }
pub fn parse_compound_identifier(&mut self, separator: &Token) -> Result<ASTNode, ParserError> { pub fn parse_compound_identifier(&mut self, separator: &Token) -> Result<ASTNode, ParserError> {
let mut idents = vec![]; let mut idents = vec![];
let mut expect_identifier = true; let mut expect_identifier = true;
@ -903,13 +949,15 @@ impl Parser {
let token = &self.next_token(); let token = &self.next_token();
match token { match token {
Some(token) => match token { Some(token) => match token {
Token::Identifier(s) => if expect_identifier{ Token::Identifier(s) => {
if expect_identifier {
expect_identifier = false; expect_identifier = false;
idents.push(s.to_string()); idents.push(s.to_string());
} else { } else {
self.prev_token(); self.prev_token();
break; break;
} }
}
token if token == separator => { token if token == separator => {
if expect_identifier { if expect_identifier {
return parser_err!(format!("Expecting identifier, found {:?}", token)); return parser_err!(format!("Expecting identifier, found {:?}", token));
@ -922,7 +970,7 @@ impl Parser {
self.prev_token(); self.prev_token();
break; break;
} }
} },
None => { None => {
self.prev_token(); self.prev_token();
break; break;
@ -964,7 +1012,9 @@ impl Parser {
} }
} }
pub fn parse_optional_precision_scale(&mut self) -> Result<(usize, Option<usize>), ParserError> { pub fn parse_optional_precision_scale(
&mut self,
) -> Result<(usize, Option<usize>), ParserError> {
if self.consume_token(&Token::LParen)? { if self.consume_token(&Token::LParen)? {
let n = self.parse_literal_int()?; let n = self.parse_literal_int()?;
let scale = if let Ok(true) = self.consume_token(&Token::Comma) { let scale = if let Ok(true) = self.consume_token(&Token::Comma) {
@ -1081,7 +1131,11 @@ impl Parser {
self.consume_token(&Token::LParen)?; self.consume_token(&Token::LParen)?;
let values = self.parse_expr_list()?; let values = self.parse_expr_list()?;
self.consume_token(&Token::RParen)?; self.consume_token(&Token::RParen)?;
Ok(ASTNode::SQLInsert{table_name, columns, values: vec![values]}) Ok(ASTNode::SQLInsert {
table_name,
columns,
values: vec![values],
})
} }
/// Parse a comma-delimited list of SQL expressions /// Parse a comma-delimited list of SQL expressions
@ -1157,4 +1211,3 @@ impl Parser {
} }
} }
} }

View file

@ -134,7 +134,7 @@ impl ToString for Token{
pub enum Whitespace { pub enum Whitespace {
Space, Space,
Newline, Newline,
Tab Tab,
} }
impl ToString for Whitespace { impl ToString for Whitespace {
@ -349,9 +349,7 @@ impl<'a> Tokenizer<'a> {
match chars.peek() { match chars.peek() {
Some(&ch) => match ch { Some(&ch) => match ch {
// double colon // double colon
':' => { ':' => self.consume_and_return(chars, Token::DoubleColon),
self.consume_and_return(chars, Token::DoubleColon)
}
_ => Ok(Some(Token::Colon)), _ => Ok(Some(Token::Colon)),
}, },
None => Ok(Some(Token::Colon)), None => Ok(Some(Token::Colon)),
@ -365,13 +363,17 @@ impl<'a> Tokenizer<'a> {
'&' => self.consume_and_return(chars, Token::Ampersand), '&' => self.consume_and_return(chars, Token::Ampersand),
'{' => self.consume_and_return(chars, Token::LBrace), '{' => self.consume_and_return(chars, Token::LBrace),
'}' => self.consume_and_return(chars, Token::RBrace), '}' => self.consume_and_return(chars, Token::RBrace),
other => self.consume_and_return(chars, Token::Char(other)) other => self.consume_and_return(chars, Token::Char(other)),
}, },
None => Ok(None), None => Ok(None),
} }
} }
fn consume_and_return(&self, chars: &mut Peekable<Chars>, t: Token) -> Result<Option<Token>, TokenizerError> { fn consume_and_return(
&self,
chars: &mut Peekable<Chars>,
t: Token,
) -> Result<Option<Token>, TokenizerError> {
chars.next(); chars.next();
Ok(Some(t)) Ok(Some(t))
} }
@ -492,10 +494,9 @@ mod tests {
Token::Char('ط'), Token::Char('ط'),
Token::Char('ف'), Token::Char('ف'),
Token::Char('ى'), Token::Char('ى'),
Token::Identifier("h".to_string()) Token::Identifier("h".to_string()),
]; ];
compare(expected, tokens); compare(expected, tokens);
} }
#[test] #[test]

View file

@ -1,10 +1,10 @@
extern crate log; extern crate log;
extern crate sqlparser; extern crate sqlparser;
use sqlparser::sqlast::*;
use sqlparser::sqltokenizer::*;
use sqlparser::sqlparser::*;
use sqlparser::dialect::PostgreSqlDialect; use sqlparser::dialect::PostgreSqlDialect;
use sqlparser::sqlast::*;
use sqlparser::sqlparser::*;
use sqlparser::sqltokenizer::*;
use log::*; use log::*;
@ -14,9 +14,18 @@ fn test_prev_index(){
let mut parser = parser(sql); let mut parser = parser(sql);
assert_eq!(parser.prev_token(), None); assert_eq!(parser.prev_token(), None);
assert_eq!(parser.next_token(), Some(Token::Keyword("SELECT".into()))); assert_eq!(parser.next_token(), Some(Token::Keyword("SELECT".into())));
assert_eq!(parser.next_token(), Some(Token::Identifier("version".into()))); assert_eq!(
assert_eq!(parser.prev_token(), Some(Token::Identifier("version".into()))); parser.next_token(),
assert_eq!(parser.peek_token(), Some(Token::Identifier("version".into()))); Some(Token::Identifier("version".into()))
);
assert_eq!(
parser.prev_token(),
Some(Token::Identifier("version".into()))
);
assert_eq!(
parser.peek_token(),
Some(Token::Identifier("version".into()))
);
assert_eq!(parser.prev_token(), Some(Token::Keyword("SELECT".into()))); assert_eq!(parser.prev_token(), Some(Token::Keyword("SELECT".into())));
assert_eq!(parser.prev_token(), None); assert_eq!(parser.prev_token(), None);
} }
@ -30,7 +39,9 @@ fn parse_delete_statement() {
match parse_sql(&sql) { match parse_sql(&sql) {
ASTNode::SQLDelete { relation, .. } => { ASTNode::SQLDelete { relation, .. } => {
assert_eq!( assert_eq!(
Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString("table".to_string())))), Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString(
"table".to_string()
)))),
relation relation
); );
} }
@ -56,7 +67,9 @@ fn parse_where_delete_statement() {
.. ..
} => { } => {
assert_eq!( assert_eq!(
Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString("table".to_string())))), Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString(
"table".to_string()
)))),
relation relation
); );
@ -97,11 +110,21 @@ fn parse_simple_insert() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLInsert { ASTNode::SQLInsert {
table_name, columns, values, .. table_name,
columns,
values,
..
} => { } => {
assert_eq!(table_name, "customer"); assert_eq!(table_name, "customer");
assert!(columns.is_empty()); assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLValue(Value::Long(1)),ASTNode::SQLValue(Value::Long(2)),ASTNode::SQLValue(Value::Long(3))]], values); assert_eq!(
vec![vec![
ASTNode::SQLValue(Value::Long(1)),
ASTNode::SQLValue(Value::Long(2)),
ASTNode::SQLValue(Value::Long(3))
]],
values
);
} }
_ => assert!(false), _ => assert!(false),
} }
@ -114,11 +137,21 @@ fn parse_common_insert() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLInsert { ASTNode::SQLInsert {
table_name, columns, values, .. table_name,
columns,
values,
..
} => { } => {
assert_eq!(table_name, "public.customer"); assert_eq!(table_name, "public.customer");
assert!(columns.is_empty()); assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLValue(Value::Long(1)),ASTNode::SQLValue(Value::Long(2)),ASTNode::SQLValue(Value::Long(3))]], values); assert_eq!(
vec![vec![
ASTNode::SQLValue(Value::Long(1)),
ASTNode::SQLValue(Value::Long(2)),
ASTNode::SQLValue(Value::Long(3))
]],
values
);
} }
_ => assert!(false), _ => assert!(false),
} }
@ -131,11 +164,21 @@ fn parse_complex_insert() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLInsert { ASTNode::SQLInsert {
table_name, columns, values, .. table_name,
columns,
values,
..
} => { } => {
assert_eq!(table_name, "db.public.customer"); assert_eq!(table_name, "db.public.customer");
assert!(columns.is_empty()); assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLValue(Value::Long(1)),ASTNode::SQLValue(Value::Long(2)),ASTNode::SQLValue(Value::Long(3))]], values); assert_eq!(
vec![vec![
ASTNode::SQLValue(Value::Long(1)),
ASTNode::SQLValue(Value::Long(2)),
ASTNode::SQLValue(Value::Long(3))
]],
values
);
} }
_ => assert!(false), _ => assert!(false),
} }
@ -155,11 +198,24 @@ fn parse_insert_with_columns() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLInsert { ASTNode::SQLInsert {
table_name, columns, values, .. table_name,
columns,
values,
..
} => { } => {
assert_eq!(table_name, "public.customer"); assert_eq!(table_name, "public.customer");
assert_eq!(columns, vec!["id".to_string(), "name".to_string(), "active".to_string()]); assert_eq!(
assert_eq!(vec![vec![ASTNode::SQLValue(Value::Long(1)),ASTNode::SQLValue(Value::Long(2)),ASTNode::SQLValue(Value::Long(3))]], values); columns,
vec!["id".to_string(), "name".to_string(), "active".to_string()]
);
assert_eq!(
vec![vec![
ASTNode::SQLValue(Value::Long(1)),
ASTNode::SQLValue(Value::Long(2)),
ASTNode::SQLValue(Value::Long(3))
]],
values
);
} }
_ => assert!(false), _ => assert!(false),
} }
@ -412,7 +468,8 @@ fn parse_create_table_with_defaults() {
activebool boolean DEFAULT true NOT NULL, activebool boolean DEFAULT true NOT NULL,
create_date date DEFAULT now()::text NOT NULL, create_date date DEFAULT now()::text NOT NULL,
last_update timestamp without time zone DEFAULT now() NOT NULL, last_update timestamp without time zone DEFAULT now() NOT NULL,
active integer NOT NULL)"); active integer NOT NULL)",
);
let ast = parse_sql(&sql); let ast = parse_sql(&sql);
match ast { match ast {
ASTNode::SQLCreateTable { name, columns } => { ASTNode::SQLCreateTable { name, columns } => {
@ -481,13 +538,15 @@ fn parse_create_table_from_pg_dump() {
#[test] #[test]
fn parse_create_table_with_inherit() { fn parse_create_table_with_inherit() {
let sql = String::from("\ let sql = String::from(
"\
CREATE TABLE bazaar.settings (\ CREATE TABLE bazaar.settings (\
settings_id uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, \ settings_id uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, \
user_id uuid UNIQUE, \ user_id uuid UNIQUE, \
value text[], \ value text[], \
use_metric boolean DEFAULT true\ use_metric boolean DEFAULT true\
)"); )",
);
let ast = parse_sql(&sql); let ast = parse_sql(&sql);
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
@ -514,9 +573,11 @@ fn parse_create_table_with_inherit() {
#[test] #[test]
fn parse_alter_table_constraint_primary_key() { fn parse_alter_table_constraint_primary_key() {
let sql = String::from("\ let sql = String::from(
"\
ALTER TABLE bazaar.address \ ALTER TABLE bazaar.address \
ADD CONSTRAINT address_pkey PRIMARY KEY (address_id)"); ADD CONSTRAINT address_pkey PRIMARY KEY (address_id)",
);
let ast = parse_sql(&sql); let ast = parse_sql(&sql);
println!("ast: {:?}", ast); println!("ast: {:?}", ast);
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
@ -623,7 +684,10 @@ fn parse_literal_string() {
assert_eq!(sql, ast.to_string()); assert_eq!(sql, ast.to_string());
match ast { match ast {
ASTNode::SQLSelect { ref projection, .. } => { ASTNode::SQLSelect { ref projection, .. } => {
assert_eq!(projection[0], ASTNode::SQLValue(Value::SingleQuotedString("one".to_string()))); assert_eq!(
projection[0],
ASTNode::SQLValue(Value::SingleQuotedString("one".to_string()))
);
} }
_ => panic!(), _ => panic!(),
} }
@ -666,4 +730,3 @@ fn parser(sql: &str) -> Parser {
debug!("tokens: {:#?}", tokens); debug!("tokens: {:#?}", tokens);
Parser::new(tokens) Parser::new(tokens)
} }