From 959e9d07759eb64a7cf9cfdb4a85233d7408ebc2 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Mon, 8 Jun 2020 08:52:18 +0200 Subject: [PATCH 01/17] Store keywords in enum --- src/dialect/keywords.rs | 11 +++ src/parser.rs | 188 +++++++++++++++++++--------------------- src/tokenizer.rs | 12 +-- 3 files changed, 105 insertions(+), 106 deletions(-) diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index bb4f678e0..0b9d2cc94 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -41,11 +41,22 @@ macro_rules! define_keywords { ($( $ident:ident $(= $string_keyword:expr)? ),*) => { + + #[derive(Debug, Clone, PartialEq)] + pub enum AllKeyWords { + $($ident),* + } + + pub const ALL_KEYWORDS_INDEX: &[AllKeyWords] = &[ + AllKeyWords::$($ident),* + ]; + $(kw_def!($ident $(= $string_keyword)?);)* pub const ALL_KEYWORDS: &[&str] = &[ $($ident),* ]; + } } diff --git a/src/parser.rs b/src/parser.rs index 9a22f4d19..b1c3b5b6a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,7 +15,7 @@ use log::debug; use super::ast::*; -use super::dialect::keywords; +use super::dialect::keywords::{AllKeyWords}; use super::dialect::Dialect; use super::tokenizer::*; use std::error::Error; @@ -113,27 +113,27 @@ impl Parser { pub fn parse_statement(&mut self) -> Result { match self.next_token() { Some(t) => match t { - Token::Word(ref w) if w.keyword != "" => match w.keyword.as_ref() { - "SELECT" | "WITH" | "VALUES" => { + Token::Word(ref w) if w.keyword.is_some() => match w.keyword.unwrap() { + AllKeyWords::SELECT | AllKeyWords::WITH | AllKeyWords::VALUES => { self.prev_token(); Ok(Statement::Query(Box::new(self.parse_query()?))) } - "CREATE" => Ok(self.parse_create()?), - "DROP" => Ok(self.parse_drop()?), - "DELETE" => Ok(self.parse_delete()?), - "INSERT" => Ok(self.parse_insert()?), - "UPDATE" => Ok(self.parse_update()?), - "ALTER" => Ok(self.parse_alter()?), - "COPY" => Ok(self.parse_copy()?), - "SET" => Ok(self.parse_set()?), - "SHOW" => Ok(self.parse_show()?), - "START" => Ok(self.parse_start_transaction()?), + AllKeyWords::CREATE => Ok(self.parse_create()?), + AllKeyWords::DROP => Ok(self.parse_drop()?), + AllKeyWords::DELETE => Ok(self.parse_delete()?), + AllKeyWords::INSERT => Ok(self.parse_insert()?), + AllKeyWords::UPDATE => Ok(self.parse_update()?), + AllKeyWords::ALTER => Ok(self.parse_alter()?), + AllKeyWords::COPY => Ok(self.parse_copy()?), + AllKeyWords::SET => Ok(self.parse_set()?), + AllKeyWords::SHOW => Ok(self.parse_show()?), + AllKeyWords::START => Ok(self.parse_start_transaction()?), // `BEGIN` is a nonstandard but common alias for the // standard `START TRANSACTION` statement. It is supported // by at least PostgreSQL and MySQL. - "BEGIN" => Ok(self.parse_begin()?), - "COMMIT" => Ok(self.parse_commit()?), - "ROLLBACK" => Ok(self.parse_rollback()?), + AllKeyWords::BEGIN => Ok(self.parse_begin()?), + AllKeyWords::COMMIT => Ok(self.parse_commit()?), + AllKeyWords::ROLLBACK => Ok(self.parse_rollback()?), _ => parser_err!(format!( "Unexpected keyword {:?} at the beginning of a statement", w.to_string() @@ -180,24 +180,24 @@ impl Parser { .next_token() .ok_or_else(|| ParserError::ParserError("Unexpected EOF".to_string()))?; let expr = match tok { - Token::Word(w) => match w.keyword.as_ref() { - "TRUE" | "FALSE" | "NULL" => { + Token::Word(w) => match w.keyword.unwrap() { + AllKeyWords::TRUE | AllKeyWords::FALSE | AllKeyWords::NULL => { self.prev_token(); Ok(Expr::Value(self.parse_value()?)) } - "CASE" => self.parse_case_expr(), - "CAST" => self.parse_cast_expr(), - "DATE" => Ok(Expr::Value(Value::Date(self.parse_literal_string()?))), - "EXISTS" => self.parse_exists_expr(), - "EXTRACT" => self.parse_extract_expr(), - "INTERVAL" => self.parse_literal_interval(), - "LISTAGG" => self.parse_listagg_expr(), - "NOT" => Ok(Expr::UnaryOp { + AllKeyWords::CASE => self.parse_case_expr(), + AllKeyWords::CAST => self.parse_cast_expr(), + AllKeyWords::DATE => Ok(Expr::Value(Value::Date(self.parse_literal_string()?))), + AllKeyWords::EXISTS => self.parse_exists_expr(), + AllKeyWords::EXTRACT => self.parse_extract_expr(), + AllKeyWords::INTERVAL => self.parse_literal_interval(), + AllKeyWords::LISTAGG => self.parse_listagg_expr(), + AllKeyWords::NOT => Ok(Expr::UnaryOp { op: UnaryOperator::Not, expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), }), - "TIME" => Ok(Expr::Value(Value::Time(self.parse_literal_string()?))), - "TIMESTAMP" => Ok(Expr::Value(Value::Timestamp(self.parse_literal_string()?))), + AllKeyWords::TIME => Ok(Expr::Value(Value::Time(self.parse_literal_string()?))), + AllKeyWords::TIMESTAMP => Ok(Expr::Value(Value::Timestamp(self.parse_literal_string()?))), // Here `w` is a word, check if it's a part of a multi-part // identifier, a function call, or a simple identifier: _ => match self.peek_token() { @@ -435,7 +435,7 @@ impl Parser { } else { self.expect_keyword("TRUNCATE")?; let filler = match self.peek_token() { - Some(Token::Word(kw)) if kw.keyword == "WITH" || kw.keyword == "WITHOUT" => { + Some(Token::Word(kw)) if kw.keyword == Some(AllKeyWords::WITH) || kw.keyword == Some(AllKeyWords::WITHOUT)=> { None } Some(Token::SingleQuotedString(_)) @@ -582,11 +582,11 @@ impl Parser { Token::Caret => Some(BinaryOperator::BitwiseXor), Token::Ampersand => Some(BinaryOperator::BitwiseAnd), Token::Div => Some(BinaryOperator::Divide), - Token::Word(ref k) => match k.keyword.as_ref() { - "AND" => Some(BinaryOperator::And), - "OR" => Some(BinaryOperator::Or), - "LIKE" => Some(BinaryOperator::Like), - "NOT" => { + Token::Word(ref k) => match k.keyword { + Some(AllKeyWords::AND) => Some(BinaryOperator::And), + Some(AllKeyWords::OR) => Some(BinaryOperator::Or), + Some(AllKeyWords::LIKE) => Some(BinaryOperator::Like), + Some(AllKeyWords::NOT) => { if self.parse_keyword("LIKE") { Some(BinaryOperator::NotLike) } else { @@ -605,17 +605,17 @@ impl Parser { right: Box::new(self.parse_subexpr(precedence)?), }) } else if let Token::Word(ref k) = tok { - match k.keyword.as_ref() { - "IS" => { - if self.parse_keyword("NULL") { + match k.keyword { + Some(AllKeyWords::IS) => { + if self.parse_keyword(Some(AllKeyWords::NULL)) { Ok(Expr::IsNull(Box::new(expr))) - } else if self.parse_keywords(vec!["NOT", "NULL"]) { + } else if self.parse_keywords(vec![Some(AllKeyWords::NOT), Some(AllKeyWords::NULL)]) { Ok(Expr::IsNotNull(Box::new(expr))) } else { self.expected("NULL or NOT NULL after IS", self.peek_token()) } } - "NOT" | "IN" | "BETWEEN" => { + Some(AllKeyWords::NOT) | Some(AllKeyWords::IN) | Some(AllKeyWords::BETWEEN) => { self.prev_token(); let negated = self.parse_keyword("NOT"); if self.parse_keyword("IN") { @@ -691,7 +691,7 @@ impl Parser { debug!("get_next_precedence() {:?}", token); match &token { - Token::Word(k) if k.keyword == "OR" => Ok(5), + Token::Word(k) if k.keyword == => Ok(5), Token::Word(k) if k.keyword == "AND" => Ok(10), Token::Word(k) if k.keyword == "NOT" => match &self.peek_nth_token(1) { // The precedence of NOT varies depending on keyword that @@ -781,9 +781,9 @@ impl Parser { } /// Report unexpected token - fn expected(&self, expected: &str, found: Option) -> Result { + fn expected(&self, expected: AllKeyWords, found: Option) -> Result { parser_err!(format!( - "Expected {}, found: {}", + "Expected {:?}, found: {}", expected, found.map_or_else(|| "EOF".to_string(), |t| format!("{}", t)) )) @@ -791,14 +791,9 @@ impl Parser { /// Look for an expected keyword and consume it if it exists #[must_use] - pub fn parse_keyword(&mut self, expected: &'static str) -> bool { - // Ideally, we'd accept a enum variant, not a string, but since - // it's not trivial to maintain the enum without duplicating all - // the keywords three times, we'll settle for a run-time check that - // the string actually represents a known keyword... - assert!(keywords::ALL_KEYWORDS.contains(&expected)); + pub fn parse_keyword(&mut self, expected: AllKeyWords) -> bool { match self.peek_token() { - Some(Token::Word(ref k)) if expected.eq_ignore_ascii_case(&k.keyword) => { + Some(Token::Word(ref k)) if Some(expected) == k.keyword => { self.next_token(); true } @@ -808,10 +803,10 @@ impl Parser { /// Look for an expected sequence of keywords and consume them if they exist #[must_use] - pub fn parse_keywords(&mut self, keywords: Vec<&'static str>) -> bool { + pub fn parse_keywords(&mut self, keywords: Vec) -> bool { let index = self.index; for keyword in keywords { - if !self.parse_keyword(&keyword) { + if !self.parse_keyword(keyword) { //println!("parse_keywords aborting .. did not find {}", keyword); // reset index and return immediately self.index = index; @@ -823,18 +818,11 @@ impl Parser { /// Look for one of the given keywords and return the one that matches. #[must_use] - pub fn parse_one_of_keywords(&mut self, keywords: &[&'static str]) -> Option<&'static str> { - for keyword in keywords { - assert!( - keywords::ALL_KEYWORDS.contains(keyword), - "{} is not contained in keyword list", - keyword - ); - } + pub fn parse_one_of_keywords(&mut self, keywords: &[AllKeyWords]) -> Option { match self.peek_token() { Some(Token::Word(ref k)) => keywords .iter() - .find(|keyword| keyword.eq_ignore_ascii_case(&k.keyword)) + .find(|keyword| Some(**keyword) == k.keyword) .map(|keyword| { self.next_token(); *keyword @@ -846,20 +834,20 @@ impl Parser { /// Bail out if the current token is not one of the expected keywords, or consume it if it is pub fn expect_one_of_keywords( &mut self, - keywords: &[&'static str], - ) -> Result<&'static str, ParserError> { + keywords: &[AllKeyWords], + ) -> Result { if let Some(keyword) = self.parse_one_of_keywords(keywords) { Ok(keyword) } else { self.expected( - &format!("one of {}", keywords.join(" or ")), + &format!("one of {}", keywords.iter().map(|x| format!("{:?}", x)).collect().join(" or ")), self.peek_token(), ) } } /// Bail out if the current token is not an expected keyword, or consume it if it is - pub fn expect_keyword(&mut self, expected: &'static str) -> Result<(), ParserError> { + pub fn expect_keyword(&mut self, expected: AllKeyWords) -> Result<(), ParserError> { if self.parse_keyword(expected) { Ok(()) } else { @@ -869,9 +857,9 @@ impl Parser { /// Bail out if the following tokens are not the expected sequence of /// keywords, or consume them if they are. - pub fn expect_keywords(&mut self, expected: &[&'static str]) -> Result<(), ParserError> { + pub fn expect_keywords(&mut self, expected: &[AllKeyWords]) -> Result<(), ParserError> { for kw in expected { - self.expect_keyword(kw)?; + self.expect_keyword(*kw)?; } Ok(()) } @@ -915,8 +903,8 @@ impl Parser { /// Parse either `ALL` or `DISTINCT`. Returns `true` if `DISTINCT` is parsed and results in a /// `ParserError` if both `ALL` and `DISTINCT` are fround. pub fn parse_all_or_distinct(&mut self) -> Result { - let all = self.parse_keyword("ALL"); - let distinct = self.parse_keyword("DISTINCT"); + let all = self.parse_keyword(AllKeyWords::ALL); + let distinct = self.parse_keyword(AllKeyWords::DISTINCT); if all && distinct { return parser_err!("Cannot specify both ALL and DISTINCT".to_string()); } else { @@ -926,18 +914,18 @@ impl Parser { /// Parse a SQL CREATE statement pub fn parse_create(&mut self) -> Result { - if self.parse_keyword("TABLE") { + if self.parse_keyword(AllKeyWords::TABLE) { self.parse_create_table() - } else if self.parse_keyword("INDEX") { + } else if self.parse_keyword(AllKeyWords::INDEX) { self.parse_create_index(false) - } else if self.parse_keywords(vec!["UNIQUE", "INDEX"]) { + } else if self.parse_keywords(vec![AllKeyWords::UNIQUE, AllKeyWords::INDEX]) { self.parse_create_index(true) - } else if self.parse_keyword("MATERIALIZED") || self.parse_keyword("VIEW") { + } else if self.parse_keyword(AllKeyWords::MATERIALIZED) || self.parse_keyword(AllKeyWords::VIEW) { self.prev_token(); self.parse_create_view() - } else if self.parse_keyword("EXTERNAL") { + } else if self.parse_keyword(AllKeyWords::EXTERNAL) { self.parse_create_external_table() - } else if self.parse_keyword("SCHEMA") { + } else if self.parse_keyword(AllKeyWords::SCHEMA) { self.parse_create_schema() } else { self.expected( @@ -953,13 +941,13 @@ impl Parser { } pub fn parse_create_external_table(&mut self) -> Result { - self.expect_keyword("TABLE")?; + self.expect_keyword(AllKeyWords::TABLE)?; let table_name = self.parse_object_name()?; let (columns, constraints) = self.parse_columns()?; - self.expect_keywords(&["STORED", "AS"])?; + self.expect_keywords(&[AllKeyWords::STORED, AllKeyWords::AS])?; let file_format = self.parse_identifier()?.value.parse::()?; - self.expect_keyword("LOCATION")?; + self.expect_keyword(AllKeyWords::LOCATION)?; let location = self.parse_literal_string()?; Ok(Statement::CreateTable { @@ -975,14 +963,14 @@ impl Parser { } pub fn parse_create_view(&mut self) -> Result { - let materialized = self.parse_keyword("MATERIALIZED"); - self.expect_keyword("VIEW")?; + let materialized = self.parse_keyword(AllKeyWords::MATERIALIZED); + self.expect_keyword(AllKeyWords::VIEW)?; // Many dialects support `OR REPLACE` | `OR ALTER` right after `CREATE`, but we don't (yet). // ANSI SQL and Postgres support RECURSIVE here, but we don't support it either. let name = self.parse_object_name()?; let columns = self.parse_parenthesized_column_list(Optional)?; let with_options = self.parse_with_options()?; - self.expect_keyword("AS")?; + self.expect_keyword(AllKeyWords::AS)?; let query = Box::new(self.parse_query()?); // Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here. Ok(Statement::CreateView { @@ -995,23 +983,23 @@ impl Parser { } pub fn parse_drop(&mut self) -> Result { - let object_type = if self.parse_keyword("TABLE") { + let object_type = if self.parse_keyword(AllKeyWords::TABLE) { ObjectType::Table - } else if self.parse_keyword("VIEW") { + } else if self.parse_keyword(AllKeyWords::VIEW) { ObjectType::View - } else if self.parse_keyword("INDEX") { + } else if self.parse_keyword(AllKeyWords::INDEX) { ObjectType::Index - } else if self.parse_keyword("SCHEMA") { + } else if self.parse_keyword(AllKeyWords::SCHEMA) { ObjectType::Schema } else { return self.expected("TABLE, VIEW, INDEX or SCHEMA after DROP", self.peek_token()); }; // Many dialects support the non standard `IF EXISTS` clause and allow // specifying multiple objects to delete in a single statement - let if_exists = self.parse_keywords(vec!["IF", "EXISTS"]); + let if_exists = self.parse_keywords(vec![AllKeyWords::IF, AllKeyWords::EXISTS]); let names = self.parse_comma_separated(Parser::parse_object_name)?; - let cascade = self.parse_keyword("CASCADE"); - let restrict = self.parse_keyword("RESTRICT"); + let cascade = self.parse_keyword(AllKeyWords::CASCADE); + let restrict = self.parse_keyword(AllKeyWords::RESTRICT); if cascade && restrict { return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP"); } @@ -1024,9 +1012,9 @@ impl Parser { } pub fn parse_create_index(&mut self, unique: bool) -> Result { - let if_not_exists = self.parse_keywords(vec!["IF", "NOT", "EXISTS"]); + let if_not_exists = self.parse_keywords(vec![AllKeyWords::IF, AllKeyWords::NOT, AllKeyWords::EXISTS]); let index_name = self.parse_object_name()?; - self.expect_keyword("ON")?; + self.expect_keyword(AllKeyWords::ON)?; let table_name = self.parse_object_name()?; let columns = self.parse_parenthesized_column_list(Mandatory)?; Ok(Statement::CreateIndex { @@ -1039,7 +1027,7 @@ impl Parser { } pub fn parse_create_table(&mut self) -> Result { - let if_not_exists = self.parse_keywords(vec!["IF", "NOT", "EXISTS"]); + let if_not_exists = self.parse_keywords(vec![AllKeyWords::IF, AllKeyWords::NOT, AllKeyWords::EXISTS]); let table_name = self.parse_object_name()?; // parse optional column list (schema) let (columns, constraints) = self.parse_columns()?; @@ -1070,7 +1058,7 @@ impl Parser { } else if let Some(Token::Word(column_name)) = self.peek_token() { self.next_token(); let data_type = self.parse_data_type()?; - let collation = if self.parse_keyword("COLLATE") { + let collation = if self.parse_keyword(AllKeyWords::COLLATE) { Some(self.parse_object_name()?) } else { None @@ -1105,23 +1093,23 @@ impl Parser { } pub fn parse_column_option_def(&mut self) -> Result { - let name = if self.parse_keyword("CONSTRAINT") { + let name = if self.parse_keyword(AllKeyWords::CONSTRAINT) { Some(self.parse_identifier()?) } else { None }; - let option = if self.parse_keywords(vec!["NOT", "NULL"]) { + let option = if self.parse_keywords(vec![AllKeyWords::NOT, AllKeyWords::NULL]) { ColumnOption::NotNull - } else if self.parse_keyword("NULL") { + } else if self.parse_keyword(AllKeyWords::NULL) { ColumnOption::Null - } else if self.parse_keyword("DEFAULT") { + } else if self.parse_keyword(AllKeyWords::DEFAULT) { ColumnOption::Default(self.parse_expr()?) - } else if self.parse_keywords(vec!["PRIMARY", "KEY"]) { + } else if self.parse_keywords(vec![AllKeyWords::PRIMARY, AllKeyWords::KEY]) { ColumnOption::Unique { is_primary: true } - } else if self.parse_keyword("UNIQUE") { + } else if self.parse_keyword(AllKeyWords::UNIQUE) { ColumnOption::Unique { is_primary: false } - } else if self.parse_keyword("REFERENCES") { + } else if self.parse_keyword(AllKeyWords::REFERENCES) { let foreign_table = self.parse_object_name()?; // PostgreSQL allows omitting the column list and // uses the primary key column of the foreign table by default @@ -1129,9 +1117,9 @@ impl Parser { let mut on_delete = None; let mut on_update = None; loop { - if on_delete.is_none() && self.parse_keywords(vec!["ON", "DELETE"]) { + if on_delete.is_none() && self.parse_keywords(vec![AllKeyWords::ON, AllKeyWords::DELETE]) { on_delete = Some(self.parse_referential_action()?); - } else if on_update.is_none() && self.parse_keywords(vec!["ON", "UPDATE"]) { + } else if on_update.is_none() && self.parse_keywords(vec![AllKeyWords::ON, AllKeyWords::UPDATE]) { on_update = Some(self.parse_referential_action()?); } else { break; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 1f33dd5dc..7f1676227 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -19,7 +19,7 @@ use std::iter::Peekable; use std::str::Chars; -use super::dialect::keywords::ALL_KEYWORDS; +use super::dialect::keywords::{AllKeyWords, ALL_KEYWORDS, ALL_KEYWORDS_INDEX}; use super::dialect::Dialect; use std::fmt; @@ -143,15 +143,15 @@ impl Token { } pub fn make_word(word: &str, quote_style: Option) -> Self { let word_uppercase = word.to_uppercase(); - let is_keyword = - quote_style == None && ALL_KEYWORDS.binary_search(&word_uppercase.as_str()).is_ok(); + let keyword = ALL_KEYWORDS.binary_search(&word_uppercase.as_str()); + let is_keyword = quote_style == None && keyword.is_ok(); Token::Word(Word { value: word.to_string(), quote_style, keyword: if is_keyword { - word_uppercase + Some(ALL_KEYWORDS_INDEX[keyword.unwrap()]) } else { - "".to_string() + None }, }) } @@ -169,7 +169,7 @@ pub struct Word { pub quote_style: Option, /// If the word was not quoted and it matched one of the known keywords, /// this will have one of the values from dialect::keywords, otherwise empty - pub keyword: String, + pub keyword: Option, } impl fmt::Display for Word { From b42e1030ae2f22c0c56b6bbd49006d24cfa0228b Mon Sep 17 00:00:00 2001 From: Dandandan Date: Mon, 8 Jun 2020 17:33:07 +0200 Subject: [PATCH 02/17] Finalize conversion --- src/dialect/keywords.rs | 5 +- src/parser.rs | 569 ++++++++++++++++++++++------------------ src/tokenizer.rs | 2 +- 3 files changed, 320 insertions(+), 256 deletions(-) diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index 0b9d2cc94..48ff28b72 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -48,7 +48,7 @@ macro_rules! define_keywords { } pub const ALL_KEYWORDS_INDEX: &[AllKeyWords] = &[ - AllKeyWords::$($ident),* + $(AllKeyWords::$ident),* ]; $(kw_def!($ident $(= $string_keyword)?);)* @@ -57,7 +57,8 @@ macro_rules! define_keywords { $($ident),* ]; - } + }; + } // The following keywords should be sorted to be able to match using binary search diff --git a/src/parser.rs b/src/parser.rs index b1c3b5b6a..fe32c524e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,7 +15,7 @@ use log::debug; use super::ast::*; -use super::dialect::keywords::{AllKeyWords}; +use super::dialect::keywords::AllKeyWords; use super::dialect::Dialect; use super::tokenizer::*; use std::error::Error; @@ -113,27 +113,29 @@ impl Parser { pub fn parse_statement(&mut self) -> Result { match self.next_token() { Some(t) => match t { - Token::Word(ref w) if w.keyword.is_some() => match w.keyword.unwrap() { - AllKeyWords::SELECT | AllKeyWords::WITH | AllKeyWords::VALUES => { + Token::Word(w) => match w.keyword { + Some(AllKeyWords::SELECT) + | Some(AllKeyWords::WITH) + | Some(AllKeyWords::VALUES) => { self.prev_token(); Ok(Statement::Query(Box::new(self.parse_query()?))) } - AllKeyWords::CREATE => Ok(self.parse_create()?), - AllKeyWords::DROP => Ok(self.parse_drop()?), - AllKeyWords::DELETE => Ok(self.parse_delete()?), - AllKeyWords::INSERT => Ok(self.parse_insert()?), - AllKeyWords::UPDATE => Ok(self.parse_update()?), - AllKeyWords::ALTER => Ok(self.parse_alter()?), - AllKeyWords::COPY => Ok(self.parse_copy()?), - AllKeyWords::SET => Ok(self.parse_set()?), - AllKeyWords::SHOW => Ok(self.parse_show()?), - AllKeyWords::START => Ok(self.parse_start_transaction()?), + Some(AllKeyWords::CREATE) => Ok(self.parse_create()?), + Some(AllKeyWords::DROP) => Ok(self.parse_drop()?), + Some(AllKeyWords::DELETE) => Ok(self.parse_delete()?), + Some(AllKeyWords::INSERT) => Ok(self.parse_insert()?), + Some(AllKeyWords::UPDATE) => Ok(self.parse_update()?), + Some(AllKeyWords::ALTER) => Ok(self.parse_alter()?), + Some(AllKeyWords::COPY) => Ok(self.parse_copy()?), + Some(AllKeyWords::SET) => Ok(self.parse_set()?), + Some(AllKeyWords::SHOW) => Ok(self.parse_show()?), + Some(AllKeyWords::START) => Ok(self.parse_start_transaction()?), // `BEGIN` is a nonstandard but common alias for the // standard `START TRANSACTION` statement. It is supported // by at least PostgreSQL and MySQL. - AllKeyWords::BEGIN => Ok(self.parse_begin()?), - AllKeyWords::COMMIT => Ok(self.parse_commit()?), - AllKeyWords::ROLLBACK => Ok(self.parse_rollback()?), + Some(AllKeyWords::BEGIN) => Ok(self.parse_begin()?), + Some(AllKeyWords::COMMIT) => Ok(self.parse_commit()?), + Some(AllKeyWords::ROLLBACK) => Ok(self.parse_rollback()?), _ => parser_err!(format!( "Unexpected keyword {:?} at the beginning of a statement", w.to_string() @@ -180,24 +182,30 @@ impl Parser { .next_token() .ok_or_else(|| ParserError::ParserError("Unexpected EOF".to_string()))?; let expr = match tok { - Token::Word(w) => match w.keyword.unwrap() { - AllKeyWords::TRUE | AllKeyWords::FALSE | AllKeyWords::NULL => { + Token::Word(ref w) => match w.keyword.clone() { + Some(AllKeyWords::TRUE) | Some(AllKeyWords::FALSE) | Some(AllKeyWords::NULL) => { self.prev_token(); Ok(Expr::Value(self.parse_value()?)) } - AllKeyWords::CASE => self.parse_case_expr(), - AllKeyWords::CAST => self.parse_cast_expr(), - AllKeyWords::DATE => Ok(Expr::Value(Value::Date(self.parse_literal_string()?))), - AllKeyWords::EXISTS => self.parse_exists_expr(), - AllKeyWords::EXTRACT => self.parse_extract_expr(), - AllKeyWords::INTERVAL => self.parse_literal_interval(), - AllKeyWords::LISTAGG => self.parse_listagg_expr(), - AllKeyWords::NOT => Ok(Expr::UnaryOp { + Some(AllKeyWords::CASE) => self.parse_case_expr(), + Some(AllKeyWords::CAST) => self.parse_cast_expr(), + Some(AllKeyWords::DATE) => { + Ok(Expr::Value(Value::Date(self.parse_literal_string()?))) + } + Some(AllKeyWords::EXISTS) => self.parse_exists_expr(), + Some(AllKeyWords::EXTRACT) => self.parse_extract_expr(), + Some(AllKeyWords::INTERVAL) => self.parse_literal_interval(), + Some(AllKeyWords::LISTAGG) => self.parse_listagg_expr(), + Some(AllKeyWords::NOT) => Ok(Expr::UnaryOp { op: UnaryOperator::Not, expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), }), - AllKeyWords::TIME => Ok(Expr::Value(Value::Time(self.parse_literal_string()?))), - AllKeyWords::TIMESTAMP => Ok(Expr::Value(Value::Timestamp(self.parse_literal_string()?))), + Some(AllKeyWords::TIME) => { + Ok(Expr::Value(Value::Time(self.parse_literal_string()?))) + } + Some(AllKeyWords::TIMESTAMP) => { + Ok(Expr::Value(Value::Timestamp(self.parse_literal_string()?))) + } // Here `w` is a word, check if it's a part of a multi-part // identifier, a function call, or a simple identifier: _ => match self.peek_token() { @@ -249,7 +257,9 @@ impl Parser { Ok(Expr::Value(self.parse_value()?)) } Token::LParen => { - let expr = if self.parse_keyword("SELECT") || self.parse_keyword("WITH") { + let expr = if self.parse_keyword(AllKeyWords::SELECT) + || self.parse_keyword(AllKeyWords::WITH) + { self.prev_token(); Expr::Subquery(Box::new(self.parse_query()?)) } else { @@ -261,7 +271,7 @@ impl Parser { unexpected => self.expected("an expression", Some(unexpected)), }?; - if self.parse_keyword("COLLATE") { + if self.parse_keyword(AllKeyWords::COLLATE) { Ok(Expr::Collate { expr: Box::new(expr), collation: self.parse_object_name()?, @@ -275,16 +285,17 @@ impl Parser { self.expect_token(&Token::LParen)?; let distinct = self.parse_all_or_distinct()?; let args = self.parse_optional_args()?; - let over = if self.parse_keyword("OVER") { + let over = if self.parse_keyword(AllKeyWords::OVER) { // TBD: support window names (`OVER mywin`) in place of inline specification self.expect_token(&Token::LParen)?; - let partition_by = if self.parse_keywords(vec!["PARTITION", "BY"]) { + let partition_by = if self.parse_keywords(vec![AllKeyWords::PARTITION, AllKeyWords::BY]) + { // a list of possibly-qualified column names self.parse_comma_separated(Parser::parse_expr)? } else { vec![] }; - let order_by = if self.parse_keywords(vec!["ORDER", "BY"]) { + let order_by = if self.parse_keywords(vec![AllKeyWords::ORDER, AllKeyWords::BY]) { self.parse_comma_separated(Parser::parse_order_by_expr)? } else { vec![] @@ -316,12 +327,12 @@ impl Parser { pub fn parse_window_frame(&mut self) -> Result { let units = match self.next_token() { - Some(Token::Word(w)) => w.keyword.parse::()?, + Some(Token::Word(w)) => w.value.to_ascii_uppercase().parse::()?, unexpected => return self.expected("ROWS, RANGE, GROUPS", unexpected), }; - let (start_bound, end_bound) = if self.parse_keyword("BETWEEN") { + let (start_bound, end_bound) = if self.parse_keyword(AllKeyWords::BETWEEN) { let start_bound = self.parse_window_frame_bound()?; - self.expect_keyword("AND")?; + self.expect_keyword(AllKeyWords::AND)?; let end_bound = Some(self.parse_window_frame_bound()?); (start_bound, end_bound) } else { @@ -336,17 +347,17 @@ impl Parser { /// Parse `CURRENT ROW` or `{ | UNBOUNDED } { PRECEDING | FOLLOWING }` pub fn parse_window_frame_bound(&mut self) -> Result { - if self.parse_keywords(vec!["CURRENT", "ROW"]) { + if self.parse_keywords(vec![AllKeyWords::CURRENT, AllKeyWords::ROW]) { Ok(WindowFrameBound::CurrentRow) } else { - let rows = if self.parse_keyword("UNBOUNDED") { + let rows = if self.parse_keyword(AllKeyWords::UNBOUNDED) { None } else { Some(self.parse_literal_uint()?) }; - if self.parse_keyword("PRECEDING") { + if self.parse_keyword(AllKeyWords::PRECEDING) { Ok(WindowFrameBound::Preceding(rows)) - } else if self.parse_keyword("FOLLOWING") { + } else if self.parse_keyword(AllKeyWords::FOLLOWING) { Ok(WindowFrameBound::Following(rows)) } else { self.expected("PRECEDING or FOLLOWING", self.peek_token()) @@ -356,26 +367,26 @@ impl Parser { pub fn parse_case_expr(&mut self) -> Result { let mut operand = None; - if !self.parse_keyword("WHEN") { + if !self.parse_keyword(AllKeyWords::WHEN) { operand = Some(Box::new(self.parse_expr()?)); - self.expect_keyword("WHEN")?; + self.expect_keyword(AllKeyWords::WHEN)?; } let mut conditions = vec![]; let mut results = vec![]; loop { conditions.push(self.parse_expr()?); - self.expect_keyword("THEN")?; + self.expect_keyword(AllKeyWords::THEN)?; results.push(self.parse_expr()?); - if !self.parse_keyword("WHEN") { + if !self.parse_keyword(AllKeyWords::WHEN) { break; } } - let else_result = if self.parse_keyword("ELSE") { + let else_result = if self.parse_keyword(AllKeyWords::ELSE) { Some(Box::new(self.parse_expr()?)) } else { None }; - self.expect_keyword("END")?; + self.expect_keyword(AllKeyWords::END)?; Ok(Expr::Case { operand, conditions, @@ -388,7 +399,7 @@ impl Parser { pub fn parse_cast_expr(&mut self) -> Result { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; - self.expect_keyword("AS")?; + self.expect_keyword(AllKeyWords::AS)?; let data_type = self.parse_data_type()?; self.expect_token(&Token::RParen)?; Ok(Expr::Cast { @@ -408,7 +419,7 @@ impl Parser { pub fn parse_extract_expr(&mut self) -> Result { self.expect_token(&Token::LParen)?; let field = self.parse_date_time_field()?; - self.expect_keyword("FROM")?; + self.expect_keyword(AllKeyWords::FROM)?; let expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; Ok(Expr::Extract { @@ -429,13 +440,16 @@ impl Parser { } else { None }; - let on_overflow = if self.parse_keywords(vec!["ON", "OVERFLOW"]) { - if self.parse_keyword("ERROR") { + let on_overflow = if self.parse_keywords(vec![AllKeyWords::ON, AllKeyWords::OVERFLOW]) { + if self.parse_keyword(AllKeyWords::ERROR) { Some(ListAggOnOverflow::Error) } else { - self.expect_keyword("TRUNCATE")?; + self.expect_keyword(AllKeyWords::TRUNCATE)?; let filler = match self.peek_token() { - Some(Token::Word(kw)) if kw.keyword == Some(AllKeyWords::WITH) || kw.keyword == Some(AllKeyWords::WITHOUT)=> { + Some(Token::Word(kw)) + if kw.keyword == Some(AllKeyWords::WITH) + || kw.keyword == Some(AllKeyWords::WITHOUT) => + { None } Some(Token::SingleQuotedString(_)) @@ -446,11 +460,11 @@ impl Parser { self.peek_token(), )?, }; - let with_count = self.parse_keyword("WITH"); - if !with_count && !self.parse_keyword("WITHOUT") { + let with_count = self.parse_keyword(AllKeyWords::WITH); + if !with_count && !self.parse_keyword(AllKeyWords::WITHOUT) { self.expected("either WITH or WITHOUT in LISTAGG", self.peek_token())?; } - self.expect_keyword("COUNT")?; + self.expect_keyword(AllKeyWords::COUNT)?; Some(ListAggOnOverflow::Truncate { filler, with_count }) } } else { @@ -459,9 +473,9 @@ impl Parser { self.expect_token(&Token::RParen)?; // Once again ANSI SQL requires WITHIN GROUP, but Redshift does not. Again we choose the // more general implementation. - let within_group = if self.parse_keywords(vec!["WITHIN", "GROUP"]) { + let within_group = if self.parse_keywords(vec![AllKeyWords::WITHIN, AllKeyWords::GROUP]) { self.expect_token(&Token::LParen)?; - self.expect_keywords(&["ORDER", "BY"])?; + self.expect_keywords(&[AllKeyWords::ORDER, AllKeyWords::BY])?; let order_by_expr = self.parse_comma_separated(Parser::parse_order_by_expr)?; self.expect_token(&Token::RParen)?; order_by_expr @@ -484,13 +498,13 @@ impl Parser { pub fn parse_date_time_field(&mut self) -> Result { let tok = self.next_token(); if let Some(Token::Word(ref k)) = tok { - match k.keyword.as_ref() { - "YEAR" => Ok(DateTimeField::Year), - "MONTH" => Ok(DateTimeField::Month), - "DAY" => Ok(DateTimeField::Day), - "HOUR" => Ok(DateTimeField::Hour), - "MINUTE" => Ok(DateTimeField::Minute), - "SECOND" => Ok(DateTimeField::Second), + match k.keyword { + Some(AllKeyWords::YEAR) => Ok(DateTimeField::Year), + Some(AllKeyWords::MONTH) => Ok(DateTimeField::Month), + Some(AllKeyWords::DAY) => Ok(DateTimeField::Day), + Some(AllKeyWords::HOUR) => Ok(DateTimeField::Hour), + Some(AllKeyWords::MINUTE) => Ok(DateTimeField::Minute), + Some(AllKeyWords::SECOND) => Ok(DateTimeField::Second), _ => self.expected("date/time field", tok)?, } } else { @@ -539,7 +553,7 @@ impl Parser { (leading_precision, last_field, fsec_precision) } else { let leading_precision = self.parse_optional_precision()?; - if self.parse_keyword("TO") { + if self.parse_keyword(AllKeyWords::TO) { let last_field = Some(self.parse_date_time_field()?); let fsec_precision = if last_field == Some(DateTimeField::Second) { self.parse_optional_precision()? @@ -586,8 +600,8 @@ impl Parser { Some(AllKeyWords::AND) => Some(BinaryOperator::And), Some(AllKeyWords::OR) => Some(BinaryOperator::Or), Some(AllKeyWords::LIKE) => Some(BinaryOperator::Like), - Some(AllKeyWords::NOT) => { - if self.parse_keyword("LIKE") { + Some(AllKeyWords::NOT) => { + if self.parse_keyword(AllKeyWords::LIKE) { Some(BinaryOperator::NotLike) } else { None @@ -607,9 +621,9 @@ impl Parser { } else if let Token::Word(ref k) = tok { match k.keyword { Some(AllKeyWords::IS) => { - if self.parse_keyword(Some(AllKeyWords::NULL)) { + if self.parse_keyword(AllKeyWords::NULL) { Ok(Expr::IsNull(Box::new(expr))) - } else if self.parse_keywords(vec![Some(AllKeyWords::NOT), Some(AllKeyWords::NULL)]) { + } else if self.parse_keywords(vec![AllKeyWords::NOT, AllKeyWords::NULL]) { Ok(Expr::IsNotNull(Box::new(expr))) } else { self.expected("NULL or NOT NULL after IS", self.peek_token()) @@ -617,10 +631,10 @@ impl Parser { } Some(AllKeyWords::NOT) | Some(AllKeyWords::IN) | Some(AllKeyWords::BETWEEN) => { self.prev_token(); - let negated = self.parse_keyword("NOT"); - if self.parse_keyword("IN") { + let negated = self.parse_keyword(AllKeyWords::NOT); + if self.parse_keyword(AllKeyWords::IN) { self.parse_in(expr, negated) - } else if self.parse_keyword("BETWEEN") { + } else if self.parse_keyword(AllKeyWords::BETWEEN) { self.parse_between(expr, negated) } else { self.expected("IN or BETWEEN after NOT", self.peek_token()) @@ -640,20 +654,21 @@ impl Parser { /// Parses the parens following the `[ NOT ] IN` operator pub fn parse_in(&mut self, expr: Expr, negated: bool) -> Result { self.expect_token(&Token::LParen)?; - let in_op = if self.parse_keyword("SELECT") || self.parse_keyword("WITH") { - self.prev_token(); - Expr::InSubquery { - expr: Box::new(expr), - subquery: Box::new(self.parse_query()?), - negated, - } - } else { - Expr::InList { - expr: Box::new(expr), - list: self.parse_comma_separated(Parser::parse_expr)?, - negated, - } - }; + let in_op = + if self.parse_keyword(AllKeyWords::SELECT) || self.parse_keyword(AllKeyWords::WITH) { + self.prev_token(); + Expr::InSubquery { + expr: Box::new(expr), + subquery: Box::new(self.parse_query()?), + negated, + } + } else { + Expr::InList { + expr: Box::new(expr), + list: self.parse_comma_separated(Parser::parse_expr)?, + negated, + } + }; self.expect_token(&Token::RParen)?; Ok(in_op) } @@ -663,7 +678,7 @@ impl Parser { // Stop parsing subexpressions for and on tokens with // precedence lower than that of `BETWEEN`, such as `AND`, `IS`, etc. let low = self.parse_subexpr(Self::BETWEEN_PREC)?; - self.expect_keyword("AND")?; + self.expect_keyword(AllKeyWords::AND)?; let high = self.parse_subexpr(Self::BETWEEN_PREC)?; Ok(Expr::Between { expr: Box::new(expr), @@ -691,23 +706,31 @@ impl Parser { debug!("get_next_precedence() {:?}", token); match &token { - Token::Word(k) if k.keyword == => Ok(5), - Token::Word(k) if k.keyword == "AND" => Ok(10), - Token::Word(k) if k.keyword == "NOT" => match &self.peek_nth_token(1) { - // The precedence of NOT varies depending on keyword that - // follows it. If it is followed by IN, BETWEEN, or LIKE, - // it takes on the precedence of those tokens. Otherwise it - // is not an infix operator, and therefore has zero - // precedence. - Some(Token::Word(k)) if k.keyword == "IN" => Ok(Self::BETWEEN_PREC), - Some(Token::Word(k)) if k.keyword == "BETWEEN" => Ok(Self::BETWEEN_PREC), - Some(Token::Word(k)) if k.keyword == "LIKE" => Ok(Self::BETWEEN_PREC), - _ => Ok(0), - }, - Token::Word(k) if k.keyword == "IS" => Ok(17), - Token::Word(k) if k.keyword == "IN" => Ok(Self::BETWEEN_PREC), - Token::Word(k) if k.keyword == "BETWEEN" => Ok(Self::BETWEEN_PREC), - Token::Word(k) if k.keyword == "LIKE" => Ok(Self::BETWEEN_PREC), + Token::Word(k) if k.keyword == Some(AllKeyWords::OR) => Ok(5), + Token::Word(k) if k.keyword == Some(AllKeyWords::AND) => Ok(10), + Token::Word(k) if k.keyword == Some(AllKeyWords::NOT) => { + match &self.peek_nth_token(1) { + // The precedence of NOT varies depending on keyword that + // follows it. If it is followed by IN, BETWEEN, or LIKE, + // it takes on the precedence of those tokens. Otherwise it + // is not an infix operator, and therefore has zero + // precedence. + Some(Token::Word(k)) if k.keyword == Some(AllKeyWords::IN) => { + Ok(Self::BETWEEN_PREC) + } + Some(Token::Word(k)) if k.keyword == Some(AllKeyWords::BETWEEN) => { + Ok(Self::BETWEEN_PREC) + } + Some(Token::Word(k)) if k.keyword == Some(AllKeyWords::LIKE) => { + Ok(Self::BETWEEN_PREC) + } + _ => Ok(0), + } + } + Token::Word(k) if k.keyword == Some(AllKeyWords::IS) => Ok(17), + Token::Word(k) if k.keyword == Some(AllKeyWords::IN) => Ok(Self::BETWEEN_PREC), + Token::Word(k) if k.keyword == Some(AllKeyWords::BETWEEN) => Ok(Self::BETWEEN_PREC), + Token::Word(k) if k.keyword == Some(AllKeyWords::LIKE) => Ok(Self::BETWEEN_PREC), Token::Eq | Token::Lt | Token::LtEq | Token::Neq | Token::Gt | Token::GtEq => { Ok(20) } @@ -781,9 +804,9 @@ impl Parser { } /// Report unexpected token - fn expected(&self, expected: AllKeyWords, found: Option) -> Result { + fn expected(&self, expected: &str, found: Option) -> Result { parser_err!(format!( - "Expected {:?}, found: {}", + "Expected {}, found: {}", expected, found.map_or_else(|| "EOF".to_string(), |t| format!("{}", t)) )) @@ -822,10 +845,10 @@ impl Parser { match self.peek_token() { Some(Token::Word(ref k)) => keywords .iter() - .find(|keyword| Some(**keyword) == k.keyword) + .find(|keyword| Some((*keyword).clone()) == k.keyword) .map(|keyword| { self.next_token(); - *keyword + keyword.clone() }), _ => None, } @@ -839,8 +862,9 @@ impl Parser { if let Some(keyword) = self.parse_one_of_keywords(keywords) { Ok(keyword) } else { + let keywords: Vec = keywords.iter().map(|x| format!("{:?}", x)).collect(); self.expected( - &format!("one of {}", keywords.iter().map(|x| format!("{:?}", x)).collect().join(" or ")), + &format!("one of {}", keywords.join(" or ")), self.peek_token(), ) } @@ -848,10 +872,10 @@ impl Parser { /// Bail out if the current token is not an expected keyword, or consume it if it is pub fn expect_keyword(&mut self, expected: AllKeyWords) -> Result<(), ParserError> { - if self.parse_keyword(expected) { + if self.parse_keyword(expected.clone()) { Ok(()) } else { - self.expected(expected, self.peek_token()) + self.expected(format!("{:?}", &expected).as_str(), self.peek_token()) } } @@ -859,7 +883,7 @@ impl Parser { /// keywords, or consume them if they are. pub fn expect_keywords(&mut self, expected: &[AllKeyWords]) -> Result<(), ParserError> { for kw in expected { - self.expect_keyword(*kw)?; + self.expect_keyword(kw.clone())?; } Ok(()) } @@ -920,7 +944,9 @@ impl Parser { self.parse_create_index(false) } else if self.parse_keywords(vec![AllKeyWords::UNIQUE, AllKeyWords::INDEX]) { self.parse_create_index(true) - } else if self.parse_keyword(AllKeyWords::MATERIALIZED) || self.parse_keyword(AllKeyWords::VIEW) { + } else if self.parse_keyword(AllKeyWords::MATERIALIZED) + || self.parse_keyword(AllKeyWords::VIEW) + { self.prev_token(); self.parse_create_view() } else if self.parse_keyword(AllKeyWords::EXTERNAL) { @@ -1012,7 +1038,8 @@ impl Parser { } pub fn parse_create_index(&mut self, unique: bool) -> Result { - let if_not_exists = self.parse_keywords(vec![AllKeyWords::IF, AllKeyWords::NOT, AllKeyWords::EXISTS]); + let if_not_exists = + self.parse_keywords(vec![AllKeyWords::IF, AllKeyWords::NOT, AllKeyWords::EXISTS]); let index_name = self.parse_object_name()?; self.expect_keyword(AllKeyWords::ON)?; let table_name = self.parse_object_name()?; @@ -1027,7 +1054,8 @@ impl Parser { } pub fn parse_create_table(&mut self) -> Result { - let if_not_exists = self.parse_keywords(vec![AllKeyWords::IF, AllKeyWords::NOT, AllKeyWords::EXISTS]); + let if_not_exists = + self.parse_keywords(vec![AllKeyWords::IF, AllKeyWords::NOT, AllKeyWords::EXISTS]); let table_name = self.parse_object_name()?; // parse optional column list (schema) let (columns, constraints) = self.parse_columns()?; @@ -1117,9 +1145,13 @@ impl Parser { let mut on_delete = None; let mut on_update = None; loop { - if on_delete.is_none() && self.parse_keywords(vec![AllKeyWords::ON, AllKeyWords::DELETE]) { + if on_delete.is_none() + && self.parse_keywords(vec![AllKeyWords::ON, AllKeyWords::DELETE]) + { on_delete = Some(self.parse_referential_action()?); - } else if on_update.is_none() && self.parse_keywords(vec![AllKeyWords::ON, AllKeyWords::UPDATE]) { + } else if on_update.is_none() + && self.parse_keywords(vec![AllKeyWords::ON, AllKeyWords::UPDATE]) + { on_update = Some(self.parse_referential_action()?); } else { break; @@ -1131,7 +1163,7 @@ impl Parser { on_delete, on_update, } - } else if self.parse_keyword("CHECK") { + } else if self.parse_keyword(AllKeyWords::CHECK) { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; @@ -1144,15 +1176,15 @@ impl Parser { } pub fn parse_referential_action(&mut self) -> Result { - if self.parse_keyword("RESTRICT") { + if self.parse_keyword(AllKeyWords::RESTRICT) { Ok(ReferentialAction::Restrict) - } else if self.parse_keyword("CASCADE") { + } else if self.parse_keyword(AllKeyWords::CASCADE) { Ok(ReferentialAction::Cascade) - } else if self.parse_keywords(vec!["SET", "NULL"]) { + } else if self.parse_keywords(vec![AllKeyWords::SET, AllKeyWords::NULL]) { Ok(ReferentialAction::SetNull) - } else if self.parse_keywords(vec!["NO", "ACTION"]) { + } else if self.parse_keywords(vec![AllKeyWords::NO, AllKeyWords::ACTION]) { Ok(ReferentialAction::NoAction) - } else if self.parse_keywords(vec!["SET", "DEFAULT"]) { + } else if self.parse_keywords(vec![AllKeyWords::SET, AllKeyWords::DEFAULT]) { Ok(ReferentialAction::SetDefault) } else { self.expected( @@ -1165,16 +1197,19 @@ impl Parser { pub fn parse_optional_table_constraint( &mut self, ) -> Result, ParserError> { - let name = if self.parse_keyword("CONSTRAINT") { + let name = if self.parse_keyword(AllKeyWords::CONSTRAINT) { Some(self.parse_identifier()?) } else { None }; match self.next_token() { - Some(Token::Word(ref k)) if k.keyword == "PRIMARY" || k.keyword == "UNIQUE" => { - let is_primary = k.keyword == "PRIMARY"; + Some(Token::Word(ref k)) + if k.keyword == Some(AllKeyWords::PRIMARY) + || k.keyword == Some(AllKeyWords::UNIQUE) => + { + let is_primary = k.keyword == Some(AllKeyWords::PRIMARY); if is_primary { - self.expect_keyword("KEY")?; + self.expect_keyword(AllKeyWords::KEY)?; } let columns = self.parse_parenthesized_column_list(Mandatory)?; Ok(Some(TableConstraint::Unique { @@ -1183,10 +1218,10 @@ impl Parser { is_primary, })) } - Some(Token::Word(ref k)) if k.keyword == "FOREIGN" => { - self.expect_keyword("KEY")?; + Some(Token::Word(ref k)) if k.keyword == Some(AllKeyWords::FOREIGN) => { + self.expect_keyword(AllKeyWords::KEY)?; let columns = self.parse_parenthesized_column_list(Mandatory)?; - self.expect_keyword("REFERENCES")?; + self.expect_keyword(AllKeyWords::REFERENCES)?; let foreign_table = self.parse_object_name()?; let referred_columns = self.parse_parenthesized_column_list(Mandatory)?; Ok(Some(TableConstraint::ForeignKey { @@ -1196,7 +1231,7 @@ impl Parser { referred_columns, })) } - Some(Token::Word(ref k)) if k.keyword == "CHECK" => { + Some(Token::Word(ref k)) if k.keyword == Some(AllKeyWords::CHECK) => { self.expect_token(&Token::LParen)?; let expr = Box::new(self.parse_expr()?); self.expect_token(&Token::RParen)?; @@ -1214,7 +1249,7 @@ impl Parser { } pub fn parse_with_options(&mut self) -> Result, ParserError> { - if self.parse_keyword("WITH") { + if self.parse_keyword(AllKeyWords::WITH) { self.expect_token(&Token::LParen)?; let options = self.parse_comma_separated(Parser::parse_sql_option)?; self.expect_token(&Token::RParen)?; @@ -1232,10 +1267,10 @@ impl Parser { } pub fn parse_alter(&mut self) -> Result { - self.expect_keyword("TABLE")?; - let _ = self.parse_keyword("ONLY"); + self.expect_keyword(AllKeyWords::TABLE)?; + let _ = self.parse_keyword(AllKeyWords::ONLY); let table_name = self.parse_object_name()?; - let operation = if self.parse_keyword("ADD") { + let operation = if self.parse_keyword(AllKeyWords::ADD) { if let Some(constraint) = self.parse_optional_table_constraint()? { AlterTableOperation::AddConstraint(constraint) } else { @@ -1254,7 +1289,7 @@ impl Parser { pub fn parse_copy(&mut self) -> Result { let table_name = self.parse_object_name()?; let columns = self.parse_parenthesized_column_list(Optional)?; - self.expect_keywords(&["FROM", "STDIN"])?; + self.expect_keywords(&[AllKeyWords::FROM, AllKeyWords::STDIN])?; self.expect_token(&Token::SemiColon)?; let values = self.parse_tsv()?; Ok(Statement::Copy { @@ -1310,12 +1345,12 @@ impl Parser { fn parse_value(&mut self) -> Result { match self.next_token() { Some(t) => match t { - Token::Word(k) => match k.keyword.as_ref() { - "TRUE" => Ok(Value::Boolean(true)), - "FALSE" => Ok(Value::Boolean(false)), - "NULL" => Ok(Value::Null), + Token::Word(k) => match k.keyword { + Some(AllKeyWords::TRUE) => Ok(Value::Boolean(true)), + Some(AllKeyWords::FALSE) => Ok(Value::Boolean(false)), + Some(AllKeyWords::NULL) => Ok(Value::Null), _ => { - return parser_err!(format!("No value parser for keyword {}", k.keyword)); + return parser_err!(format!("No value parser for keyword {:?}", k.keyword)); } }, // The call to n.parse() returns a bigdecimal when the @@ -1367,47 +1402,53 @@ impl Parser { /// Parse a SQL datatype (in the context of a CREATE TABLE statement for example) pub fn parse_data_type(&mut self) -> Result { match self.next_token() { - Some(Token::Word(k)) => match k.keyword.as_ref() { - "BOOLEAN" => Ok(DataType::Boolean), - "FLOAT" => Ok(DataType::Float(self.parse_optional_precision()?)), - "REAL" => Ok(DataType::Real), - "DOUBLE" => { - let _ = self.parse_keyword("PRECISION"); + Some(Token::Word(k)) => match k.keyword { + Some(AllKeyWords::BOOLEAN) => Ok(DataType::Boolean), + Some(AllKeyWords::FLOAT) => Ok(DataType::Float(self.parse_optional_precision()?)), + Some(AllKeyWords::REAL) => Ok(DataType::Real), + Some(AllKeyWords::DOUBLE) => { + let _ = self.parse_keyword(AllKeyWords::PRECISION); Ok(DataType::Double) } - "SMALLINT" => Ok(DataType::SmallInt), - "INT" | "INTEGER" => Ok(DataType::Int), - "BIGINT" => Ok(DataType::BigInt), - "VARCHAR" => Ok(DataType::Varchar(self.parse_optional_precision()?)), - "CHAR" | "CHARACTER" => { - if self.parse_keyword("VARYING") { + Some(AllKeyWords::SMALLINT) => Ok(DataType::SmallInt), + Some(AllKeyWords::INT) | Some(AllKeyWords::INTEGER) => Ok(DataType::Int), + Some(AllKeyWords::BIGINT) => Ok(DataType::BigInt), + Some(AllKeyWords::VARCHAR) => { + Ok(DataType::Varchar(self.parse_optional_precision()?)) + } + Some(AllKeyWords::CHAR) | Some(AllKeyWords::CHARACTER) => { + if self.parse_keyword(AllKeyWords::VARYING) { Ok(DataType::Varchar(self.parse_optional_precision()?)) } else { Ok(DataType::Char(self.parse_optional_precision()?)) } } - "UUID" => Ok(DataType::Uuid), - "DATE" => Ok(DataType::Date), - "TIMESTAMP" => { + Some(AllKeyWords::UUID) => Ok(DataType::Uuid), + Some(AllKeyWords::DATE) => Ok(DataType::Date), + Some(AllKeyWords::TIMESTAMP) => { // TBD: we throw away "with/without timezone" information - if self.parse_keyword("WITH") || self.parse_keyword("WITHOUT") { - self.expect_keywords(&["TIME", "ZONE"])?; + if self.parse_keyword(AllKeyWords::WITH) + || self.parse_keyword(AllKeyWords::WITHOUT) + { + self.expect_keywords(&[AllKeyWords::TIME, AllKeyWords::ZONE])?; } Ok(DataType::Timestamp) } - "TIME" => { + Some(AllKeyWords::TIME) => { // TBD: we throw away "with/without timezone" information - if self.parse_keyword("WITH") || self.parse_keyword("WITHOUT") { - self.expect_keywords(&["TIME", "ZONE"])?; + if self.parse_keyword(AllKeyWords::WITH) + || self.parse_keyword(AllKeyWords::WITHOUT) + { + self.expect_keywords(&[AllKeyWords::TIME, AllKeyWords::ZONE])?; } Ok(DataType::Time) } // Interval types can be followed by a complicated interval // qualifier that we don't currently support. See // parse_interval_literal for a taste. - "INTERVAL" => Ok(DataType::Interval), - "REGCLASS" => Ok(DataType::Regclass), - "TEXT" => { + Some(AllKeyWords::INTERVAL) => Ok(DataType::Interval), + Some(AllKeyWords::REGCLASS) => Ok(DataType::Regclass), + Some(AllKeyWords::TEXT) => { if self.consume_token(&Token::LBracket) { // Note: this is postgresql-specific self.expect_token(&Token::RBracket)?; @@ -1416,8 +1457,10 @@ impl Parser { Ok(DataType::Text) } } - "BYTEA" => Ok(DataType::Bytea), - "NUMERIC" | "DECIMAL" | "DEC" => { + Some(AllKeyWords::BYTEA) => Ok(DataType::Bytea), + Some(AllKeyWords::NUMERIC) + | Some(AllKeyWords::DECIMAL) + | Some(AllKeyWords::DEC) => { let (precision, scale) = self.parse_optional_precision_scale()?; Ok(DataType::Decimal(precision, scale)) } @@ -1438,7 +1481,7 @@ impl Parser { &mut self, reserved_kwds: &[&str], ) -> Result, ParserError> { - let after_as = self.parse_keyword("AS"); + let after_as = self.parse_keyword(AllKeyWords::AS); match self.next_token() { // Accept any identifier after `AS` (though many dialects have restrictions on // keywords that may appear here). If there's no `AS`: don't parse keywords, @@ -1446,7 +1489,7 @@ impl Parser { // (For example, in `FROM t1 JOIN` the `JOIN` will always be parsed as a keyword, // not an alias.) Some(Token::Word(ref w)) - if after_as || !reserved_kwds.contains(&w.keyword.as_str()) => + if after_as || !reserved_kwds.contains(&w.value.to_ascii_uppercase().as_str()) => { Ok(Some(w.to_ident())) } @@ -1491,7 +1534,7 @@ impl Parser { } /// Parse a possibly qualified, possibly quoted identifier, e.g. - /// `foo` or `myschema."table"` + /// `foo` or `myschema.AllKeyWords::table` pub fn parse_object_name(&mut self) -> Result { let mut idents = vec![]; loop { @@ -1555,9 +1598,9 @@ impl Parser { } pub fn parse_delete(&mut self) -> Result { - self.expect_keyword("FROM")?; + self.expect_keyword(AllKeyWords::FROM)?; let table_name = self.parse_object_name()?; - let selection = if self.parse_keyword("WHERE") { + let selection = if self.parse_keyword(AllKeyWords::WHERE) { Some(self.parse_expr()?) } else { None @@ -1574,7 +1617,7 @@ impl Parser { /// by `ORDER BY`. Unlike some other parse_... methods, this one doesn't /// expect the initial keyword to be already consumed pub fn parse_query(&mut self) -> Result { - let ctes = if self.parse_keyword("WITH") { + let ctes = if self.parse_keyword(AllKeyWords::WITH) { // TODO: optional RECURSIVE self.parse_comma_separated(Parser::parse_cte)? } else { @@ -1583,25 +1626,25 @@ impl Parser { let body = self.parse_query_body(0)?; - let order_by = if self.parse_keywords(vec!["ORDER", "BY"]) { + let order_by = if self.parse_keywords(vec![AllKeyWords::ORDER, AllKeyWords::BY]) { self.parse_comma_separated(Parser::parse_order_by_expr)? } else { vec![] }; - let limit = if self.parse_keyword("LIMIT") { + let limit = if self.parse_keyword(AllKeyWords::LIMIT) { self.parse_limit()? } else { None }; - let offset = if self.parse_keyword("OFFSET") { + let offset = if self.parse_keyword(AllKeyWords::OFFSET) { Some(self.parse_offset()?) } else { None }; - let fetch = if self.parse_keyword("FETCH") { + let fetch = if self.parse_keyword(AllKeyWords::FETCH) { Some(self.parse_fetch()?) } else { None @@ -1623,7 +1666,7 @@ impl Parser { name: self.parse_identifier()?, columns: self.parse_parenthesized_column_list(Optional)?, }; - self.expect_keyword("AS")?; + self.expect_keyword(AllKeyWords::AS)?; self.expect_token(&Token::LParen)?; let query = self.parse_query()?; self.expect_token(&Token::RParen)?; @@ -1641,14 +1684,14 @@ impl Parser { fn parse_query_body(&mut self, precedence: u8) -> Result { // We parse the expression using a Pratt parser, as in `parse_expr()`. // Start by parsing a restricted SELECT or a `(subquery)`: - let mut expr = if self.parse_keyword("SELECT") { + let mut expr = if self.parse_keyword(AllKeyWords::SELECT) { SetExpr::Select(Box::new(self.parse_select()?)) } else if self.consume_token(&Token::LParen) { // CTEs are not allowed here, but the parser currently accepts them let subquery = self.parse_query()?; self.expect_token(&Token::RParen)?; SetExpr::Query(Box::new(subquery)) - } else if self.parse_keyword("VALUES") { + } else if self.parse_keyword(AllKeyWords::VALUES) { SetExpr::Values(self.parse_values()?) } else { return self.expected( @@ -1676,7 +1719,7 @@ impl Parser { expr = SetExpr::SetOperation { left: Box::new(expr), op: op.unwrap(), - all: self.parse_keyword("ALL"), + all: self.parse_keyword(AllKeyWords::ALL), right: Box::new(self.parse_query_body(next_precedence)?), }; } @@ -1686,9 +1729,15 @@ impl Parser { fn parse_set_operator(&mut self, token: &Option) -> Option { match token { - Some(Token::Word(w)) if w.keyword == "UNION" => Some(SetOperator::Union), - Some(Token::Word(w)) if w.keyword == "EXCEPT" => Some(SetOperator::Except), - Some(Token::Word(w)) if w.keyword == "INTERSECT" => Some(SetOperator::Intersect), + Some(Token::Word(w)) if w.keyword == Some(AllKeyWords::UNION) => { + Some(SetOperator::Union) + } + Some(Token::Word(w)) if w.keyword == Some(AllKeyWords::EXCEPT) => { + Some(SetOperator::Except) + } + Some(Token::Word(w)) if w.keyword == Some(AllKeyWords::INTERSECT) => { + Some(SetOperator::Intersect) + } _ => None, } } @@ -1698,7 +1747,7 @@ impl Parser { pub fn parse_select(&mut self) -> Result { let distinct = self.parse_all_or_distinct()?; - let top = if self.parse_keyword("TOP") { + let top = if self.parse_keyword(AllKeyWords::TOP) { Some(self.parse_top()?) } else { None @@ -1711,25 +1760,25 @@ impl Parser { // otherwise they may be parsed as an alias as part of the `projection` // or `from`. - let from = if self.parse_keyword("FROM") { + let from = if self.parse_keyword(AllKeyWords::FROM) { self.parse_comma_separated(Parser::parse_table_and_joins)? } else { vec![] }; - let selection = if self.parse_keyword("WHERE") { + let selection = if self.parse_keyword(AllKeyWords::WHERE) { Some(self.parse_expr()?) } else { None }; - let group_by = if self.parse_keywords(vec!["GROUP", "BY"]) { + let group_by = if self.parse_keywords(vec![AllKeyWords::GROUP, AllKeyWords::BY]) { self.parse_comma_separated(Parser::parse_expr)? } else { vec![] }; - let having = if self.parse_keyword("HAVING") { + let having = if self.parse_keyword(AllKeyWords::HAVING) { Some(self.parse_expr()?) } else { None @@ -1747,9 +1796,9 @@ impl Parser { } pub fn parse_set(&mut self) -> Result { - let modifier = self.parse_one_of_keywords(&["SESSION", "LOCAL"]); + let modifier = self.parse_one_of_keywords(&[AllKeyWords::SESSION, AllKeyWords::LOCAL]); let variable = self.parse_identifier()?; - if self.consume_token(&Token::Eq) || self.parse_keyword("TO") { + if self.consume_token(&Token::Eq) || self.parse_keyword(AllKeyWords::TO) { let token = self.peek_token(); let value = match (self.parse_value(), token) { (Ok(value), _) => SetVariableValue::Literal(value), @@ -1757,7 +1806,7 @@ impl Parser { (Err(_), other) => self.expected("variable value", other)?, }; Ok(Statement::SetVariable { - local: modifier == Some("LOCAL"), + local: modifier == Some(AllKeyWords::LOCAL), variable, value, }) @@ -1772,7 +1821,12 @@ impl Parser { pub fn parse_show(&mut self) -> Result { if self - .parse_one_of_keywords(&["EXTENDED", "FULL", "COLUMNS", "FIELDS"]) + .parse_one_of_keywords(&[ + AllKeyWords::EXTENDED, + AllKeyWords::FULL, + AllKeyWords::COLUMNS, + AllKeyWords::FIELDS, + ]) .is_some() { self.prev_token(); @@ -1785,10 +1839,10 @@ impl Parser { } fn parse_show_columns(&mut self) -> Result { - let extended = self.parse_keyword("EXTENDED"); - let full = self.parse_keyword("FULL"); - self.expect_one_of_keywords(&["COLUMNS", "FIELDS"])?; - self.expect_one_of_keywords(&["FROM", "IN"])?; + let extended = self.parse_keyword(AllKeyWords::EXTENDED); + let full = self.parse_keyword(AllKeyWords::FULL); + self.expect_one_of_keywords(&[AllKeyWords::COLUMNS, AllKeyWords::FIELDS])?; + self.expect_one_of_keywords(&[AllKeyWords::FROM, AllKeyWords::IN])?; let table_name = self.parse_object_name()?; // MySQL also supports FROM here. In other words, MySQL // allows both FROM FROM and FROM .
, @@ -1803,11 +1857,11 @@ impl Parser { } fn parse_show_statement_filter(&mut self) -> Result, ParserError> { - if self.parse_keyword("LIKE") { + if self.parse_keyword(AllKeyWords::LIKE) { Ok(Some(ShowStatementFilter::Like( self.parse_literal_string()?, ))) - } else if self.parse_keyword("WHERE") { + } else if self.parse_keyword(AllKeyWords::WHERE) { Ok(Some(ShowStatementFilter::Where(self.parse_expr()?))) } else { Ok(None) @@ -1822,10 +1876,10 @@ impl Parser { // a table alias. let mut joins = vec![]; loop { - let join = if self.parse_keyword("CROSS") { - let join_operator = if self.parse_keyword("JOIN") { + let join = if self.parse_keyword(AllKeyWords::CROSS) { + let join_operator = if self.parse_keyword(AllKeyWords::JOIN) { JoinOperator::CrossJoin - } else if self.parse_keyword("APPLY") { + } else if self.parse_keyword(AllKeyWords::APPLY) { // MSSQL extension, similar to CROSS JOIN LATERAL JoinOperator::CrossApply } else { @@ -1835,39 +1889,43 @@ impl Parser { relation: self.parse_table_factor()?, join_operator, } - } else if self.parse_keyword("OUTER") { + } else if self.parse_keyword(AllKeyWords::OUTER) { // MSSQL extension, similar to LEFT JOIN LATERAL .. ON 1=1 - self.expect_keyword("APPLY")?; + self.expect_keyword(AllKeyWords::APPLY)?; Join { relation: self.parse_table_factor()?, join_operator: JoinOperator::OuterApply, } } else { - let natural = self.parse_keyword("NATURAL"); + let natural = self.parse_keyword(AllKeyWords::NATURAL); let peek_keyword = if let Some(Token::Word(kw)) = self.peek_token() { kw.keyword } else { - String::default() + None }; let join_operator_type = match peek_keyword.as_ref() { - "INNER" | "JOIN" => { - let _ = self.parse_keyword("INNER"); - self.expect_keyword("JOIN")?; + Some(AllKeyWords::INNER) | Some(AllKeyWords::JOIN) => { + let _ = self.parse_keyword(AllKeyWords::INNER); + self.expect_keyword(AllKeyWords::JOIN)?; JoinOperator::Inner } - kw @ "LEFT" | kw @ "RIGHT" | kw @ "FULL" => { + kw @ Some(AllKeyWords::LEFT) + | kw @ Some(AllKeyWords::RIGHT) + | kw @ Some(AllKeyWords::FULL) => { let _ = self.next_token(); - let _ = self.parse_keyword("OUTER"); - self.expect_keyword("JOIN")?; + let _ = self.parse_keyword(AllKeyWords::OUTER); + self.expect_keyword(AllKeyWords::JOIN)?; match kw { - "LEFT" => JoinOperator::LeftOuter, - "RIGHT" => JoinOperator::RightOuter, - "FULL" => JoinOperator::FullOuter, + Some(AllKeyWords::LEFT) => JoinOperator::LeftOuter, + Some(AllKeyWords::RIGHT) => JoinOperator::RightOuter, + Some(AllKeyWords::FULL) => JoinOperator::FullOuter, _ => unreachable!(), } } - "OUTER" => return self.expected("LEFT, RIGHT, or FULL", self.peek_token()), + Some(AllKeyWords::OUTER) => { + return self.expected("LEFT, RIGHT, or FULL", self.peek_token()) + } _ if natural => { return self.expected("a join type after NATURAL", self.peek_token()); } @@ -1887,7 +1945,7 @@ impl Parser { /// A table name or a parenthesized subquery, followed by optional `[AS] alias` pub fn parse_table_factor(&mut self) -> Result { - if self.parse_keyword("LATERAL") { + if self.parse_keyword(AllKeyWords::LATERAL) { // LATERAL must always be followed by a subquery. if !self.consume_token(&Token::LParen) { self.expected("subquery after LATERAL", self.peek_token())?; @@ -1945,10 +2003,10 @@ impl Parser { } else { vec![] }; - let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; + let alias = self.parse_optional_table_alias(&["RESERVED_FOR_TABLE_ALIAS"])?; // MSSQL-specific table hints: let mut with_hints = vec![]; - if self.parse_keyword("WITH") { + if self.parse_keyword(AllKeyWords::WITH) { if self.consume_token(&Token::LParen) { with_hints = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; @@ -1972,7 +2030,7 @@ impl Parser { ) -> Result { let subquery = Box::new(self.parse_query()?); self.expect_token(&Token::RParen)?; - let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; + let alias = self.parse_optional_table_alias(&["RESERVED_FOR_TABLE_ALIAS"])?; Ok(TableFactor::Derived { lateral: match lateral { Lateral => true, @@ -1986,10 +2044,10 @@ impl Parser { fn parse_join_constraint(&mut self, natural: bool) -> Result { if natural { Ok(JoinConstraint::Natural) - } else if self.parse_keyword("ON") { + } else if self.parse_keyword(AllKeyWords::ON) { let constraint = self.parse_expr()?; Ok(JoinConstraint::On(constraint)) - } else if self.parse_keyword("USING") { + } else if self.parse_keyword(AllKeyWords::USING) { let columns = self.parse_parenthesized_column_list(Mandatory)?; Ok(JoinConstraint::Using(columns)) } else { @@ -1999,7 +2057,7 @@ impl Parser { /// Parse an INSERT statement pub fn parse_insert(&mut self) -> Result { - self.expect_keyword("INTO")?; + self.expect_keyword(AllKeyWords::INTO)?; let table_name = self.parse_object_name()?; let columns = self.parse_parenthesized_column_list(Optional)?; let source = Box::new(self.parse_query()?); @@ -2012,9 +2070,9 @@ impl Parser { pub fn parse_update(&mut self) -> Result { let table_name = self.parse_object_name()?; - self.expect_keyword("SET")?; + self.expect_keyword(AllKeyWords::SET)?; let assignments = self.parse_comma_separated(Parser::parse_assignment)?; - let selection = if self.parse_keyword("WHERE") { + let selection = if self.parse_keyword(AllKeyWords::WHERE) { Some(self.parse_expr()?) } else { None @@ -2053,7 +2111,7 @@ impl Parser { Ok(SelectItem::QualifiedWildcard(ObjectName(prefix))) } else { // `expr` is a regular SQL expression and can be followed by an alias - if let Some(alias) = self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)? { + if let Some(alias) = self.parse_optional_alias(&["RESERVED_FOR_COLUMN_ALIAS"])? { Ok(SelectItem::ExprWithAlias { expr, alias }) } else { Ok(SelectItem::UnnamedExpr(expr)) @@ -2065,17 +2123,17 @@ impl Parser { pub fn parse_order_by_expr(&mut self) -> Result { let expr = self.parse_expr()?; - let asc = if self.parse_keyword("ASC") { + let asc = if self.parse_keyword(AllKeyWords::ASC) { Some(true) - } else if self.parse_keyword("DESC") { + } else if self.parse_keyword(AllKeyWords::DESC) { Some(false) } else { None }; - let nulls_first = if self.parse_keywords(vec!["NULLS", "FIRST"]) { + let nulls_first = if self.parse_keywords(vec![AllKeyWords::NULLS, AllKeyWords::FIRST]) { Some(true) - } else if self.parse_keywords(vec!["NULLS", "LAST"]) { + } else if self.parse_keywords(vec![AllKeyWords::NULLS, AllKeyWords::LAST]) { Some(false) } else { None @@ -2099,9 +2157,9 @@ impl Parser { Some(Expr::Value(self.parse_number_value()?)) }; - let percent = self.parse_keyword("PERCENT"); + let percent = self.parse_keyword(AllKeyWords::PERCENT); - let with_ties = self.parse_keywords(vec!["WITH", "TIES"]); + let with_ties = self.parse_keywords(vec![AllKeyWords::WITH, AllKeyWords::TIES]); Ok(Top { with_ties, @@ -2112,7 +2170,7 @@ impl Parser { /// Parse a LIMIT clause pub fn parse_limit(&mut self) -> Result, ParserError> { - if self.parse_keyword("ALL") { + if self.parse_keyword(AllKeyWords::ALL) { Ok(None) } else { Ok(Some(Expr::Value(self.parse_number_value()?))) @@ -2122,9 +2180,9 @@ impl Parser { /// Parse an OFFSET clause pub fn parse_offset(&mut self) -> Result { let value = Expr::Value(self.parse_number_value()?); - let rows = if self.parse_keyword("ROW") { + let rows = if self.parse_keyword(AllKeyWords::ROW) { OffsetRows::Row - } else if self.parse_keyword("ROWS") { + } else if self.parse_keyword(AllKeyWords::ROWS) { OffsetRows::Rows } else { OffsetRows::None @@ -2134,18 +2192,21 @@ impl Parser { /// Parse a FETCH clause pub fn parse_fetch(&mut self) -> Result { - self.expect_one_of_keywords(&["FIRST", "NEXT"])?; - let (quantity, percent) = if self.parse_one_of_keywords(&["ROW", "ROWS"]).is_some() { + self.expect_one_of_keywords(&[AllKeyWords::FIRST, AllKeyWords::NEXT])?; + let (quantity, percent) = if self + .parse_one_of_keywords(&[AllKeyWords::ROW, AllKeyWords::ROWS]) + .is_some() + { (None, false) } else { let quantity = Expr::Value(self.parse_value()?); - let percent = self.parse_keyword("PERCENT"); - self.expect_one_of_keywords(&["ROW", "ROWS"])?; + let percent = self.parse_keyword(AllKeyWords::PERCENT); + self.expect_one_of_keywords(&[AllKeyWords::ROW, AllKeyWords::ROWS])?; (Some(quantity), percent) }; - let with_ties = if self.parse_keyword("ONLY") { + let with_ties = if self.parse_keyword(AllKeyWords::ONLY) { false - } else if self.parse_keywords(vec!["WITH", "TIES"]) { + } else if self.parse_keywords(vec![AllKeyWords::WITH, AllKeyWords::TIES]) { true } else { return self.expected("one of ONLY or WITH TIES", self.peek_token()); @@ -2168,14 +2229,14 @@ impl Parser { } pub fn parse_start_transaction(&mut self) -> Result { - self.expect_keyword("TRANSACTION")?; + self.expect_keyword(AllKeyWords::TRANSACTION)?; Ok(Statement::StartTransaction { modes: self.parse_transaction_modes()?, }) } pub fn parse_begin(&mut self) -> Result { - let _ = self.parse_one_of_keywords(&["TRANSACTION", "WORK"]); + let _ = self.parse_one_of_keywords(&[AllKeyWords::TRANSACTION, AllKeyWords::WORK]); Ok(Statement::StartTransaction { modes: self.parse_transaction_modes()?, }) @@ -2185,22 +2246,24 @@ impl Parser { let mut modes = vec![]; let mut required = false; loop { - let mode = if self.parse_keywords(vec!["ISOLATION", "LEVEL"]) { - let iso_level = if self.parse_keywords(vec!["READ", "UNCOMMITTED"]) { + let mode = if self.parse_keywords(vec![AllKeyWords::ISOLATION, AllKeyWords::LEVEL]) { + let iso_level = if self + .parse_keywords(vec![AllKeyWords::READ, AllKeyWords::UNCOMMITTED]) + { TransactionIsolationLevel::ReadUncommitted - } else if self.parse_keywords(vec!["READ", "COMMITTED"]) { + } else if self.parse_keywords(vec![AllKeyWords::READ, AllKeyWords::COMMITTED]) { TransactionIsolationLevel::ReadCommitted - } else if self.parse_keywords(vec!["REPEATABLE", "READ"]) { + } else if self.parse_keywords(vec![AllKeyWords::REPEATABLE, AllKeyWords::READ]) { TransactionIsolationLevel::RepeatableRead - } else if self.parse_keyword("SERIALIZABLE") { + } else if self.parse_keyword(AllKeyWords::SERIALIZABLE) { TransactionIsolationLevel::Serializable } else { self.expected("isolation level", self.peek_token())? }; TransactionMode::IsolationLevel(iso_level) - } else if self.parse_keywords(vec!["READ", "ONLY"]) { + } else if self.parse_keywords(vec![AllKeyWords::READ, AllKeyWords::ONLY]) { TransactionMode::AccessMode(TransactionAccessMode::ReadOnly) - } else if self.parse_keywords(vec!["READ", "WRITE"]) { + } else if self.parse_keywords(vec![AllKeyWords::READ, AllKeyWords::WRITE]) { TransactionMode::AccessMode(TransactionAccessMode::ReadWrite) } else if required { self.expected("transaction mode", self.peek_token())? @@ -2230,10 +2293,10 @@ impl Parser { } pub fn parse_commit_rollback_chain(&mut self) -> Result { - let _ = self.parse_one_of_keywords(&["TRANSACTION", "WORK"]); - if self.parse_keyword("AND") { - let chain = !self.parse_keyword("NO"); - self.expect_keyword("CHAIN")?; + let _ = self.parse_one_of_keywords(&[AllKeyWords::TRANSACTION, AllKeyWords::WORK]); + if self.parse_keyword(AllKeyWords::AND) { + let chain = !self.parse_keyword(AllKeyWords::NO); + self.expect_keyword(AllKeyWords::CHAIN)?; Ok(chain) } else { Ok(false) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 7f1676227..1e9c98663 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -149,7 +149,7 @@ impl Token { value: word.to_string(), quote_style, keyword: if is_keyword { - Some(ALL_KEYWORDS_INDEX[keyword.unwrap()]) + keyword.map(|x| ALL_KEYWORDS_INDEX[x].clone()).ok() } else { None }, From 547a6da07a5f6ea1782050484e8883d3f41a490c Mon Sep 17 00:00:00 2001 From: Dandandan Date: Mon, 8 Jun 2020 17:40:23 +0200 Subject: [PATCH 03/17] Fix RESERVED_FOR_COLUMN_ALIAS --- src/parser.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index fe32c524e..48d222ba1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,6 +15,7 @@ use log::debug; use super::ast::*; +use super::dialect::keywords; use super::dialect::keywords::AllKeyWords; use super::dialect::Dialect; use super::tokenizer::*; @@ -2003,7 +2004,7 @@ impl Parser { } else { vec![] }; - let alias = self.parse_optional_table_alias(&["RESERVED_FOR_TABLE_ALIAS"])?; + let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; // MSSQL-specific table hints: let mut with_hints = vec![]; if self.parse_keyword(AllKeyWords::WITH) { @@ -2030,7 +2031,7 @@ impl Parser { ) -> Result { let subquery = Box::new(self.parse_query()?); self.expect_token(&Token::RParen)?; - let alias = self.parse_optional_table_alias(&["RESERVED_FOR_TABLE_ALIAS"])?; + let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; Ok(TableFactor::Derived { lateral: match lateral { Lateral => true, @@ -2111,7 +2112,7 @@ impl Parser { Ok(SelectItem::QualifiedWildcard(ObjectName(prefix))) } else { // `expr` is a regular SQL expression and can be followed by an alias - if let Some(alias) = self.parse_optional_alias(&["RESERVED_FOR_COLUMN_ALIAS"])? { + if let Some(alias) = self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)? { Ok(SelectItem::ExprWithAlias { expr, alias }) } else { Ok(SelectItem::UnnamedExpr(expr)) From 582ca8628eeaf6edc16de71cd0579e8de7ec9407 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Mon, 8 Jun 2020 18:07:19 +0200 Subject: [PATCH 04/17] Fix last tests --- src/dialect/keywords.rs | 14 +++++++------- src/parser.rs | 9 ++++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index 48ff28b72..065f5a652 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -446,20 +446,20 @@ define_keywords!( /// These keywords can't be used as a table alias, so that `FROM table_name alias` /// can be parsed unambiguously without looking ahead. -pub const RESERVED_FOR_TABLE_ALIAS: &[&str] = &[ +pub const RESERVED_FOR_TABLE_ALIAS: &[AllKeyWords] = &[ // Reserved as both a table and a column alias: - WITH, SELECT, WHERE, GROUP, HAVING, ORDER, TOP, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT, + AllKeyWords::WITH, AllKeyWords::SELECT, AllKeyWords::WHERE, AllKeyWords::GROUP, AllKeyWords::HAVING, AllKeyWords::ORDER, AllKeyWords::TOP, AllKeyWords::LIMIT, AllKeyWords::OFFSET, AllKeyWords::FETCH, AllKeyWords::UNION, AllKeyWords::EXCEPT, AllKeyWords::INTERSECT, // Reserved only as a table alias in the `FROM`/`JOIN` clauses: - ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING, + AllKeyWords::ON, AllKeyWords::JOIN, AllKeyWords::INNER, AllKeyWords::CROSS, AllKeyWords::FULL, AllKeyWords::LEFT, AllKeyWords::RIGHT, AllKeyWords::NATURAL, AllKeyWords::USING, // for MSSQL-specific OUTER APPLY (seems reserved in most dialects) - OUTER, + AllKeyWords::OUTER, ]; /// Can't be used as a column alias, so that `SELECT alias` /// can be parsed unambiguously without looking ahead. -pub const RESERVED_FOR_COLUMN_ALIAS: &[&str] = &[ +pub const RESERVED_FOR_COLUMN_ALIAS: &[AllKeyWords] = &[ // Reserved as both a table and a column alias: - WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT, + AllKeyWords::WITH, AllKeyWords::SELECT, AllKeyWords::WHERE, AllKeyWords::GROUP, AllKeyWords::HAVING, AllKeyWords::ORDER, AllKeyWords::LIMIT, AllKeyWords::OFFSET, AllKeyWords::FETCH, AllKeyWords::UNION, AllKeyWords::EXCEPT, AllKeyWords::INTERSECT, // Reserved only as a column alias in the `SELECT` clause: - FROM, + AllKeyWords::FROM, ]; diff --git a/src/parser.rs b/src/parser.rs index 48d222ba1..debd21a35 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1480,7 +1480,7 @@ impl Parser { /// `SELECT ... FROM t1 foo, t2 bar`, `SELECT ... FROM (...) AS bar` pub fn parse_optional_alias( &mut self, - reserved_kwds: &[&str], + reserved_kwds: &[AllKeyWords], ) -> Result, ParserError> { let after_as = self.parse_keyword(AllKeyWords::AS); match self.next_token() { @@ -1490,7 +1490,10 @@ impl Parser { // (For example, in `FROM t1 JOIN` the `JOIN` will always be parsed as a keyword, // not an alias.) Some(Token::Word(ref w)) - if after_as || !reserved_kwds.contains(&w.value.to_ascii_uppercase().as_str()) => + if after_as + || w.keyword + .clone() + .map_or(true, |kw| !reserved_kwds.contains(&kw)) => { Ok(Some(w.to_ident())) } @@ -1523,7 +1526,7 @@ impl Parser { /// addition to the table itself. pub fn parse_optional_table_alias( &mut self, - reserved_kwds: &[&str], + reserved_kwds: &[AllKeyWords], ) -> Result, ParserError> { match self.parse_optional_alias(reserved_kwds)? { Some(name) => { From 46f88c5e48605de768e3d3c7a35a837450512583 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Mon, 8 Jun 2020 23:32:42 +0200 Subject: [PATCH 05/17] Handle other tables in same way --- src/dialect/keywords.rs | 48 ++++++++++++++++++++++++++++++--------- src/parser.rs | 2 +- tests/sqlparser_common.rs | 18 +++++++++++++-- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index 065f5a652..7805eaa63 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -35,14 +35,15 @@ macro_rules! kw_def { }; } + /// Expands to a list of `kw_def!()` invocations for each keyword /// and defines an ALL_KEYWORDS array of the defined constants. macro_rules! define_keywords { ($( $ident:ident $(= $string_keyword:expr)? ),*) => { - - #[derive(Debug, Clone, PartialEq)] + #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] + #[allow(non_camel_case_types)] pub enum AllKeyWords { $($ident),* } @@ -52,7 +53,6 @@ macro_rules! define_keywords { ]; $(kw_def!($ident $(= $string_keyword)?);)* - pub const ALL_KEYWORDS: &[&str] = &[ $($ident),* ]; @@ -447,19 +447,45 @@ define_keywords!( /// These keywords can't be used as a table alias, so that `FROM table_name alias` /// can be parsed unambiguously without looking ahead. pub const RESERVED_FOR_TABLE_ALIAS: &[AllKeyWords] = &[ - // Reserved as both a table and a column alias: - AllKeyWords::WITH, AllKeyWords::SELECT, AllKeyWords::WHERE, AllKeyWords::GROUP, AllKeyWords::HAVING, AllKeyWords::ORDER, AllKeyWords::TOP, AllKeyWords::LIMIT, AllKeyWords::OFFSET, AllKeyWords::FETCH, AllKeyWords::UNION, AllKeyWords::EXCEPT, AllKeyWords::INTERSECT, - // Reserved only as a table alias in the `FROM`/`JOIN` clauses: - AllKeyWords::ON, AllKeyWords::JOIN, AllKeyWords::INNER, AllKeyWords::CROSS, AllKeyWords::FULL, AllKeyWords::LEFT, AllKeyWords::RIGHT, AllKeyWords::NATURAL, AllKeyWords::USING, - // for MSSQL-specific OUTER APPLY (seems reserved in most dialects) + AllKeyWords::CROSS, + AllKeyWords::EXCEPT, + AllKeyWords::FETCH, + AllKeyWords::FULL, + AllKeyWords::GROUP, + AllKeyWords::HAVING, + AllKeyWords::INNER, + AllKeyWords::INTERSECT, + AllKeyWords::JOIN, + AllKeyWords::LEFT, + AllKeyWords::LIMIT, + AllKeyWords::NATURAL, + AllKeyWords::OFFSET, + AllKeyWords::ON, + AllKeyWords::ORDER, AllKeyWords::OUTER, + AllKeyWords::RIGHT, + AllKeyWords::SELECT, + AllKeyWords::TOP, + AllKeyWords::UNION, + AllKeyWords::USING, + AllKeyWords::WHERE, + AllKeyWords::WITH, ]; /// Can't be used as a column alias, so that `SELECT alias` /// can be parsed unambiguously without looking ahead. pub const RESERVED_FOR_COLUMN_ALIAS: &[AllKeyWords] = &[ - // Reserved as both a table and a column alias: - AllKeyWords::WITH, AllKeyWords::SELECT, AllKeyWords::WHERE, AllKeyWords::GROUP, AllKeyWords::HAVING, AllKeyWords::ORDER, AllKeyWords::LIMIT, AllKeyWords::OFFSET, AllKeyWords::FETCH, AllKeyWords::UNION, AllKeyWords::EXCEPT, AllKeyWords::INTERSECT, - // Reserved only as a column alias in the `SELECT` clause: + AllKeyWords::EXCEPT, + AllKeyWords::FETCH, AllKeyWords::FROM, + AllKeyWords::GROUP, + AllKeyWords::HAVING, + AllKeyWords::INTERSECT, + AllKeyWords::LIMIT, + AllKeyWords::OFFSET, + AllKeyWords::ORDER, + AllKeyWords::SELECT, + AllKeyWords::UNION, + AllKeyWords::WHERE, + AllKeyWords::WITH, ]; diff --git a/src/parser.rs b/src/parser.rs index debd21a35..080280999 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1493,7 +1493,7 @@ impl Parser { if after_as || w.keyword .clone() - .map_or(true, |kw| !reserved_kwds.contains(&kw)) => + .map_or(true, |kw| !reserved_kwds.binary_search(&kw).is_ok()) => { Ok(Some(w.to_ident())) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a63d3d49f..04bcb9b92 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -21,7 +21,7 @@ use matches::assert_matches; use sqlparser::ast::*; -use sqlparser::dialect::keywords::ALL_KEYWORDS; +use sqlparser::dialect::keywords::*; use sqlparser::parser::*; use sqlparser::test_utils::{all_dialects, expr_from_projection, number, only}; @@ -2853,13 +2853,27 @@ fn parse_drop_index() { } #[test] -fn keywords_sorted() { +fn all_keywords_sorted() { // assert!(ALL_KEYWORDS.is_sorted()) let mut copy = Vec::from(ALL_KEYWORDS); copy.sort(); assert!(copy == ALL_KEYWORDS) } +#[test] +fn keywords_sorted_reserved_for_column_alias() { + let mut copy = Vec::from(RESERVED_FOR_COLUMN_ALIAS.clone()); + copy.sort(); + assert!(copy == RESERVED_FOR_COLUMN_ALIAS) +} + +#[test] +fn keywords_sorted_reserved_for_table_alias() { + let mut copy = Vec::from(RESERVED_FOR_TABLE_ALIAS.clone()); + copy.sort(); + assert!(copy == RESERVED_FOR_TABLE_ALIAS) +} + fn parse_sql_statements(sql: &str) -> Result, ParserError> { all_dialects().parse_sql_statements(sql) } From 6afcbebb643f07bf22b5e0cabb4cb612a74274e7 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Mon, 8 Jun 2020 23:37:49 +0200 Subject: [PATCH 06/17] Fix style issue --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 080280999..910cf2115 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1493,7 +1493,7 @@ impl Parser { if after_as || w.keyword .clone() - .map_or(true, |kw| !reserved_kwds.binary_search(&kw).is_ok()) => + .map_or(true, |kw| reserved_kwds.binary_search(&kw).is_err()) => { Ok(Some(w.to_ident())) } From 2f851802188727c8053e65c071532fe5db001205 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Mon, 8 Jun 2020 23:40:34 +0200 Subject: [PATCH 07/17] Fix style issue --- tests/sqlparser_common.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 04bcb9b92..d05685762 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2862,14 +2862,14 @@ fn all_keywords_sorted() { #[test] fn keywords_sorted_reserved_for_column_alias() { - let mut copy = Vec::from(RESERVED_FOR_COLUMN_ALIAS.clone()); + let mut copy = Vec::from(RESERVED_FOR_COLUMN_ALIAS); copy.sort(); assert!(copy == RESERVED_FOR_COLUMN_ALIAS) } #[test] fn keywords_sorted_reserved_for_table_alias() { - let mut copy = Vec::from(RESERVED_FOR_TABLE_ALIAS.clone()); + let mut copy = Vec::from(RESERVED_FOR_TABLE_ALIAS); copy.sort(); assert!(copy == RESERVED_FOR_TABLE_ALIAS) } From 488415621a096a352e74af34820787ebfcc07fea Mon Sep 17 00:00:00 2001 From: Dandandan Date: Mon, 8 Jun 2020 23:41:15 +0200 Subject: [PATCH 08/17] Fix format issue --- src/dialect/keywords.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index 7805eaa63..3aa53b384 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -35,7 +35,6 @@ macro_rules! kw_def { }; } - /// Expands to a list of `kw_def!()` invocations for each keyword /// and defines an ALL_KEYWORDS array of the defined constants. macro_rules! define_keywords { From 3dd467af03e1335a91fb2e9f165fd120333bbce7 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Wed, 10 Jun 2020 19:32:58 +0200 Subject: [PATCH 09/17] Remove explicit clones --- src/parser.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index df6d1af7b..cbeb6cd2b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -847,7 +847,7 @@ impl Parser { /// Bail out if the current token is not an expected keyword, or consume it if it is pub fn expect_keyword(&mut self, expected: Keyword) -> Result<(), ParserError> { - if self.parse_keyword(expected.clone()) { + if self.parse_keyword(expected) { Ok(()) } else { self.expected(format!("{:?}", &expected).as_str(), self.peek_token()) @@ -857,8 +857,8 @@ impl Parser { /// Bail out if the following tokens are not the expected sequence of /// keywords, or consume them if they are. pub fn expect_keywords(&mut self, expected: &[Keyword]) -> Result<(), ParserError> { - for kw in expected { - self.expect_keyword(kw.clone())?; + for &kw in expected { + self.expect_keyword(kw)?; } Ok(()) } From ca3c10d6f6c1be684ab4fcce9e8102bbb67c8082 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Wed, 10 Jun 2020 19:37:17 +0200 Subject: [PATCH 10/17] Remove reduntant clones --- src/parser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index cbeb6cd2b..7965e196c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -138,7 +138,7 @@ impl Parser { Keyword::BEGIN => Ok(self.parse_begin()?), Keyword::COMMIT => Ok(self.parse_commit()?), Keyword::ROLLBACK => Ok(self.parse_rollback()?), - _ => self.expected("an SQL statement", Token::Word(w.clone())), + _ => self.expected("an SQL statement", Token::Word(w)), }, Token::LParen => { self.prev_token(); @@ -1309,7 +1309,7 @@ impl Parser { Keyword::TRUE => Ok(Value::Boolean(true)), Keyword::FALSE => Ok(Value::Boolean(false)), Keyword::NULL => Ok(Value::Null), - _ => self.expected("a concrete value", Token::Word(w.clone())), + _ => self.expected("a concrete value", Token::Word(w)), }, // The call to n.parse() returns a bigdecimal when the // bigdecimal feature is enabled, and is otherwise a no-op From df6d60d85d590d9783697a1be66d51644c767309 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Wed, 10 Jun 2020 22:12:21 +0200 Subject: [PATCH 11/17] Remove reduntant clones --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 7965e196c..ec36ca902 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -487,7 +487,7 @@ impl Parser { Keyword::HOUR => Ok(DateTimeField::Hour), Keyword::MINUTE => Ok(DateTimeField::Minute), Keyword::SECOND => Ok(DateTimeField::Second), - _ => self.expected("date/time field", Token::Word(w.clone()))?, + _ => self.expected("date/time field", Token::Word(w))?, }, unexpected => self.expected("date/time field", unexpected), } From b16c17b8ab355218fa2db66686d3dfa68ee0ee66 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Thu, 11 Jun 2020 08:41:39 +0200 Subject: [PATCH 12/17] Address review comments, do not use ::parse but create normal parse function --- src/ast/mod.rs | 16 ---------------- src/parser.rs | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c04fb0269..da8088842 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -388,22 +388,6 @@ impl fmt::Display for WindowFrameUnits { } } -impl FromStr for WindowFrameUnits { - type Err = ParserError; - - fn from_str(s: &str) -> Result { - match s { - "ROWS" => Ok(WindowFrameUnits::Rows), - "RANGE" => Ok(WindowFrameUnits::Range), - "GROUPS" => Ok(WindowFrameUnits::Groups), - _ => Err(ParserError::ParserError(format!( - "Expected ROWS, RANGE, or GROUPS, found: {}", - s - ))), - } - } -} - /// Specifies [WindowFrame]'s `start_bound` and `end_bound` #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/parser.rs b/src/parser.rs index ec36ca902..e906f46b0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -310,11 +310,20 @@ impl Parser { })) } + pub fn parse_window_frame_units(&mut self) -> Result { + match self.next_token() { + Token::Word(w) => match w.keyword { + Keyword::ROWS => Ok(WindowFrameUnits::Rows), + Keyword::RANGE => Ok(WindowFrameUnits::Range), + Keyword::GROUPS => Ok(WindowFrameUnits::Range), + _ => self.expected("ROWS, RANGE, GROUPS", Token::Word(w))?, + }, + unexpected => self.expected("ROWS, RANGE, GROUPS", unexpected), + } + } + pub fn parse_window_frame(&mut self) -> Result { - let units = match self.next_token() { - Token::Word(w) => w.value.to_ascii_uppercase().parse::()?, - unexpected => return self.expected("ROWS, RANGE, GROUPS", unexpected), - }; + let units = self.parse_window_frame_units()?; let (start_bound, end_bound) = if self.parse_keyword(Keyword::BETWEEN) { let start_bound = self.parse_window_frame_bound()?; self.expect_keyword(Keyword::AND)?; @@ -1478,7 +1487,7 @@ impl Parser { } /// Parse a possibly qualified, possibly quoted identifier, e.g. - /// `foo` or `myschema.Keyword::table` + /// `foo` or `myschema."table" pub fn parse_object_name(&mut self) -> Result { let mut idents = vec![]; loop { From b03f4759772e72793e2efa63faf373a252e40a72 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Thu, 11 Jun 2020 19:50:38 +0200 Subject: [PATCH 13/17] Group together related keywords --- src/dialect/keywords.rs | 34 ++++++++++++++++++++-------------- src/parser.rs | 2 +- tests/sqlparser_common.rs | 14 -------------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index aab0034f6..f7bd018bc 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -447,37 +447,41 @@ define_keywords!( /// These keywords can't be used as a table alias, so that `FROM table_name alias` /// can be parsed unambiguously without looking ahead. pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[ + // Reserved as both a table and a column alias + Keyword::WHERE, + Keyword::WITH, + Keyword::SELECT, + Keyword::GROUP, + Keyword::HAVING, + Keyword::ORDER, + Keyword::TOP, + Keyword::LIMIT, + Keyword::OFFSET, + Keyword::FETCH, + Keyword::UNION, + Keyword::EXCEPT, + Keyword::INTERSECT, + // Reserved only as a table alias in the `FROM`/`JOIN` clauses Keyword::CROSS, Keyword::EXCEPT, - Keyword::FETCH, Keyword::FULL, - Keyword::GROUP, - Keyword::HAVING, Keyword::INNER, - Keyword::INTERSECT, Keyword::JOIN, Keyword::LEFT, - Keyword::LIMIT, Keyword::NATURAL, - Keyword::OFFSET, Keyword::ON, - Keyword::ORDER, - Keyword::OUTER, Keyword::RIGHT, - Keyword::SELECT, - Keyword::TOP, - Keyword::UNION, Keyword::USING, - Keyword::WHERE, - Keyword::WITH, + // for MSSQL-specific OUTER APPLY (seems reserved in most dialects) + Keyword::OUTER, ]; /// Can't be used as a column alias, so that `SELECT alias` /// can be parsed unambiguously without looking ahead. pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[ + // Reserved as both a table and a column alias Keyword::EXCEPT, Keyword::FETCH, - Keyword::FROM, Keyword::GROUP, Keyword::HAVING, Keyword::INTERSECT, @@ -488,4 +492,6 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[ Keyword::UNION, Keyword::WHERE, Keyword::WITH, + // Reserved only as a column alias in the `SELECT` clause + Keyword::FROM, ]; diff --git a/src/parser.rs b/src/parser.rs index e906f46b0..5d3d854e3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1443,7 +1443,7 @@ impl Parser { // which may start a construct allowed in this position, to be parsed as aliases. // (For example, in `FROM t1 JOIN` the `JOIN` will always be parsed as a keyword, // not an alias.) - Token::Word(w) if after_as || reserved_kwds.binary_search(&w.keyword).is_err() => { + Token::Word(w) if after_as || !reserved_kwds.contains(&w.keyword) => { Ok(Some(w.to_ident())) } // MSSQL supports single-quoted strings as aliases for columns diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 08a8d755d..496bb8a3b 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2879,20 +2879,6 @@ fn all_keywords_sorted() { assert!(copy == ALL_KEYWORDS) } -#[test] -fn keywords_sorted_reserved_for_column_alias() { - let mut copy = Vec::from(RESERVED_FOR_COLUMN_ALIAS); - copy.sort(); - assert!(copy == RESERVED_FOR_COLUMN_ALIAS) -} - -#[test] -fn keywords_sorted_reserved_for_table_alias() { - let mut copy = Vec::from(RESERVED_FOR_TABLE_ALIAS); - copy.sort(); - assert!(copy == RESERVED_FOR_TABLE_ALIAS) -} - fn parse_sql_statements(sql: &str) -> Result, ParserError> { all_dialects().parse_sql_statements(sql) } From 14da209e0e563601a6ba3e2d36393f9e99fbe386 Mon Sep 17 00:00:00 2001 From: Dandandan Date: Thu, 11 Jun 2020 19:59:45 +0200 Subject: [PATCH 14/17] Fix Range -> Groups --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 5d3d854e3..2dc612dfc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -315,7 +315,7 @@ impl Parser { Token::Word(w) => match w.keyword { Keyword::ROWS => Ok(WindowFrameUnits::Rows), Keyword::RANGE => Ok(WindowFrameUnits::Range), - Keyword::GROUPS => Ok(WindowFrameUnits::Range), + Keyword::GROUPS => Ok(WindowFrameUnits::Groups), _ => self.expected("ROWS, RANGE, GROUPS", Token::Word(w))?, }, unexpected => self.expected("ROWS, RANGE, GROUPS", unexpected), From 6ac887973c4e513ee804e047c7c7abba7d2f5ee4 Mon Sep 17 00:00:00 2001 From: Nickolay Ponomarev Date: Thu, 11 Jun 2020 21:25:30 +0300 Subject: [PATCH 15/17] Add a test to ensure GROUPS in window specification round-trips --- tests/sqlparser_common.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 496bb8a3b..d072d5b14 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1354,10 +1354,12 @@ fn parse_window_functions() { avg(bar) OVER (ORDER BY a \ RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING), \ max(baz) OVER (ORDER BY a \ - ROWS UNBOUNDED PRECEDING) \ + ROWS UNBOUNDED PRECEDING), \ + sum(qux) OVER (ORDER BY a \ + GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) \ FROM foo"; let select = verified_only_select(sql); - assert_eq!(4, select.projection.len()); + assert_eq!(5, select.projection.len()); assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("row_number")]), From fea2f91ec30a03b22dbdaee23904a3740180e4f4 Mon Sep 17 00:00:00 2001 From: Nickolay Ponomarev Date: Thu, 11 Jun 2020 21:38:55 +0300 Subject: [PATCH 16/17] Change the order in RESERVED_FOR_... keyword lists to match --- src/dialect/keywords.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index f7bd018bc..e804f3693 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -447,10 +447,10 @@ define_keywords!( /// These keywords can't be used as a table alias, so that `FROM table_name alias` /// can be parsed unambiguously without looking ahead. pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[ - // Reserved as both a table and a column alias - Keyword::WHERE, + // Reserved as both a table and a column alias: Keyword::WITH, Keyword::SELECT, + Keyword::WHERE, Keyword::GROUP, Keyword::HAVING, Keyword::ORDER, @@ -461,16 +461,15 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[ Keyword::UNION, Keyword::EXCEPT, Keyword::INTERSECT, - // Reserved only as a table alias in the `FROM`/`JOIN` clauses + // Reserved only as a table alias in the `FROM`/`JOIN` clauses: + Keyword::ON, + Keyword::JOIN, + Keyword::INNER, Keyword::CROSS, - Keyword::EXCEPT, Keyword::FULL, - Keyword::INNER, - Keyword::JOIN, Keyword::LEFT, - Keyword::NATURAL, - Keyword::ON, Keyword::RIGHT, + Keyword::NATURAL, Keyword::USING, // for MSSQL-specific OUTER APPLY (seems reserved in most dialects) Keyword::OUTER, @@ -479,19 +478,20 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[ /// Can't be used as a column alias, so that `SELECT alias` /// can be parsed unambiguously without looking ahead. pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[ - // Reserved as both a table and a column alias - Keyword::EXCEPT, - Keyword::FETCH, + // Reserved as both a table and a column alias: + Keyword::WITH, + Keyword::SELECT, + Keyword::WHERE, Keyword::GROUP, Keyword::HAVING, - Keyword::INTERSECT, + Keyword::ORDER, + Keyword::TOP, Keyword::LIMIT, Keyword::OFFSET, - Keyword::ORDER, - Keyword::SELECT, + Keyword::FETCH, Keyword::UNION, - Keyword::WHERE, - Keyword::WITH, + Keyword::EXCEPT, + Keyword::INTERSECT, // Reserved only as a column alias in the `SELECT` clause Keyword::FROM, ]; From b551997c5ef23e0d570d950980d7c5a6189d2ee5 Mon Sep 17 00:00:00 2001 From: Nickolay Ponomarev Date: Thu, 11 Jun 2020 21:43:06 +0300 Subject: [PATCH 17/17] Avoid unnecessary wildcard imports in tests --- tests/sqlparser_common.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index d072d5b14..9720c3972 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -21,8 +21,8 @@ use matches::assert_matches; use sqlparser::ast::*; -use sqlparser::dialect::keywords::*; -use sqlparser::parser::*; +use sqlparser::dialect::keywords::ALL_KEYWORDS; +use sqlparser::parser::{Parser, ParserError}; use sqlparser::test_utils::{all_dialects, expr_from_projection, number, only}; #[test]