From 722ea7a91b60b9f9f8a991819f0a1a8efd1d3bfb Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Sat, 6 Oct 2018 09:39:26 -0600 Subject: [PATCH] ran cargo fmt --- src/dialect.rs | 2 - src/dialect/generic_sql.rs | 68 +---- src/dialect/postgresql.rs | 84 +----- src/sqlast.rs | 220 +++++++++----- src/sqlast/sql_operator.rs | 7 +- src/sqlast/sqltype.rs | 50 ++-- src/sqlast/table_key.rs | 47 +-- src/sqlast/value.rs | 22 +- src/sqlparser.rs | 581 ++++++++++++++++++++----------------- src/sqltokenizer.rs | 51 ++-- tests/sqlparser.rs | 145 ++++++--- 11 files changed, 662 insertions(+), 615 deletions(-) diff --git a/src/dialect.rs b/src/dialect.rs index 81fc638f..e4296d17 100644 --- a/src/dialect.rs +++ b/src/dialect.rs @@ -32,5 +32,3 @@ pub trait Dialect { /// Determine if a character is a valid identifier character fn is_identifier_part(&self, ch: char) -> bool; } - - diff --git a/src/dialect/generic_sql.rs b/src/dialect/generic_sql.rs index 09cb22fc..5f38c93c 100644 --- a/src/dialect/generic_sql.rs +++ b/src/dialect/generic_sql.rs @@ -6,67 +6,12 @@ pub struct GenericSqlDialect {} impl Dialect for GenericSqlDialect { fn keywords(&self) -> Vec<&'static str> { return vec![ - SELECT, - FROM, - WHERE, - LIMIT, - ORDER, - GROUP, - 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, + SELECT, FROM, WHERE, LIMIT, ORDER, GROUP, 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 == '_' } } - diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index 0238561f..f5afd3f3 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -7,83 +7,13 @@ pub struct PostgreSqlDialect {} impl Dialect for PostgreSqlDialect { fn keywords(&self) -> Vec<&'static str> { return vec![ - ALTER, - ONLY, - SELECT, - FROM, - WHERE, - LIMIT, - ORDER, - 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, + ALTER, ONLY, SELECT, FROM, WHERE, LIMIT, ORDER, 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, ]; } diff --git a/src/sqlast.rs b/src/sqlast.rs index fb5007b8..cfdfe2ec 100644 --- a/src/sqlast.rs +++ b/src/sqlast.rs @@ -14,31 +14,22 @@ //! SQL Abstract Syntax Tree (AST) types //! -use chrono::{NaiveDate, - NaiveDateTime, - NaiveTime, - offset::{FixedOffset, - TimeZone, - }, - DateTime, - Utc, - }; - -use uuid::Uuid; -pub use self::value::Value; -pub use self::sqltype::SQLType; -pub use self::table_key::{ - AlterOperation, - TableKey, - Key +use chrono::{ + offset::{FixedOffset, TimeZone}, + DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc, }; +pub use self::sqltype::SQLType; +pub use self::table_key::{AlterOperation, Key, TableKey}; +pub use self::value::Value; +use uuid::Uuid; + pub use self::sql_operator::SQLOperator; -mod value; +mod sql_operator; mod sqltype; mod table_key; -mod sql_operator; +mod value; /// SQL Abstract Syntax Tree (AST) #[derive(Debug, Clone, PartialEq)] @@ -103,7 +94,7 @@ pub enum ASTNode { /// VALUES (vector of rows to insert) values: Vec>, }, - SQLCopy{ + SQLCopy { /// TABLE table_name: String, /// COLUMNS @@ -139,33 +130,43 @@ pub enum ASTNode { /// Table name name: String, operation: AlterOperation, - } + }, } - -impl ToString for ASTNode{ - +impl ToString for ASTNode { fn to_string(&self) -> String { - match self{ + match self { 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::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::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, rex } => { + ASTNode::SQLUnary { operator, rex } => { format!("{} {}", operator.to_string(), rex.as_ref().to_string()) } ASTNode::SQLValue(v) => v.to_string(), - ASTNode::SQLFunction{id, args} => format!("{}({})", id, args.iter().map(|a|a.to_string()).collect::>().join(", ")), - ASTNode::SQLSelect{ + ASTNode::SQLFunction { id, args } => format!( + "{}({})", + id, + args.iter() + .map(|a| a.to_string()) + .collect::>() + .join(", ") + ), + ASTNode::SQLSelect { projection, relation, selection, @@ -174,82 +175,147 @@ impl ToString for ASTNode{ having, limit, } => { - let mut s = format!("SELECT {}", projection.iter().map(|p|p.to_string()).collect::>().join(", ")); - if let Some(relation) = relation{ + let mut s = format!( + "SELECT {}", + projection + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", ") + ); + if let Some(relation) = relation { s += &format!(" FROM {}", relation.as_ref().to_string()); } - if let Some(selection) = selection{ + 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::>().join(", ")); + s += &format!( + " GROUP BY {}", + group_by + .iter() + .map(|g| g.to_string()) + .collect::>() + .join(", ") + ); } - if let Some(having) = having{ + 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::>().join(", ")); + if let Some(order_by) = order_by { + s += &format!( + " ORDER BY {}", + order_by + .iter() + .map(|o| o.to_string()) + .collect::>() + .join(", ") + ); } if let Some(limit) = limit { s += &format!(" LIMIT {}", limit.as_ref().to_string()); } s } - ASTNode::SQLInsert{ table_name, columns, values } => { + 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::>().join(", ") - ).collect::>().join(", ") - ); + s += &format!( + " VALUES({})", + values + .iter() + .map(|row| row + .iter() + .map(|c| c.to_string()) + .collect::>() + .join(", ")) + .collect::>() + .join(", ") + ); } s } - ASTNode::SQLCopy{table_name, columns, values} => { + 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::>().join(", ")); + s += &format!( + " ({})", + columns + .iter() + .map(|c| c.to_string()) + .collect::>() + .join(", ") + ); } s += " FROM stdin; "; if values.len() > 0 { - s += &format!("\n{}", - values.iter() - .map(|v|v.to_string()) - .collect::>().join("\t") - ); + s += &format!( + "\n{}", + values + .iter() + .map(|v| v.to_string()) + .collect::>() + .join("\t") + ); } s += "\n\\."; s } - ASTNode::SQLUpdate{table_name, assignments, selection} => { + 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::>().join(", ")); + s += &format!( + "{}", + assignments + .iter() + .map(|ass| ass.to_string()) + .collect::>() + .join(", ") + ); } - if let Some(selection) = selection{ + if let Some(selection) = selection { s += &format!(" WHERE {}", selection.as_ref().to_string()); } s } - ASTNode::SQLDelete{relation, selection} => { + ASTNode::SQLDelete { + relation, + selection, + } => { let mut s = String::from("DELETE"); - if let Some(relation) = relation{ + if let Some(relation) = relation { s += &format!(" FROM {}", relation.as_ref().to_string()); } - if let Some(selection) = selection{ + 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::>().join(", ")) - } - ASTNode::SQLAlterTable{name, operation} => { + ASTNode::SQLCreateTable { name, columns } => format!( + "CREATE TABLE {} ({})", + name, + columns + .iter() + .map(|c| c.to_string()) + .collect::>() + .join(", ") + ), + ASTNode::SQLAlterTable { name, operation } => { format!("ALTER TABLE {} {}", name, operation.to_string()) } } @@ -264,8 +330,7 @@ pub struct SQLAssignment { value: Box, } -impl ToString for SQLAssignment{ - +impl ToString for SQLAssignment { fn to_string(&self) -> String { format!("SET {} = {}", self.id, self.value.as_ref().to_string()) } @@ -284,11 +349,11 @@ impl SQLOrderByExpr { } } -impl ToString for SQLOrderByExpr{ +impl ToString for SQLOrderByExpr { fn to_string(&self) -> String { - if self.asc{ + if self.asc { format!("{} ASC", self.expr.as_ref().to_string()) - }else{ + } else { format!("{} DESC", self.expr.as_ref().to_string()) } } @@ -305,24 +370,21 @@ pub struct SQLColumnDef { pub allow_null: bool, } - -impl ToString for SQLColumnDef{ - +impl ToString for SQLColumnDef { fn to_string(&self) -> String { let mut s = format!("{} {}", self.name, self.data_type.to_string()); - if self.is_primary{ + if self.is_primary { s += " PRIMARY KEY"; } - if self.is_unique{ + if self.is_unique { s += " UNIQUE"; } - if let Some(ref default) = self.default{ + if let Some(ref default) = self.default { s += &format!(" DEFAULT {}", default.as_ref().to_string()); } - if !self.allow_null{ + if !self.allow_null { s += " NOT NULL"; } s } } - diff --git a/src/sqlast/sql_operator.rs b/src/sqlast/sql_operator.rs index bf054801..19e9d04e 100644 --- a/src/sqlast/sql_operator.rs +++ b/src/sqlast/sql_operator.rs @@ -1,4 +1,3 @@ - /// SQL Operator #[derive(Debug, PartialEq, Clone)] pub enum SQLOperator { @@ -17,10 +16,9 @@ pub enum SQLOperator { Or, } -impl ToString for SQLOperator{ - +impl ToString for SQLOperator { fn to_string(&self) -> String { - match self{ + match self { SQLOperator::Plus => "+".to_string(), SQLOperator::Minus => "-".to_string(), SQLOperator::Multiply => "*".to_string(), @@ -37,4 +35,3 @@ impl ToString for SQLOperator{ } } } - diff --git a/src/sqlast/sqltype.rs b/src/sqlast/sqltype.rs index c35edeaa..c81313ab 100644 --- a/src/sqlast/sqltype.rs +++ b/src/sqlast/sqltype.rs @@ -1,4 +1,3 @@ - /// SQL datatypes for literals in SQL statements #[derive(Debug, Clone, PartialEq)] pub enum SQLType { @@ -50,38 +49,45 @@ pub enum SQLType { Array(Box), } -impl ToString for SQLType{ - +impl ToString for SQLType { fn to_string(&self) -> String { match self { - SQLType::Char(size) => if let Some(size) = size { - format!("char({})", size) - }else{ - "char".to_string() + SQLType::Char(size) => { + if let Some(size) = size { + format!("char({})", size) + } else { + "char".to_string() + } } - SQLType::Varchar(size) => if let Some(size) = size{ - format!("character varying({})", size) - }else{ - "character varying".to_string() + SQLType::Varchar(size) => { + if let Some(size) = size { + format!("character varying({})", size) + } else { + "character varying".to_string() + } } SQLType::Uuid => "uuid".to_string(), SQLType::Clob(size) => format!("clob({})", size), SQLType::Binary(size) => format!("binary({})", size), SQLType::Varbinary(size) => format!("varbinary({})", size), SQLType::Blob(size) => format!("blob({})", size), - SQLType::Decimal(precision, scale) => if let Some(scale) = scale{ - format!("numeric({},{})", precision, scale) - }else{ - format!("numeric({})", precision) - }, + SQLType::Decimal(precision, scale) => { + if let Some(scale) = scale { + format!("numeric({},{})", precision, scale) + } else { + format!("numeric({})", precision) + } + } SQLType::SmallInt => "smallint".to_string(), SQLType::Int => "int".to_string(), SQLType::BigInt => "bigint".to_string(), - SQLType::Float(size) => if let Some(size) = size{ - format!("float({})", size) - }else{ - "float".to_string() - }, + SQLType::Float(size) => { + if let Some(size) = size { + format!("float({})", size) + } else { + "float".to_string() + } + } SQLType::Real => "real".to_string(), SQLType::Double => "double".to_string(), SQLType::Boolean => "boolean".to_string(), @@ -91,7 +97,7 @@ impl ToString for SQLType{ SQLType::Regclass => "regclass".to_string(), SQLType::Text => "text".to_string(), SQLType::Bytea => "bytea".to_string(), - SQLType::Array(ty) => format!("{}[]",ty.to_string()), + SQLType::Array(ty) => format!("{}[]", ty.to_string()), SQLType::Custom(ty) => ty.to_string(), } } diff --git a/src/sqlast/table_key.rs b/src/sqlast/table_key.rs index 894d7913..9dacc21b 100644 --- a/src/sqlast/table_key.rs +++ b/src/sqlast/table_key.rs @@ -1,31 +1,28 @@ - #[derive(Debug, PartialEq, Clone)] -pub enum AlterOperation{ +pub enum AlterOperation { AddConstraint(TableKey), - RemoveConstraint{ - name: String, - } + RemoveConstraint { name: String }, } impl ToString for AlterOperation { - fn to_string(&self) -> String { - match self{ - AlterOperation::AddConstraint(table_key) => format!("ADD CONSTRAINT {}", table_key.to_string()), - AlterOperation::RemoveConstraint{name} => format!("REMOVE CONSTRAINT {}", name), + match self { + AlterOperation::AddConstraint(table_key) => { + format!("ADD CONSTRAINT {}", table_key.to_string()) + } + AlterOperation::RemoveConstraint { name } => format!("REMOVE CONSTRAINT {}", name), } } } - #[derive(Debug, PartialEq, Clone)] -pub struct Key{ +pub struct Key { pub name: String, pub columns: Vec, } #[derive(Debug, PartialEq, Clone)] -pub enum TableKey{ +pub enum TableKey { PrimaryKey(Key), UniqueKey(Key), Key(Key), @@ -33,26 +30,30 @@ pub enum TableKey{ key: Key, foreign_table: String, referred_columns: Vec, - } + }, } - -impl ToString for TableKey{ - +impl ToString for TableKey { fn to_string(&self) -> String { - match self{ + match self { TableKey::PrimaryKey(ref key) => { format!("{} PRIMARY KEY ({})", key.name, key.columns.join(", ")) } TableKey::UniqueKey(ref key) => { format!("{} UNIQUE KEY ({})", key.name, key.columns.join(", ")) } - TableKey::Key(ref key) => { - format!("{} KEY ({})", key.name, key.columns.join(", ")) - } - TableKey::ForeignKey{key, foreign_table, referred_columns} => { - format!("{} FOREIGN KEY ({}) REFERENCES {}({})", key.name, key.columns.join(", "), foreign_table, referred_columns.join(", ")) - } + TableKey::Key(ref key) => format!("{} KEY ({})", key.name, key.columns.join(", ")), + TableKey::ForeignKey { + key, + foreign_table, + referred_columns, + } => format!( + "{} FOREIGN KEY ({}) REFERENCES {}({})", + key.name, + key.columns.join(", "), + foreign_table, + referred_columns.join(", ") + ), } } } diff --git a/src/sqlast/value.rs b/src/sqlast/value.rs index 9c3e0442..d438879e 100644 --- a/src/sqlast/value.rs +++ b/src/sqlast/value.rs @@ -1,19 +1,13 @@ - -use chrono::{NaiveDate, - NaiveDateTime, - NaiveTime, - offset::{FixedOffset, - TimeZone, - }, - DateTime, - Utc, - }; +use chrono::{ + offset::{FixedOffset, TimeZone}, + DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc, +}; use uuid::Uuid; /// SQL values such as int, double, string timestamp #[derive(Debug, Clone, PartialEq)] -pub enum Value{ +pub enum Value { /// Literal signed long Long(i64), /// Literal floating point value @@ -40,11 +34,9 @@ pub enum Value{ Null, } - -impl ToString for Value{ - +impl ToString for Value { fn to_string(&self) -> String { - match self{ + match self { Value::Long(v) => v.to_string(), Value::Double(v) => v.to_string(), Value::String(v) => v.to_string(), diff --git a/src/sqlparser.rs b/src/sqlparser.rs index 27b60748..a8ee791f 100644 --- a/src/sqlparser.rs +++ b/src/sqlparser.rs @@ -17,15 +17,10 @@ use super::dialect::Dialect; use super::sqlast::*; use super::sqltokenizer::*; -use chrono::{NaiveDate, - NaiveDateTime, - NaiveTime, - offset::{FixedOffset, - TimeZone, - }, - DateTime, - Utc, - }; +use chrono::{ + offset::{FixedOffset, TimeZone}, + DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc, +}; #[derive(Debug, Clone)] pub enum ParserError { @@ -95,100 +90,102 @@ impl Parser { /// Parse an expression prefix pub fn parse_prefix(&mut self) -> Result { match self.next_token() { - Some(t) => { - match t { - Token::Keyword(k) => match k.to_uppercase().as_ref() { - "SELECT" => Ok(self.parse_select()?), - "CREATE" => Ok(self.parse_create()?), - "DELETE" => Ok(self.parse_delete()?), - "INSERT" => Ok(self.parse_insert()?), - "ALTER" => Ok(self.parse_alter()?), - "COPY" => Ok(self.parse_copy()?), - "TRUE" => { - self.prev_token(); - self.parse_sql_value() - } - "FALSE" => { - self.prev_token(); - self.parse_sql_value() - } - "NULL" => { - self.prev_token(); - self.parse_sql_value() - } - _ => return parser_err!(format!("No prefix parser for keyword {}", k)), - }, - Token::Mult => Ok(ASTNode::SQLWildcard), - Token::Identifier(id) => { - if "CAST" == id.to_uppercase(){ - self.parse_cast_expression() - }else{ - match self.peek_token() { - Some(Token::LParen) => { - self.parse_function_or_pg_cast(&id) - } - Some(Token::Period) => { - let mut id_parts: Vec = vec![id]; - while self.peek_token() == Some(Token::Period) { - self.consume_token(&Token::Period)?; - match self.next_token() { - Some(Token::Identifier(id)) => id_parts.push(id), - _ => { - return parser_err!(format!( - "Error parsing compound identifier" - )) - } + Some(t) => match t { + Token::Keyword(k) => match k.to_uppercase().as_ref() { + "SELECT" => Ok(self.parse_select()?), + "CREATE" => Ok(self.parse_create()?), + "DELETE" => Ok(self.parse_delete()?), + "INSERT" => Ok(self.parse_insert()?), + "ALTER" => Ok(self.parse_alter()?), + "COPY" => Ok(self.parse_copy()?), + "TRUE" => { + self.prev_token(); + self.parse_sql_value() + } + "FALSE" => { + self.prev_token(); + self.parse_sql_value() + } + "NULL" => { + self.prev_token(); + self.parse_sql_value() + } + _ => return parser_err!(format!("No prefix parser for keyword {}", k)), + }, + Token::Mult => Ok(ASTNode::SQLWildcard), + Token::Identifier(id) => { + if "CAST" == id.to_uppercase() { + self.parse_cast_expression() + } else { + match self.peek_token() { + Some(Token::LParen) => self.parse_function_or_pg_cast(&id), + Some(Token::Period) => { + let mut id_parts: Vec = vec![id]; + while self.peek_token() == Some(Token::Period) { + self.consume_token(&Token::Period)?; + match self.next_token() { + Some(Token::Identifier(id)) => id_parts.push(id), + _ => { + return parser_err!(format!( + "Error parsing compound identifier" + )) } } - Ok(ASTNode::SQLCompoundIdentifier(id_parts)) } - _ => Ok(ASTNode::SQLIdentifier(id)), + Ok(ASTNode::SQLCompoundIdentifier(id_parts)) } + _ => Ok(ASTNode::SQLIdentifier(id)), } } - Token::Number(ref n) => { - self.prev_token(); - self.parse_sql_value() - }, - Token::String(ref s) => { - self.prev_token(); - self.parse_sql_value() - } - Token::SingleQuotedString(ref s) => { - self.prev_token(); - self.parse_sql_value() - } - Token::DoubleQuotedString(ref s) => { - self.prev_token(); - self.parse_sql_value() - } - _ => parser_err!(format!( - "Prefix parser expected a keyword but found {:?}", - t - )), } - } + Token::Number(ref n) => { + self.prev_token(); + self.parse_sql_value() + } + Token::String(ref s) => { + self.prev_token(); + self.parse_sql_value() + } + Token::SingleQuotedString(ref s) => { + self.prev_token(); + self.parse_sql_value() + } + Token::DoubleQuotedString(ref s) => { + self.prev_token(); + self.parse_sql_value() + } + _ => parser_err!(format!( + "Prefix parser expected a keyword but found {:?}", + t + )), + }, None => parser_err!(format!("Prefix parser expected a keyword but hit EOF")), } } pub fn parse_function_or_pg_cast(&mut self, id: &str) -> Result { let func = self.parse_function(&id)?; - if let Some(Token::DoubleColon) = self.peek_token(){ + if let Some(Token::DoubleColon) = self.peek_token() { self.parse_pg_cast(func) - }else{ + } else { Ok(func) } } pub fn parse_function(&mut self, id: &str) -> Result { self.consume_token(&Token::LParen)?; - if let Ok(true) = self.consume_token(&Token::RParen){ - Ok(ASTNode::SQLFunction { id: id.to_string(), args: vec![] }) - }else{ + if let Ok(true) = self.consume_token(&Token::RParen) { + Ok(ASTNode::SQLFunction { + id: id.to_string(), + args: vec![], + }) + } else { let args = self.parse_expr_list()?; self.consume_token(&Token::RParen)?; - Ok(ASTNode::SQLFunction { id: id.to_string(), args }) + Ok(ASTNode::SQLFunction { + id: id.to_string(), + args, + }) } } @@ -205,24 +202,23 @@ impl Parser { }) } - /// Parse a postgresql casting style which is in the form or expr::datatype pub fn parse_pg_cast(&mut self, expr: ASTNode) -> Result { let ast = self.consume_token(&Token::DoubleColon)?; - let datatype = if let Ok(data_type) = self.parse_data_type(){ + let datatype = if let Ok(data_type) = self.parse_data_type() { Ok(data_type) - }else if let Ok(table_name) = self.parse_tablename(){ + } else if let Ok(table_name) = self.parse_tablename() { Ok(SQLType::Custom(table_name)) - }else{ - parser_err!("Expecting datatype or identifier") + } else { + parser_err!("Expecting datatype or identifier") }; - let pg_cast = ASTNode::SQLCast{ - expr: Box::new(expr), - data_type: datatype?, - }; - if let Some(Token::DoubleColon) = self.peek_token(){ + let pg_cast = ASTNode::SQLCast { + expr: Box::new(expr), + data_type: datatype?, + }; + if let Some(Token::DoubleColon) = self.peek_token() { self.parse_pg_cast(pg_cast) - }else{ + } else { Ok(pg_cast) } } @@ -236,21 +232,23 @@ impl Parser { debug!("parsing infix"); match self.next_token() { Some(tok) => match tok { - Token::Keyword(ref k) => if k == "IS" { - if self.parse_keywords(vec!["NULL"]) { - Ok(Some(ASTNode::SQLIsNull(Box::new(expr)))) - } else if self.parse_keywords(vec!["NOT", "NULL"]) { - Ok(Some(ASTNode::SQLIsNotNull(Box::new(expr)))) + Token::Keyword(ref k) => { + if k == "IS" { + if self.parse_keywords(vec!["NULL"]) { + Ok(Some(ASTNode::SQLIsNull(Box::new(expr)))) + } else if self.parse_keywords(vec!["NOT", "NULL"]) { + Ok(Some(ASTNode::SQLIsNotNull(Box::new(expr)))) + } else { + parser_err!("Invalid tokens after IS") + } } else { - parser_err!("Invalid tokens after IS") + Ok(Some(ASTNode::SQLBinaryExpr { + left: Box::new(expr), + op: self.to_sql_operator(&tok)?, + right: Box::new(self.parse_expr(precedence)?), + })) } - } else { - Ok(Some(ASTNode::SQLBinaryExpr { - left: Box::new(expr), - op: self.to_sql_operator(&tok)?, - right: Box::new(self.parse_expr(precedence)?), - })) - }, + } Token::Eq | Token::Neq | Token::Gt @@ -266,10 +264,10 @@ impl Parser { op: self.to_sql_operator(&tok)?, right: Box::new(self.parse_expr(precedence)?), })), - | Token::DoubleColon => { + Token::DoubleColon => { let pg_cast = self.parse_pg_cast(expr)?; Ok(Some(pg_cast)) - }, + } _ => parser_err!(format!("No infix parser for token {:?}", tok)), }, None => Ok(None), @@ -327,10 +325,9 @@ impl Parser { self.peek_token_skip_whitespace() } - pub fn skip_whitespace(&mut self) -> Option { loop { - match self.next_token_no_skip(){ + match self.next_token_no_skip() { Some(Token::Whitespace(ws)) => { continue; } @@ -345,7 +342,7 @@ impl Parser { fn til_non_whitespace(&self) -> Option { let mut index = self.index; loop { - match self.token_at(index){ + match self.token_at(index) { Some(Token::Whitespace(ws)) => { index = index + 1; } @@ -360,23 +357,22 @@ impl Parser { } /// see the token at this index - fn token_at(&self, n: usize) -> Option{ - if let Some(token) = self.tokens.get(n){ + fn token_at(&self, n: usize) -> Option { + if let Some(token) = self.tokens.get(n) { Some(token.clone()) - }else{ + } else { None } } pub fn peek_token_skip_whitespace(&self) -> Option { - if let Some(n) = self.til_non_whitespace(){ + if let Some(n) = self.til_non_whitespace() { self.token_at(n) - }else{ + } else { None } } - /// Get the next token skipping whitespace and increment the token index pub fn next_token(&mut self) -> Option { self.skip_whitespace() @@ -393,15 +389,15 @@ impl Parser { /// if prev token is whitespace skip it /// if prev token is not whitespace skipt it as well - pub fn prev_token_skip_whitespace(&mut self) -> Option{ - loop{ - match self.prev_token_no_skip(){ + pub fn prev_token_skip_whitespace(&mut self) -> Option { + loop { + match self.prev_token_no_skip() { Some(Token::Whitespace(ws)) => { continue; } token => { return token; - }, + } } } } @@ -454,21 +450,18 @@ impl Parser { /// Consume the next token if it matches the expected token, otherwise return an error pub fn consume_token(&mut self, expected: &Token) -> Result { match self.peek_token() { - Some(ref t) => if *t == *expected { - self.next_token(); - Ok(true) - } else { - Ok(false) - }, - other => parser_err!(format!( - "expected token {:?} but was {:?}", - expected, - other, - )), + Some(ref t) => { + if *t == *expected { + self.next_token(); + Ok(true) + } else { + Ok(false) + } + } + other => parser_err!(format!("expected token {:?} but was {:?}", expected, other,)), } } - /// Parse a SQL CREATE statement pub fn parse_create(&mut self) -> Result { if self.parse_keywords(vec!["TABLE"]) { @@ -481,10 +474,10 @@ impl Parser { if let Ok(data_type) = self.parse_data_type() { let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]); let is_unique = self.parse_keyword("UNIQUE"); - let default = if self.parse_keyword("DEFAULT"){ + let default = if self.parse_keyword("DEFAULT") { let expr = self.parse_expr(0)?; Some(Box::new(expr)) - }else{ + } else { None }; let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) { @@ -527,16 +520,20 @@ impl Parser { } } } else { - return parser_err!( - format!("Error parsing data type in column definition near: {:?}", self.peek_token()) - ); + return parser_err!(format!( + "Error parsing data type in column definition near: {:?}", + self.peek_token() + )); } } else { return parser_err!("Error parsing column name"); } } } - Ok(ASTNode::SQLCreateTable { name: table_name, columns }) + Ok(ASTNode::SQLCreateTable { + name: table_name, + columns, + }) } else { parser_err!(format!( "Unexpected token after CREATE: {:?}", @@ -550,34 +547,35 @@ impl Parser { let is_unique_key = self.parse_keywords(vec!["UNIQUE", "KEY"]); let is_foreign_key = self.parse_keywords(vec!["FOREIGN", "KEY"]); self.consume_token(&Token::LParen)?; - let column_names= self.parse_column_names()?; + let column_names = self.parse_column_names()?; self.consume_token(&Token::RParen)?; - let key = Key{ + let key = Key { name: constraint_name.to_string(), - columns: column_names + columns: column_names, }; - if is_primary_key{ + if is_primary_key { Ok(TableKey::PrimaryKey(key)) - } - else if is_unique_key{ + } else if is_unique_key { Ok(TableKey::UniqueKey(key)) - } - else if is_foreign_key{ - if self.parse_keyword("REFERENCES"){ + } else if is_foreign_key { + if self.parse_keyword("REFERENCES") { let foreign_table = self.parse_tablename()?; self.consume_token(&Token::LParen)?; let referred_columns = self.parse_column_names()?; self.consume_token(&Token::RParen)?; - Ok(TableKey::ForeignKey{ + Ok(TableKey::ForeignKey { key, foreign_table, referred_columns, }) - }else{ + } else { parser_err!("Expecting references") } - }else{ - parser_err!(format!("Expecting primary key, unique key, or foreign key, found: {:?}", self.peek_token())) + } else { + parser_err!(format!( + "Expecting primary key, unique key, or foreign key, found: {:?}", + self.peek_token() + )) } } @@ -585,52 +583,64 @@ impl Parser { if self.parse_keyword("TABLE") { let is_only = self.parse_keyword("ONLY"); let table_name = self.parse_tablename()?; - let operation:Result = if self.parse_keywords(vec!["ADD", "CONSTRAINT"]){ - match self.next_token(){ - Some(Token::Identifier(ref id)) => { - let table_key = self.parse_table_key(id)?; - Ok(AlterOperation::AddConstraint(table_key)) + let operation: Result = + if self.parse_keywords(vec!["ADD", "CONSTRAINT"]) { + match self.next_token() { + Some(Token::Identifier(ref id)) => { + let table_key = self.parse_table_key(id)?; + 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{ - return parser_err!(format!("Expecting ADD CONSTRAINT, found :{:?}", self.peek_token())); - }; - Ok(ASTNode::SQLAlterTable{ + } else { + return parser_err!(format!( + "Expecting ADD CONSTRAINT, found :{:?}", + self.peek_token() + )); + }; + Ok(ASTNode::SQLAlterTable { name: table_name, operation: operation?, }) } 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 pub fn parse_copy(&mut self) -> Result { let table_name = self.parse_tablename()?; - let columns = if self.consume_token(&Token::LParen)?{ + let columns = if self.consume_token(&Token::LParen)? { let column_names = self.parse_column_names()?; self.consume_token(&Token::RParen)?; column_names - }else{ + } else { vec![] }; self.parse_keyword("FROM"); self.parse_keyword("STDIN"); self.consume_token(&Token::SemiColon); 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 /// COPY payload - fn parse_tsv(&mut self) -> Result, ParserError>{ + fn parse_tsv(&mut self) -> Result, ParserError> { let values = self.parse_tab_value()?; Ok(values) - } fn parse_sql_value(&mut self) -> Result { @@ -640,8 +650,8 @@ impl Parser { fn parse_tab_value(&mut self) -> Result, ParserError> { let mut values = vec![]; let mut content = String::from(""); - while let Some(t) = self.next_token_no_skip(){ - match t{ + while let Some(t) = self.next_token_no_skip() { + match t { Token::Whitespace(Whitespace::Tab) => { values.push(Value::String(content.to_string())); content.clear(); @@ -653,11 +663,12 @@ impl Parser { Token::Backslash => { if let Ok(true) = self.consume_token(&Token::Period) { return Ok(values); - }if let Some(token) = self.next_token(){ - if token == Token::Identifier("N".to_string()){ + } + if let Some(token) = self.next_token() { + if token == Token::Identifier("N".to_string()) { values.push(Value::Null); } - }else{ + } else { continue; } } @@ -685,10 +696,10 @@ impl Parser { Err(e) => { let index = self.index; self.prev_token(); - if let Ok(timestamp) = self.parse_timestamp_value(){ + if let Ok(timestamp) = self.parse_timestamp_value() { println!("timstamp: {:?}", timestamp); Ok(timestamp) - }else{ + } else { self.index = index; parser_err!(format!("Could not parse '{}' as i64: {}", n, e)) } @@ -696,10 +707,10 @@ impl Parser { }, Token::Number(ref n) => match n.parse::() { Ok(n) => { - if let Some(Token::Minus) = self.peek_token(){ + if let Some(Token::Minus) = self.peek_token() { self.prev_token(); self.parse_timestamp_value() - }else{ + } else { Ok(Value::Long(n)) } } @@ -707,8 +718,12 @@ impl Parser { }, Token::Identifier(id) => Ok(Value::String(id.to_string())), Token::String(ref s) => Ok(Value::String(s.to_string())), - Token::SingleQuotedString(ref s) => Ok(Value::SingleQuotedString(s.to_string())), - Token::DoubleQuotedString(ref s) => Ok(Value::DoubleQuotedString(s.to_string())), + Token::SingleQuotedString(ref s) => { + 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())), } } @@ -744,9 +759,8 @@ impl Parser { } } - pub fn parse_timezone_offset(&mut self) -> Result { - match self.next_token(){ + match self.next_token() { Some(Token::Plus) => { let n = self.parse_literal_int()?; Ok(n as i8) @@ -755,46 +769,56 @@ impl Parser { let n = self.parse_literal_int()?; 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 { let year = self.parse_literal_int()?; let date = self.parse_date(year)?; - if let Ok(time) = self.parse_time(){ + if let Ok(time) = self.parse_time() { let date_time = NaiveDateTime::new(date, time); - match self.peek_token(){ - Some(token) => match token{ - Token::Plus | Token::Minus => { - let tz = self.parse_timezone_offset()?; - let offset = FixedOffset::east(tz as i32 * 3600); - Ok(Value::Timestamp(DateTime::from_utc(date_time, offset))) - } - _ => Ok(Value::DateTime(date_time)), - }, + match self.peek_token() { + Some(token) => match token { + Token::Plus | Token::Minus => { + let tz = self.parse_timezone_offset()?; + let offset = FixedOffset::east(tz as i32 * 3600); + Ok(Value::Timestamp(DateTime::from_utc(date_time, offset))) + } + _ => Ok(Value::DateTime(date_time)), + }, _ => Ok(Value::DateTime(date_time)), } - }else{ - parser_err!(format!("Expecting time after date, but found {:?}", self.peek_token())) + } else { + parser_err!(format!( + "Expecting time after date, but found {:?}", + self.peek_token() + )) } } pub fn parse_date(&mut self, year: i64) -> Result { - if let Ok(true) = self.consume_token(&Token::Minus){ + if let Ok(true) = self.consume_token(&Token::Minus) { let month = self.parse_literal_int()?; - if let Ok(true) = self.consume_token(&Token::Minus){ + if let Ok(true) = self.consume_token(&Token::Minus) { let day = self.parse_literal_int()?; let date = NaiveDate::from_ymd(year as i32, month as u32, day as u32); Ok(date) - }else{ - parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token())) + } else { + parser_err!(format!( + "Expecting `-` for date separator, found {:?}", + self.peek_token() + )) } - }else{ - parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token())) + } else { + parser_err!(format!( + "Expecting `-` for date separator, found {:?}", + self.peek_token() + )) } - } pub fn parse_time(&mut self) -> Result { @@ -804,10 +828,15 @@ impl Parser { self.consume_token(&Token::Colon)?; let sec = self.parse_literal_double()?; 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()?; - Ok(NaiveTime::from_hms_milli(hour as u32, min as u32, sec as u32, ms as u32)) - }else{ + Ok(NaiveTime::from_hms_milli( + hour as u32, + min as u32, + sec as u32, + ms as u32, + )) + } else { Ok(NaiveTime::from_hms(hour as u32, min as u32, sec as u32)) } } @@ -819,60 +848,78 @@ impl Parser { "BOOLEAN" => Ok(SQLType::Boolean), "FLOAT" => Ok(SQLType::Float(self.parse_optional_precision()?)), "REAL" => Ok(SQLType::Real), - "DOUBLE" => if self.parse_keyword("PRECISION"){ - Ok(SQLType::Double) - }else{ - Ok(SQLType::Double) + "DOUBLE" => { + if self.parse_keyword("PRECISION") { + Ok(SQLType::Double) + } else { + Ok(SQLType::Double) + } } "SMALLINT" => Ok(SQLType::SmallInt), "INT" | "INTEGER" => Ok(SQLType::Int), "BIGINT" => Ok(SQLType::BigInt), "VARCHAR" => Ok(SQLType::Varchar(self.parse_optional_precision()?)), "CHARACTER" => { - if self.parse_keyword("VARYING"){ + if self.parse_keyword("VARYING") { Ok(SQLType::Varchar(self.parse_optional_precision()?)) - }else{ + } else { Ok(SQLType::Char(self.parse_optional_precision()?)) } } "UUID" => Ok(SQLType::Uuid), "DATE" => Ok(SQLType::Date), - "TIMESTAMP" => if self.parse_keyword("WITH"){ - if self.parse_keywords(vec!["TIME","ZONE"]){ + "TIMESTAMP" => { + if self.parse_keyword("WITH") { + if self.parse_keywords(vec!["TIME", "ZONE"]) { + Ok(SQLType::Timestamp) + } else { + parser_err!(format!( + "Expecting 'time zone', found: {:?}", + self.peek_token() + )) + } + } else if self.parse_keyword("WITHOUT") { + if self.parse_keywords(vec!["TIME", "ZONE"]) { + Ok(SQLType::Timestamp) + } else { + parser_err!(format!( + "Expecting 'time zone', found: {:?}", + self.peek_token() + )) + } + } else { Ok(SQLType::Timestamp) - }else{ - parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) } - }else if self.parse_keyword("WITHOUT"){ - if self.parse_keywords(vec!["TIME","ZONE"]){ - Ok(SQLType::Timestamp) - }else{ - parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) - } - }else{ - Ok(SQLType::Timestamp) } - "TIME" => if self.parse_keyword("WITH"){ - if self.parse_keywords(vec!["TIME","ZONE"]){ - Ok(SQLType::Time) - }else{ - parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) + "TIME" => { + if self.parse_keyword("WITH") { + if self.parse_keywords(vec!["TIME", "ZONE"]) { + Ok(SQLType::Time) + } else { + parser_err!(format!( + "Expecting 'time zone', found: {:?}", + self.peek_token() + )) + } + } else if self.parse_keyword("WITHOUT") { + if self.parse_keywords(vec!["TIME", "ZONE"]) { + Ok(SQLType::Time) + } else { + parser_err!(format!( + "Expecting 'time zone', found: {:?}", + self.peek_token() + )) + } + } else { + Ok(SQLType::Timestamp) } - }else if self.parse_keyword("WITHOUT"){ - if self.parse_keywords(vec!["TIME","ZONE"]){ - Ok(SQLType::Time) - }else{ - parser_err!(format!("Expecting 'time zone', found: {:?}", self.peek_token())) - } - }else{ - Ok(SQLType::Timestamp) } "REGCLASS" => Ok(SQLType::Regclass), "TEXT" => { - if let Ok(true) = self.consume_token(&Token::LBracket){ + if let Ok(true) = self.consume_token(&Token::LBracket) { self.consume_token(&Token::RBracket)?; Ok(SQLType::Array(Box::new(SQLType::Text))) - }else{ + } else { Ok(SQLType::Text) } } @@ -886,8 +933,8 @@ impl Parser { Some(Token::Identifier(id)) => { if let Ok(true) = self.consume_token(&Token::Period) { let ids = self.parse_tablename()?; - Ok(SQLType::Custom(format!("{}.{}",id,ids))) - }else{ + Ok(SQLType::Custom(format!("{}.{}", id, ids))) + } else { Ok(SQLType::Custom(id)) } } @@ -895,25 +942,26 @@ impl Parser { } } - pub fn parse_compound_identifier(&mut self, separator: &Token) -> Result { let mut idents = vec![]; let mut expect_identifier = true; loop { let token = &self.next_token(); - match token{ - Some(token) => match token{ - Token::Identifier(s) => if expect_identifier{ - expect_identifier = false; - idents.push(s.to_string()); - }else{ - self.prev_token(); - break; + match token { + Some(token) => match token { + Token::Identifier(s) => { + if expect_identifier { + expect_identifier = false; + idents.push(s.to_string()); + } else { + self.prev_token(); + break; + } } token if token == separator => { - if expect_identifier{ + if expect_identifier { return parser_err!(format!("Expecting identifier, found {:?}", token)); - }else{ + } else { expect_identifier = true; continue; } @@ -922,7 +970,7 @@ impl Parser { self.prev_token(); break; } - } + }, None => { self.prev_token(); break; @@ -934,7 +982,7 @@ impl Parser { pub fn parse_tablename(&mut self) -> Result { let identifier = self.parse_compound_identifier(&Token::Period)?; - match identifier{ + match identifier { ASTNode::SQLCompoundIdentifier(idents) => Ok(idents.join(".")), other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)), } @@ -942,7 +990,7 @@ impl Parser { pub fn parse_column_names(&mut self) -> Result, ParserError> { let identifier = self.parse_compound_identifier(&Token::Comma)?; - match identifier{ + match identifier { ASTNode::SQLCompoundIdentifier(idents) => Ok(idents), other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)), } @@ -964,12 +1012,14 @@ impl Parser { } } - pub fn parse_optional_precision_scale(&mut self) -> Result<(usize, Option), ParserError> { + pub fn parse_optional_precision_scale( + &mut self, + ) -> Result<(usize, Option), ParserError> { if self.consume_token(&Token::LParen)? { 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) { Some(self.parse_literal_int()? as usize) - }else{ + } else { None }; self.consume_token(&Token::RParen)?; @@ -1070,18 +1120,22 @@ impl Parser { pub fn parse_insert(&mut self) -> Result { self.parse_keyword("INTO"); let table_name = self.parse_tablename()?; - let columns = if self.consume_token(&Token::LParen)?{ + let columns = if self.consume_token(&Token::LParen)? { let column_names = self.parse_column_names()?; self.consume_token(&Token::RParen)?; column_names - }else{ + } else { vec![] }; self.parse_keyword("VALUES"); self.consume_token(&Token::LParen)?; let values = self.parse_expr_list()?; 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 @@ -1157,4 +1211,3 @@ impl Parser { } } } - diff --git a/src/sqltokenizer.rs b/src/sqltokenizer.rs index 6abbebc1..8f3e15d7 100644 --- a/src/sqltokenizer.rs +++ b/src/sqltokenizer.rs @@ -71,7 +71,7 @@ pub enum Token { RParen, /// Period (used for compound identifiers or projections into nested types) Period, - /// Colon `:` + /// Colon `:` Colon, /// DoubleColon `::` (used for casting in postgresql) DoubleColon, @@ -91,16 +91,16 @@ pub enum Token { RBrace, } -impl ToString for Token{ +impl ToString for Token { fn to_string(&self) -> String { - match self{ + match self { Token::Identifier(ref id) => id.to_string(), - Token::Keyword(ref k) =>k.to_string(), + Token::Keyword(ref k) => k.to_string(), Token::Number(ref n) => n.to_string(), Token::String(ref s) => s.to_string(), Token::Char(ref c) => c.to_string(), - Token::SingleQuotedString(ref s) => format!("'{}'",s), - Token::DoubleQuotedString(ref s) => format!("\"{}\"",s), + Token::SingleQuotedString(ref s) => format!("'{}'", s), + Token::DoubleQuotedString(ref s) => format!("\"{}\"", s), Token::Comma => ",".to_string(), Token::Whitespace(ws) => ws.to_string(), Token::Eq => "=".to_string(), @@ -131,15 +131,15 @@ impl ToString for Token{ } #[derive(Debug, Clone, PartialEq)] -pub enum Whitespace{ +pub enum Whitespace { Space, Newline, - Tab + Tab, } -impl ToString for Whitespace{ +impl ToString for Whitespace { fn to_string(&self) -> String { - match self{ + match self { Whitespace::Space => " ".to_string(), Whitespace::Newline => "\n".to_string(), Whitespace::Tab => "\t".to_string(), @@ -349,10 +349,8 @@ impl<'a> Tokenizer<'a> { match chars.peek() { Some(&ch) => match ch { // double colon - ':' => { - self.consume_and_return(chars, Token::DoubleColon) - } - _ => Ok(Some(Token::Colon)), + ':' => self.consume_and_return(chars, Token::DoubleColon), + _ => 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::LBrace), '}' => 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), } } - fn consume_and_return(&self, chars: &mut Peekable, t: Token) -> Result, TokenizerError> { + fn consume_and_return( + &self, + chars: &mut Peekable, + t: Token, + ) -> Result, TokenizerError> { chars.next(); Ok(Some(t)) } @@ -486,16 +488,15 @@ mod tests { let tokens = tokenizer.tokenize().unwrap(); println!("tokens: {:#?}", tokens); let expected = vec![ - Token::Whitespace(Whitespace::Newline), - Token::Char('م'), - Token::Char('ص'), - Token::Char('ط'), - Token::Char('ف'), - Token::Char('ى'), - Token::Identifier("h".to_string()) - ]; + Token::Whitespace(Whitespace::Newline), + Token::Char('م'), + Token::Char('ص'), + Token::Char('ط'), + Token::Char('ف'), + Token::Char('ى'), + Token::Identifier("h".to_string()), + ]; compare(expected, tokens); - } #[test] diff --git a/tests/sqlparser.rs b/tests/sqlparser.rs index e740e169..eddf7edc 100644 --- a/tests/sqlparser.rs +++ b/tests/sqlparser.rs @@ -1,22 +1,31 @@ extern crate log; extern crate sqlparser; -use sqlparser::sqlast::*; -use sqlparser::sqltokenizer::*; -use sqlparser::sqlparser::*; use sqlparser::dialect::PostgreSqlDialect; +use sqlparser::sqlast::*; +use sqlparser::sqlparser::*; +use sqlparser::sqltokenizer::*; use log::*; #[test] -fn test_prev_index(){ +fn test_prev_index() { let sql: &str = "SELECT version()"; let mut parser = parser(sql); assert_eq!(parser.prev_token(), None); assert_eq!(parser.next_token(), Some(Token::Keyword("SELECT".into()))); - assert_eq!(parser.next_token(), 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.next_token(), + 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(), None); } @@ -30,7 +39,9 @@ fn parse_delete_statement() { match parse_sql(&sql) { ASTNode::SQLDelete { relation, .. } => { assert_eq!( - Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString("table".to_string())))), + Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString( + "table".to_string() + )))), relation ); } @@ -56,7 +67,9 @@ fn parse_where_delete_statement() { .. } => { assert_eq!( - Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString("table".to_string())))), + Some(Box::new(ASTNode::SQLValue(Value::SingleQuotedString( + "table".to_string() + )))), relation ); @@ -97,11 +110,21 @@ fn parse_simple_insert() { assert_eq!(sql, ast.to_string()); match ast { ASTNode::SQLInsert { - table_name, columns, values, .. + table_name, + columns, + values, + .. } => { assert_eq!(table_name, "customer"); 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), } @@ -114,11 +137,21 @@ fn parse_common_insert() { assert_eq!(sql, ast.to_string()); match ast { ASTNode::SQLInsert { - table_name, columns, values, .. + table_name, + columns, + values, + .. } => { assert_eq!(table_name, "public.customer"); 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), } @@ -131,11 +164,21 @@ fn parse_complex_insert() { assert_eq!(sql, ast.to_string()); match ast { ASTNode::SQLInsert { - table_name, columns, values, .. + table_name, + columns, + values, + .. } => { assert_eq!(table_name, "db.public.customer"); 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), } @@ -155,11 +198,24 @@ fn parse_insert_with_columns() { assert_eq!(sql, ast.to_string()); match ast { ASTNode::SQLInsert { - table_name, columns, values, .. + table_name, + columns, + values, + .. } => { assert_eq!(table_name, "public.customer"); - assert_eq!(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_eq!( + 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), } @@ -412,7 +468,8 @@ fn parse_create_table_with_defaults() { activebool boolean DEFAULT true NOT NULL, create_date date DEFAULT now()::text 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); match ast { ASTNode::SQLCreateTable { name, columns } => { @@ -481,13 +538,15 @@ fn parse_create_table_from_pg_dump() { #[test] fn parse_create_table_with_inherit() { - let sql = String::from("\ - CREATE TABLE bazaar.settings (\ - settings_id uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, \ - user_id uuid UNIQUE, \ - value text[], \ - use_metric boolean DEFAULT true\ - )"); + let sql = String::from( + "\ + CREATE TABLE bazaar.settings (\ + settings_id uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, \ + user_id uuid UNIQUE, \ + value text[], \ + use_metric boolean DEFAULT true\ + )", + ); let ast = parse_sql(&sql); assert_eq!(sql, ast.to_string()); match ast { @@ -513,15 +572,17 @@ fn parse_create_table_with_inherit() { } #[test] -fn parse_alter_table_constraint_primary_key(){ - let sql = String::from("\ - ALTER TABLE bazaar.address \ - ADD CONSTRAINT address_pkey PRIMARY KEY (address_id)"); +fn parse_alter_table_constraint_primary_key() { + let sql = String::from( + "\ + ALTER TABLE bazaar.address \ + ADD CONSTRAINT address_pkey PRIMARY KEY (address_id)", + ); let ast = parse_sql(&sql); println!("ast: {:?}", ast); assert_eq!(sql, ast.to_string()); match ast { - ASTNode::SQLAlterTable{ name, operation } => { + ASTNode::SQLAlterTable { name, operation } => { assert_eq!(name, "bazaar.address"); } _ => assert!(false), @@ -529,7 +590,7 @@ fn parse_alter_table_constraint_primary_key(){ } #[test] -fn parse_alter_table_constraint_foreign_key(){ +fn parse_alter_table_constraint_foreign_key() { let sql = String::from("\ ALTER TABLE public.customer \ ADD CONSTRAINT customer_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id)"); @@ -537,7 +598,7 @@ fn parse_alter_table_constraint_foreign_key(){ assert_eq!(sql, ast.to_string()); println!("ast: {:?}", ast); match ast { - ASTNode::SQLAlterTable{ name, operation } => { + ASTNode::SQLAlterTable { name, operation } => { assert_eq!(name, "public.customer"); } _ => assert!(false), @@ -545,7 +606,7 @@ fn parse_alter_table_constraint_foreign_key(){ } #[test] -fn parse_copy_example(){ +fn parse_copy_example() { let sql = String::from(r#"COPY public.actor (actor_id, first_name, last_name, last_update, value) FROM stdin; 1 PENELOPE GUINESS 2006-02-15 09:34:33 0.11111 2 NICK WAHLBERG 2006-02-15 09:34:33 0.22222 @@ -572,20 +633,20 @@ PHP ₱ USD $ } #[test] -fn parse_timestamps_example(){ +fn parse_timestamps_example() { let sql = "2016-02-15 09:43:33"; let ast = parse_sql(sql); assert_eq!(sql, ast.to_string()); } #[test] -fn parse_timestamps_with_millis_example(){ +fn parse_timestamps_with_millis_example() { let sql = "2017-11-02 19:15:42.308637"; let ast = parse_sql(sql); } #[test] -fn parse_example_value(){ +fn parse_example_value() { let sql = "SARAH.LEWIS@sakilacustomer.org"; let ast = parse_sql(sql); assert_eq!(sql, ast.to_string()); @@ -623,7 +684,10 @@ fn parse_literal_string() { assert_eq!(sql, ast.to_string()); match ast { 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!(), } @@ -634,7 +698,7 @@ fn parse_select_version() { let sql = "SELECT @@version"; let ast = parse_sql(&sql); assert_eq!(sql, ast.to_string()); - match ast{ + match ast { ASTNode::SQLSelect { ref projection, .. } => { assert_eq!( projection[0], @@ -646,7 +710,7 @@ fn parse_select_version() { } #[test] -fn parse_function_now(){ +fn parse_function_now() { let sql = "now()"; let ast = parse_sql(sql); assert_eq!(sql, ast.to_string()); @@ -666,4 +730,3 @@ fn parser(sql: &str) -> Parser { debug!("tokens: {:#?}", tokens); Parser::new(tokens) } -