From 1a77bac49db047702fa40ed0dd44bcf6ba1ffd9e Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Mon, 16 Sep 2024 10:36:32 +0200 Subject: [PATCH 01/32] feat(tokenizer): add source location spans to tokens --- src/ast/mod.rs | 1 + src/parser.rs | 20 +++++++-------- src/tokenizer.rs | 66 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index b8d5cf042..a57eee946 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -52,6 +52,7 @@ pub mod helpers; mod operator; mod query; mod value; +mod spans; #[cfg(feature = "visitor")] mod visitor; diff --git a/src/parser.rs b/src/parser.rs index 3883a333d..c16aa5422 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -269,7 +269,7 @@ impl<'a> Parser<'a> { .into_iter() .map(|token| TokenWithLocation { token, - location: Location { line: 0, column: 0 }, + span: Span::empty(), }) .collect(); self.with_tokens_with_locations(tokens_with_locations) @@ -2022,13 +2022,13 @@ impl<'a> Parser<'a> { match self.tokens.get(index - 1) { Some(TokenWithLocation { token: Token::Whitespace(_), - location: _, + span: _, }) => continue, non_whitespace => { if n == 0 { return non_whitespace.cloned().unwrap_or(TokenWithLocation { token: Token::EOF, - location: Location { line: 0, column: 0 }, + span: Span::empty(), }); } n -= 1; @@ -2046,7 +2046,7 @@ impl<'a> Parser<'a> { match self.tokens.get(self.index - 1) { Some(TokenWithLocation { token: Token::Whitespace(_), - location: _, + span: _, }) => continue, token => { return token @@ -2072,7 +2072,7 @@ impl<'a> Parser<'a> { self.index -= 1; if let Some(TokenWithLocation { token: Token::Whitespace(_), - location: _, + span: _, }) = self.tokens.get(self.index) { continue; @@ -3600,7 +3600,7 @@ impl<'a> Parser<'a> { "FULLTEXT or SPATIAL option without constraint name", TokenWithLocation { token: Token::make_keyword(&name.to_string()), - location: next_token.location, + span: next_token.span, }, ); } @@ -4073,7 +4073,7 @@ impl<'a> Parser<'a> { /// Parse a literal value (numbers, strings, date/time, booleans) pub fn parse_value(&mut self) -> Result { let next_token = self.next_token(); - let location = next_token.location; + let span = next_token.span; match next_token.token { Token::Word(w) => match w.keyword { Keyword::TRUE => Ok(Value::Boolean(true)), @@ -4086,7 +4086,7 @@ impl<'a> Parser<'a> { "A value?", TokenWithLocation { token: Token::Word(w), - location, + span, }, )?, }, @@ -4098,7 +4098,7 @@ impl<'a> Parser<'a> { "a concrete value", TokenWithLocation { token: Token::Word(w), - location, + span, }, ), }, @@ -4125,7 +4125,7 @@ impl<'a> Parser<'a> { "a value", TokenWithLocation { token: unexpected, - location, + span, }, ), } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index dcf128542..2cbcf865e 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -338,23 +338,63 @@ pub struct Location { pub column: u64, } +impl Location { + pub fn of(line: u64, column: u64) -> Self { + Self { line, column } + } + + pub fn span_to(self, end: Self) -> Span { + Span { start: self, end } + } +} + +impl From<(u64, u64)> for Location { + fn from((line, column): (u64, u64)) -> Self { + Self { line, column } + } +} + +#[derive(Debug, Eq, PartialEq, Clone)] +pub struct Span { + pub start: Location, + pub end: Location, +} + +impl Span { + pub fn new(start: Location, end: Location) -> Span { + Span { start, end } + } + + pub fn empty() -> Span { + Span { + start: Location { line: 0, column: 0 }, + end: Location { line: 0, column: 0 }, + } + } +} + + /// A [Token] with [Location] attached to it #[derive(Debug, Eq, PartialEq, Clone)] pub struct TokenWithLocation { pub token: Token, - pub location: Location, + pub span: Span, } impl TokenWithLocation { - pub fn new(token: Token, line: u64, column: u64) -> TokenWithLocation { + pub fn new(token: Token, span: Span) -> TokenWithLocation { TokenWithLocation { token, - location: Location { line, column }, + span, } } pub fn wrap(token: Token) -> TokenWithLocation { - TokenWithLocation::new(token, 0, 0) + TokenWithLocation::new(token, Span::empty()) + } + + pub fn at(token: Token, start: Location, end: Location) -> TokenWithLocation { + TokenWithLocation::new(token, Span::new(start, end)) } } @@ -467,9 +507,11 @@ impl<'a> Tokenizer<'a> { let mut location = state.location(); while let Some(token) = self.next_token(&mut state)? { + let span = location.span_to(state.location()); + tokens.push(TokenWithLocation { token, - location: location.clone(), + span, }); location = state.location(); @@ -1815,13 +1857,13 @@ mod tests { let mut tokenizer = Tokenizer::new(&dialect, sql); let tokens = tokenizer.tokenize_with_location().unwrap(); let expected = vec![ - TokenWithLocation::new(Token::make_keyword("SELECT"), 1, 1), - TokenWithLocation::new(Token::Whitespace(Whitespace::Space), 1, 7), - TokenWithLocation::new(Token::make_word("a", None), 1, 8), - TokenWithLocation::new(Token::Comma, 1, 9), - TokenWithLocation::new(Token::Whitespace(Whitespace::Newline), 1, 10), - TokenWithLocation::new(Token::Whitespace(Whitespace::Space), 2, 1), - TokenWithLocation::new(Token::make_word("b", None), 2, 2), + TokenWithLocation::at(Token::make_keyword("SELECT"), (1, 1).into(), (1, 7).into()), + TokenWithLocation::at(Token::Whitespace(Whitespace::Space), (1, 7).into(), (1, 8).into()), + TokenWithLocation::at(Token::make_word("a", None), (1, 8).into(), (1, 9).into()), + TokenWithLocation::at(Token::Comma, (1, 9).into(), (1, 10).into()), + TokenWithLocation::at(Token::Whitespace(Whitespace::Newline), (1, 10).into(), (2, 1).into()), + TokenWithLocation::at(Token::Whitespace(Whitespace::Space), (2, 1).into(), (2, 2).into()), + TokenWithLocation::at(Token::make_word("b", None), (2, 2).into(), (2, 3).into()), ]; compare(expected, tokens); } From d81801227a31dfe6bcfb17e8d820135855a81f26 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Mon, 16 Sep 2024 14:41:02 +0200 Subject: [PATCH 02/32] feat: begin work on trait Spanned --- src/ast/mod.rs | 32 +- src/ast/query.rs | 4 +- src/ast/spans.rs | 286 +++++++++++++ src/dialect/postgresql.rs | 4 +- src/dialect/sqlite.rs | 2 +- src/parser.rs | 670 ++++++++++++++++-------------- src/tokenizer.rs | 85 +++- tests/sqlparser_bigquery.rs | 2 + tests/sqlparser_clickhouse.rs | 3 + tests/sqlparser_common.rs | 44 +- tests/sqlparser_custom_dialect.rs | 2 +- tests/sqlparser_mssql.rs | 4 +- tests/sqlparser_mysql.rs | 15 +- tests/sqlparser_postgres.rs | 42 +- tests/sqlparser_redshift.rs | 19 +- 15 files changed, 845 insertions(+), 369 deletions(-) create mode 100644 src/ast/spans.rs diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a57eee946..7037228c3 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -25,6 +25,8 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; +use crate::tokenizer::Span; + pub use self::data_type::{ CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, TimezoneInfo, }; @@ -51,8 +53,8 @@ mod ddl; pub mod helpers; mod operator; mod query; -mod value; mod spans; +mod value; #[cfg(feature = "visitor")] mod visitor; @@ -104,6 +106,8 @@ pub struct Ident { /// The starting quote if any. Valid quote characters are the single quote, /// double quote, backtick, and opening square bracket. pub quote_style: Option, + /// The span of the identifier in the original SQL string. + pub span: Span, } impl Ident { @@ -115,6 +119,7 @@ impl Ident { Ident { value: value.into(), quote_style: None, + span: Span::empty(), } } @@ -128,6 +133,30 @@ impl Ident { Ident { value: value.into(), quote_style: Some(quote), + span: Span::empty(), + } + } + + pub fn with_span(span: Span, value: S) -> Self + where + S: Into, + { + Ident { + value: value.into(), + quote_style: None, + span, + } + } + + pub fn with_quote_and_span(quote: char, span: Span, value: S) -> Self + where + S: Into, + { + assert!(quote == '\'' || quote == '"' || quote == '`' || quote == '['); + Ident { + value: value.into(), + quote_style: Some(quote), + span, } } } @@ -137,6 +166,7 @@ impl From<&str> for Ident { Ident { value: value.to_string(), quote_style: None, + span: Span::empty(), } } } diff --git a/src/ast/query.rs b/src/ast/query.rs index f63234b09..53ce5f9fb 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -19,7 +19,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -use crate::ast::*; +use crate::{ast::*, tokenizer::TokenWithLocation}; /// The most complete variant of a `SELECT` query expression, optionally /// including `WITH`, `UNION` / other set operations, and `ORDER BY`. @@ -193,6 +193,8 @@ impl fmt::Display for Table { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct Select { + /// SELECT + pub select_token: TokenWithLocation, pub distinct: bool, /// MSSQL syntax: `TOP () [ PERCENT ] [ WITH TIES ]` pub top: Option, diff --git a/src/ast/spans.rs b/src/ast/spans.rs new file mode 100644 index 000000000..c5169d392 --- /dev/null +++ b/src/ast/spans.rs @@ -0,0 +1,286 @@ +use core::iter; + +use crate::ast; +use crate::tokenizer::Span; + +use super::{Expr, Join, Query, Select, SelectItem, SetExpr, TableFactor, TableWithJoins}; + +pub trait Spanned { + fn span(&self) -> Span; +} + +impl Spanned for Query { + fn span(&self) -> Span { + self.body.span() + } +} + +impl Spanned for SetExpr { + fn span(&self) -> Span { + match self { + SetExpr::Select(select) => select.span(), + SetExpr::Query(query) => todo!(), + SetExpr::SetOperation { + op, + set_quantifier, + left, + right, + } => todo!(), + SetExpr::Values(values) => todo!(), + SetExpr::Insert(statement) => todo!(), + SetExpr::Table(table) => todo!(), + } + } +} + +impl Spanned for Expr { + fn span(&self) -> Span { + match self { + Expr::Identifier(ident) => ident.span, + Expr::CompoundIdentifier(vec) => todo!(), + Expr::JsonAccess { + left, + operator, + right, + } => todo!(), + Expr::CompositeAccess { expr, key } => todo!(), + Expr::IsFalse(expr) => todo!(), + Expr::IsNotFalse(expr) => todo!(), + Expr::IsTrue(expr) => todo!(), + Expr::IsNotTrue(expr) => todo!(), + Expr::IsNull(expr) => todo!(), + Expr::IsNotNull(expr) => todo!(), + Expr::IsUnknown(expr) => todo!(), + Expr::IsNotUnknown(expr) => todo!(), + Expr::IsDistinctFrom(expr, expr1) => todo!(), + Expr::IsNotDistinctFrom(expr, expr1) => todo!(), + Expr::InList { + expr, + list, + negated, + } => todo!(), + Expr::InSubquery { + expr, + subquery, + negated, + } => todo!(), + Expr::InUnnest { + expr, + array_expr, + negated, + } => todo!(), + Expr::Between { + expr, + negated, + low, + high, + } => todo!(), + Expr::BinaryOp { left, op, right } => todo!(), + Expr::Like { + negated, + expr, + pattern, + escape_char, + } => todo!(), + Expr::ILike { + negated, + expr, + pattern, + escape_char, + } => todo!(), + Expr::SimilarTo { + negated, + expr, + pattern, + escape_char, + } => todo!(), + Expr::AnyOp(expr) => todo!(), + Expr::AllOp(expr) => todo!(), + Expr::UnaryOp { op, expr } => todo!(), + Expr::Cast { expr, data_type } => todo!(), + Expr::TryCast { expr, data_type } => todo!(), + Expr::SafeCast { expr, data_type } => todo!(), + Expr::AtTimeZone { + timestamp, + time_zone, + } => todo!(), + Expr::Extract { field, expr } => todo!(), + Expr::Ceil { expr, field } => todo!(), + Expr::Floor { expr, field } => todo!(), + Expr::Position { expr, r#in } => todo!(), + Expr::Substring { + expr, + substring_from, + substring_for, + } => todo!(), + Expr::Trim { + expr, + trim_where, + trim_what, + } => todo!(), + Expr::Overlay { + expr, + overlay_what, + overlay_from, + overlay_for, + } => todo!(), + Expr::Collate { expr, collation } => todo!(), + Expr::Nested(expr) => todo!(), + Expr::Value(value) => todo!(), + Expr::TypedString { data_type, value } => todo!(), + Expr::MapAccess { column, keys } => todo!(), + Expr::Function(function) => todo!(), + Expr::AggregateExpressionWithFilter { expr, filter } => todo!(), + Expr::Case { + operand, + conditions, + results, + else_result, + } => todo!(), + Expr::Exists { subquery, negated } => todo!(), + Expr::Subquery(query) => todo!(), + Expr::ArraySubquery(query) => todo!(), + Expr::ListAgg(list_agg) => todo!(), + Expr::ArrayAgg(array_agg) => todo!(), + Expr::GroupingSets(vec) => todo!(), + Expr::Cube(vec) => todo!(), + Expr::Rollup(vec) => todo!(), + Expr::Tuple(vec) => todo!(), + Expr::ArrayIndex { obj, indexes } => todo!(), + Expr::Array(array) => todo!(), + Expr::Interval { + value, + leading_field, + leading_precision, + last_field, + fractional_seconds_precision, + } => todo!(), + Expr::MatchAgainst { + columns, + match_value, + opt_search_modifier, + } => todo!(), + } + } +} + +impl Spanned for SelectItem { + fn span(&self) -> Span { + match self { + SelectItem::UnnamedExpr(expr) => expr.span(), + SelectItem::ExprWithAlias { expr, alias } => expr.span().union(&alias.span), + SelectItem::QualifiedWildcard(object_name, wildcard_additional_options) => object_name + .0 + .iter() + .map(|i| i.span) + .reduce(|acc, item| acc.union(&item)) + .expect("Empty iterator"), + SelectItem::Wildcard(wildcard_additional_options) => todo!(), + } + } +} + +impl Spanned for TableFactor { + fn span(&self) -> Span { + match self { + TableFactor::Table { + name, + alias, + args, + with_hints, + } => union_spans( + name.0.iter().map(|i| i.span).chain( + alias + .as_ref() + .map(|alias| { + union_spans( + iter::once(alias.name.span) + .chain(alias.columns.iter().map(|i| i.span)), + ) + }) + .into_iter(), + ), + ), + TableFactor::Derived { + lateral, + subquery, + alias, + } => todo!(), + TableFactor::TableFunction { expr, alias } => todo!(), + TableFactor::UNNEST { + alias, + array_expr, + with_offset, + with_offset_alias, + } => todo!(), + TableFactor::NestedJoin { + table_with_joins, + alias, + } => todo!(), + } + } +} + +impl Spanned for Join { + fn span(&self) -> Span { + todo!() + } +} + +impl Spanned for TableWithJoins { + fn span(&self) -> Span { + union_spans( + core::iter::once(self.relation.span()).chain(self.joins.iter().map(|item| item.span())), + ) + } +} + +pub fn union_spans>(iter: I) -> Span { + iter.reduce(|acc, item| acc.union(&item)) + .expect("Empty iterator") +} + +impl Spanned for Select { + fn span(&self) -> Span { + union_spans( + core::iter::once(self.select_token.span.clone()) + .chain(self.projection.iter().map(|item| item.span())) + .chain(self.from.iter().map(|item| item.span())), + ) + } +} + +/** + * TODO: + * + * - CTE + * - With + * - SetExpr + * - Fetch + * - Lock Clause + */ +struct Ignore; + +#[cfg(test)] +pub mod tests { + use crate::dialect::{Dialect, GenericDialect}; + use crate::tokenizer::Span; + + use super::*; + + #[test] + fn test_span() { + let query = crate::parser::Parser::new(&GenericDialect::default()) + .try_with_sql("SELECT id, name FROM users") + .unwrap() + .parse_query() + .unwrap(); + + dbg!(&query); + + assert_eq!( + query.span(), + Span::new((1, 1).into(), (1, 54 - 28 + 1).into()) + ); + } +} diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index fe1953d2a..81d5a9fc4 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -36,7 +36,7 @@ impl Dialect for PostgreSqlDialect { } fn parse_statement(&self, parser: &mut Parser) -> Option> { - if parser.parse_keyword(Keyword::COMMENT) { + if parser.parse_keyword(Keyword::COMMENT).is_some() { Some(parse_comment(parser)) } else { None @@ -65,7 +65,7 @@ pub fn parse_comment(parser: &mut Parser) -> Result { }; parser.expect_keyword(Keyword::IS)?; - let comment = if parser.parse_keyword(Keyword::NULL) { + let comment = if parser.parse_keyword(Keyword::NULL).is_some() { None } else { Some(parser.parse_literal_string()?) diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs index 64d7f62fd..5fef13671 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -40,7 +40,7 @@ impl Dialect for SQLiteDialect { } fn parse_statement(&self, parser: &mut Parser) -> Option> { - if parser.parse_keyword(Keyword::REPLACE) { + if parser.parse_keyword(Keyword::REPLACE).is_some() { parser.prev_token(); Some(parser.parse_insert()) } else { diff --git a/src/parser.rs b/src/parser.rs index c16aa5422..2446553d3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -284,8 +284,8 @@ impl<'a> Parser<'a> { pub fn try_with_sql(self, sql: &str) -> Result { debug!("Parsing sql '{}'...", sql); let mut tokenizer = Tokenizer::new(self.dialect, sql); - let tokens = tokenizer.tokenize()?; - Ok(self.with_tokens(tokens)) + let tokens = tokenizer.tokenize_with_location()?; + Ok(self.with_tokens_with_locations(tokens)) } /// Parse potentially multiple statements @@ -412,7 +412,7 @@ impl<'a> Parser<'a> { } pub fn parse_msck(&mut self) -> Result { - let repair = self.parse_keyword(Keyword::REPAIR); + let repair = self.parse_keyword(Keyword::REPAIR).is_some(); self.expect_keyword(Keyword::TABLE)?; let table_name = self.parse_object_name()?; let partition_action = self @@ -442,7 +442,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::TABLE)?; let table_name = self.parse_object_name()?; let mut partitions = None; - if self.parse_keyword(Keyword::PARTITION) { + if self.parse_keyword(Keyword::PARTITION).is_some() { self.expect_token(&Token::LParen)?; partitions = Some(self.parse_comma_separated(Parser::parse_expr)?); self.expect_token(&Token::RParen)?; @@ -516,12 +516,12 @@ impl<'a> Parser<'a> { let next_token = self.next_token(); match next_token.token { Token::Word(w) if self.peek_token().token == Token::Period => { - let mut id_parts: Vec = vec![w.to_ident()]; + let mut id_parts: Vec = vec![w.to_ident(next_token.span)]; while self.consume_token(&Token::Period) { let next_token = self.next_token(); match next_token.token { - Token::Word(w) => id_parts.push(w.to_ident()), + Token::Word(w) => id_parts.push(w.to_ident(next_token.span)), Token::Mul => { return Ok(WildcardExpr::QualifiedWildcard(ObjectName(id_parts))); } @@ -597,7 +597,7 @@ impl<'a> Parser<'a> { pub fn parse_assert(&mut self) -> Result { let condition = self.parse_expr()?; - let message = if self.parse_keyword(Keyword::AS) { + let message = if self.parse_keyword(Keyword::AS).is_some() { Some(self.parse_expr()?) } else { None @@ -666,7 +666,7 @@ impl<'a> Parser<'a> { if dialect_of!(self is PostgreSqlDialect | GenericDialect) => { Ok(Expr::Function(Function { - name: ObjectName(vec![w.to_ident()]), + name: ObjectName(vec![w.to_ident(next_token.span)]), args: vec![], over: None, distinct: false, @@ -678,7 +678,7 @@ impl<'a> Parser<'a> { | Keyword::CURRENT_DATE | Keyword::LOCALTIME | Keyword::LOCALTIMESTAMP => { - self.parse_time_functions(ObjectName(vec![w.to_ident()])) + self.parse_time_functions(ObjectName(vec![w.to_ident(next_token.span)])) } Keyword::CASE => self.parse_case_expr(), Keyword::CAST => self.parse_cast_expr(), @@ -715,11 +715,11 @@ impl<'a> Parser<'a> { // identifier, a function call, or a simple identifier: _ => match self.peek_token().token { Token::LParen | Token::Period => { - let mut id_parts: Vec = vec![w.to_ident()]; + let mut id_parts: Vec = vec![w.to_ident(next_token.span)]; while self.consume_token(&Token::Period) { let next_token = self.next_token(); match next_token.token { - Token::Word(w) => id_parts.push(w.to_ident()), + Token::Word(w) => id_parts.push(w.to_ident(next_token.span)), _ => { return self .expected("an identifier or a '*' after '.'", next_token); @@ -734,7 +734,7 @@ impl<'a> Parser<'a> { Ok(Expr::CompoundIdentifier(id_parts)) } } - _ => Ok(Expr::Identifier(w.to_ident())), + _ => Ok(Expr::Identifier(w.to_ident(next_token.span))), }, }, // End of Token::Word // array `[1, 2, 3]` @@ -786,25 +786,26 @@ impl<'a> Parser<'a> { } Token::LParen => { - let expr = - if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) { - self.prev_token(); - Expr::Subquery(Box::new(self.parse_query()?)) - } else { - let exprs = self.parse_comma_separated(Parser::parse_expr)?; - match exprs.len() { - 0 => unreachable!(), // parse_comma_separated ensures 1 or more - 1 => Expr::Nested(Box::new(exprs.into_iter().next().unwrap())), - _ => Expr::Tuple(exprs), - } - }; + let expr = if self.parse_keyword(Keyword::SELECT).is_some() + || self.parse_keyword(Keyword::WITH).is_some() + { + self.prev_token(); + Expr::Subquery(Box::new(self.parse_query()?)) + } else { + let exprs = self.parse_comma_separated(Parser::parse_expr)?; + match exprs.len() { + 0 => unreachable!(), // parse_comma_separated ensures 1 or more + 1 => Expr::Nested(Box::new(exprs.into_iter().next().unwrap())), + _ => Expr::Tuple(exprs), + } + }; self.expect_token(&Token::RParen)?; if !self.consume_token(&Token::Period) { Ok(expr) } else { let tok = self.next_token(); let key = match tok.token { - Token::Word(word) => word.to_ident(), + Token::Word(word) => word.to_ident(tok.span), _ => return parser_err!(format!("Expected identifier, found: {}", tok)), }; Ok(Expr::CompositeAccess { @@ -820,7 +821,7 @@ impl<'a> Parser<'a> { _ => self.expected("an expression:", next_token), }?; - if self.parse_keyword(Keyword::COLLATE) { + if self.parse_keyword(Keyword::COLLATE).is_some() { Ok(Expr::Collate { expr: Box::new(expr), collation: self.parse_object_name()?, @@ -834,7 +835,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let distinct = self.parse_all_or_distinct()?; let args = self.parse_optional_args()?; - let over = if self.parse_keyword(Keyword::OVER) { + let over = if self.parse_keyword(Keyword::OVER).is_some() { // TBD: support window names (`OVER mywin`) in place of inline specification self.expect_token(&Token::LParen)?; let partition_by = if self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) { @@ -903,7 +904,7 @@ impl<'a> Parser<'a> { pub fn parse_window_frame(&mut self) -> Result { let units = self.parse_window_frame_units()?; - let (start_bound, end_bound) = if self.parse_keyword(Keyword::BETWEEN) { + let (start_bound, end_bound) = if self.parse_keyword(Keyword::BETWEEN).is_some() { let start_bound = self.parse_window_frame_bound()?; self.expect_keyword(Keyword::AND)?; let end_bound = Some(self.parse_window_frame_bound()?); @@ -923,7 +924,7 @@ impl<'a> Parser<'a> { if self.parse_keywords(&[Keyword::CURRENT, Keyword::ROW]) { Ok(WindowFrameBound::CurrentRow) } else { - let rows = if self.parse_keyword(Keyword::UNBOUNDED) { + let rows = if self.parse_keyword(Keyword::UNBOUNDED).is_some() { None } else { Some(Box::new(match self.peek_token().token { @@ -931,9 +932,9 @@ impl<'a> Parser<'a> { _ => self.parse_expr()?, })) }; - if self.parse_keyword(Keyword::PRECEDING) { + if self.parse_keyword(Keyword::PRECEDING).is_some() { Ok(WindowFrameBound::Preceding(rows)) - } else if self.parse_keyword(Keyword::FOLLOWING) { + } else if self.parse_keyword(Keyword::FOLLOWING).is_some() { Ok(WindowFrameBound::Following(rows)) } else { self.expected("PRECEDING or FOLLOWING", self.peek_token()) @@ -950,12 +951,12 @@ impl<'a> Parser<'a> { let result = self.parse_comma_separated(|p| p.parse_tuple(false, true))?; self.expect_token(&Token::RParen)?; Ok(Expr::GroupingSets(result)) - } else if self.parse_keyword(Keyword::CUBE) { + } else if self.parse_keyword(Keyword::CUBE).is_some() { self.expect_token(&Token::LParen)?; let result = self.parse_comma_separated(|p| p.parse_tuple(true, true))?; self.expect_token(&Token::RParen)?; Ok(Expr::Cube(result)) - } else if self.parse_keyword(Keyword::ROLLUP) { + } else if self.parse_keyword(Keyword::ROLLUP).is_some() { self.expect_token(&Token::LParen)?; let result = self.parse_comma_separated(|p| p.parse_tuple(true, true))?; self.expect_token(&Token::RParen)?; @@ -1005,7 +1006,7 @@ impl<'a> Parser<'a> { pub fn parse_case_expr(&mut self) -> Result { let mut operand = None; - if !self.parse_keyword(Keyword::WHEN) { + if !self.parse_keyword(Keyword::WHEN).is_some() { operand = Some(Box::new(self.parse_expr()?)); self.expect_keyword(Keyword::WHEN)?; } @@ -1015,11 +1016,11 @@ impl<'a> Parser<'a> { conditions.push(self.parse_expr()?); self.expect_keyword(Keyword::THEN)?; results.push(self.parse_expr()?); - if !self.parse_keyword(Keyword::WHEN) { + if !self.parse_keyword(Keyword::WHEN).is_some() { break; } } - let else_result = if self.parse_keyword(Keyword::ELSE) { + let else_result = if self.parse_keyword(Keyword::ELSE).is_some() { Some(Box::new(self.parse_expr()?)) } else { None @@ -1100,7 +1101,7 @@ impl<'a> Parser<'a> { let expr = self.parse_expr()?; // Parse `CEIL/FLOOR(expr)` let mut field = DateTimeField::NoDateTime; - let keyword_to = self.parse_keyword(Keyword::TO); + let keyword_to = self.parse_keyword(Keyword::TO).is_some(); if keyword_to { // Parse `CEIL/FLOOR(expr TO DateTimeField)` field = self.parse_date_time_field()?; @@ -1125,7 +1126,7 @@ impl<'a> Parser<'a> { // Parse the subexpr till the IN keyword let expr = self.parse_subexpr(Self::BETWEEN_PREC)?; - if self.parse_keyword(Keyword::IN) { + if self.parse_keyword(Keyword::IN).is_some() { let from = self.parse_expr()?; self.expect_token(&Token::RParen)?; Ok(Expr::Position { @@ -1142,12 +1143,12 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; let mut from_expr = None; - if self.parse_keyword(Keyword::FROM) || self.consume_token(&Token::Comma) { + if self.parse_keyword(Keyword::FROM).is_some() || self.consume_token(&Token::Comma) { from_expr = Some(self.parse_expr()?); } let mut to_expr = None; - if self.parse_keyword(Keyword::FOR) || self.consume_token(&Token::Comma) { + if self.parse_keyword(Keyword::FOR).is_some() || self.consume_token(&Token::Comma) { to_expr = Some(self.parse_expr()?); } self.expect_token(&Token::RParen)?; @@ -1168,7 +1169,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::FROM)?; let from_expr = self.parse_expr()?; let mut for_expr = None; - if self.parse_keyword(Keyword::FOR) { + if self.parse_keyword(Keyword::FOR).is_some() { for_expr = Some(self.parse_expr()?); } self.expect_token(&Token::RParen)?; @@ -1197,7 +1198,7 @@ impl<'a> Parser<'a> { } } let expr = self.parse_expr()?; - if self.parse_keyword(Keyword::FROM) { + if self.parse_keyword(Keyword::FROM).is_some() { let trim_what = Box::new(expr); let expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; @@ -1265,7 +1266,7 @@ impl<'a> Parser<'a> { None }; let on_overflow = if self.parse_keywords(&[Keyword::ON, Keyword::OVERFLOW]) { - if self.parse_keyword(Keyword::ERROR) { + if self.parse_keyword(Keyword::ERROR).is_some() { Some(ListAggOnOverflow::Error) } else { self.expect_keyword(Keyword::TRUNCATE)?; @@ -1284,8 +1285,8 @@ impl<'a> Parser<'a> { self.peek_token(), )?, }; - let with_count = self.parse_keyword(Keyword::WITH); - if !with_count && !self.parse_keyword(Keyword::WITHOUT) { + let with_count = self.parse_keyword(Keyword::WITH).is_some(); + if !with_count && !self.parse_keyword(Keyword::WITHOUT).is_some() { self.expected("either WITH or WITHOUT in LISTAGG", self.peek_token())?; } self.expect_keyword(Keyword::COUNT)?; @@ -1317,7 +1318,7 @@ impl<'a> Parser<'a> { pub fn parse_array_agg_expr(&mut self) -> Result { self.expect_token(&Token::LParen)?; - let distinct = self.parse_keyword(Keyword::DISTINCT); + let distinct = self.parse_keyword(Keyword::DISTINCT).is_some(); let expr = Box::new(self.parse_expr()?); // ANSI SQL and BigQuery define ORDER BY inside function. if !self.dialect.supports_within_after_array_aggregation() { @@ -1327,7 +1328,7 @@ impl<'a> Parser<'a> { } else { None }; - let limit = if self.parse_keyword(Keyword::LIMIT) { + let limit = if self.parse_keyword(Keyword::LIMIT).is_some() { self.parse_limit()?.map(Box::new) } else { None @@ -1410,7 +1411,7 @@ impl<'a> Parser<'a> { Token::Word(w) => match w.keyword { Keyword::EXISTS => { let negated = true; - let _ = self.parse_keyword(Keyword::EXISTS); + let _ = self.parse_keyword(Keyword::EXISTS).is_some(); self.parse_exists_expr(negated) } _ => Ok(Expr::UnaryOp { @@ -1555,7 +1556,7 @@ impl<'a> Parser<'a> { (leading_precision, last_field, fsec_precision) } else { let leading_precision = self.parse_optional_precision()?; - if self.parse_keyword(Keyword::TO) { + if self.parse_keyword(Keyword::TO).is_some() { let last_field = Some(self.parse_date_time_field()?); let fsec_precision = if last_field == Some(DateTimeField::Second) { self.parse_optional_precision()? @@ -1669,7 +1670,7 @@ impl<'a> Parser<'a> { } else if let Token::Word(w) = &tok.token { match w.keyword { Keyword::IS => { - if self.parse_keyword(Keyword::NULL) { + if self.parse_keyword(Keyword::NULL).is_some() { Ok(Expr::IsNull(Box::new(expr))) } else if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) { Ok(Expr::IsNotNull(Box::new(expr))) @@ -1700,7 +1701,7 @@ impl<'a> Parser<'a> { } } Keyword::AT => { - // if self.parse_keyword(Keyword::TIME) { + // if self.parse_keyword(Keyword::TIME).is_some() { // self.expect_keyword(Keyword::ZONE)?; if self.parse_keywords(&[Keyword::TIME, Keyword::ZONE]) { let time_zone = self.next_token(); @@ -1728,19 +1729,19 @@ impl<'a> Parser<'a> { | Keyword::ILIKE | Keyword::SIMILAR => { self.prev_token(); - let negated = self.parse_keyword(Keyword::NOT); - if self.parse_keyword(Keyword::IN) { + let negated = self.parse_keyword(Keyword::NOT).is_some(); + if self.parse_keyword(Keyword::IN).is_some() { self.parse_in(expr, negated) - } else if self.parse_keyword(Keyword::BETWEEN) { + } else if self.parse_keyword(Keyword::BETWEEN).is_some() { self.parse_between(expr, negated) - } else if self.parse_keyword(Keyword::LIKE) { + } else if self.parse_keyword(Keyword::LIKE).is_some() { Ok(Expr::Like { negated, expr: Box::new(expr), pattern: Box::new(self.parse_subexpr(Self::LIKE_PREC)?), escape_char: self.parse_escape_char()?, }) - } else if self.parse_keyword(Keyword::ILIKE) { + } else if self.parse_keyword(Keyword::ILIKE).is_some() { Ok(Expr::ILike { negated, expr: Box::new(expr), @@ -1816,7 +1817,7 @@ impl<'a> Parser<'a> { /// parse the ESCAPE CHAR portion of LIKE, ILIKE, and SIMILAR TO pub fn parse_escape_char(&mut self) -> Result, ParserError> { - if self.parse_keyword(Keyword::ESCAPE) { + if self.parse_keyword(Keyword::ESCAPE).is_some() { Ok(Some(self.parse_literal_char()?)) } else { Ok(None) @@ -1862,7 +1863,7 @@ impl<'a> Parser<'a> { pub fn parse_in(&mut self, expr: Expr, negated: bool) -> Result { // BigQuery allows `IN UNNEST(array_expression)` // https://cloud.google.com/bigquery/docs/reference/standard-sql/operators#in_operators - if self.parse_keyword(Keyword::UNNEST) { + if self.parse_keyword(Keyword::UNNEST).is_some() { self.expect_token(&Token::LParen)?; let array_expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; @@ -1873,7 +1874,9 @@ impl<'a> Parser<'a> { }); } self.expect_token(&Token::LParen)?; - let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) { + let in_op = if self.parse_keyword(Keyword::SELECT).is_some() + || self.parse_keyword(Keyword::WITH).is_some() + { self.prev_token(); Expr::InSubquery { expr: Box::new(expr), @@ -2088,12 +2091,17 @@ impl<'a> Parser<'a> { /// Look for an expected keyword and consume it if it exists #[must_use] - pub fn parse_keyword(&mut self, expected: Keyword) -> bool { + pub fn parse_keyword(&mut self, expected: Keyword) -> Option { match self.peek_token().token { - Token::Word(w) if expected == w.keyword => { - self.next_token(); - true - } + Token::Word(w) if expected == w.keyword => Some(self.next_token()), + _ => None, + } + } + + #[must_use] + pub fn peek_keyword(&mut self, expected: Keyword) -> bool { + match self.peek_token().token { + Token::Word(w) if expected == w.keyword => true, _ => false, } } @@ -2103,7 +2111,7 @@ impl<'a> Parser<'a> { pub fn parse_keywords(&mut self, keywords: &[Keyword]) -> bool { let index = self.index; for &keyword in keywords { - if !self.parse_keyword(keyword) { + if !self.parse_keyword(keyword).is_some() { // println!("parse_keywords aborting .. did not find {:?}", keyword); // reset index and return immediately self.index = index; @@ -2144,9 +2152,9 @@ impl<'a> Parser<'a> { } /// 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) { - Ok(()) + pub fn expect_keyword(&mut self, expected: Keyword) -> Result { + if let Some(token) = self.parse_keyword(expected) { + Ok(token) } else { self.expected(format!("{:?}", &expected).as_str(), self.peek_token()) } @@ -2242,8 +2250,8 @@ impl<'a> Parser<'a> { /// 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(Keyword::ALL); - let distinct = self.parse_keyword(Keyword::DISTINCT); + let all = self.parse_keyword(Keyword::ALL).is_some(); + let distinct = self.parse_keyword(Keyword::DISTINCT).is_some(); if all && distinct { parser_err!("Cannot specify both ALL and DISTINCT".to_string()) } else { @@ -2266,33 +2274,35 @@ impl<'a> Parser<'a> { let temporary = self .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY]) .is_some(); - if self.parse_keyword(Keyword::TABLE) { + if self.parse_keyword(Keyword::TABLE).is_some() { self.parse_create_table(or_replace, temporary, global) - } else if self.parse_keyword(Keyword::MATERIALIZED) || self.parse_keyword(Keyword::VIEW) { + } else if self.parse_keyword(Keyword::MATERIALIZED).is_some() + || self.parse_keyword(Keyword::VIEW).is_some() + { self.prev_token(); self.parse_create_view(or_replace) - } else if self.parse_keyword(Keyword::EXTERNAL) { + } else if self.parse_keyword(Keyword::EXTERNAL).is_some() { self.parse_create_external_table(or_replace) - } else if self.parse_keyword(Keyword::FUNCTION) { + } else if self.parse_keyword(Keyword::FUNCTION).is_some() { self.parse_create_function(or_replace, temporary) } else if or_replace { self.expected( "[EXTERNAL] TABLE or [MATERIALIZED] VIEW or FUNCTION after CREATE OR REPLACE", self.peek_token(), ) - } else if self.parse_keyword(Keyword::INDEX) { + } else if self.parse_keyword(Keyword::INDEX).is_some() { self.parse_create_index(false) } else if self.parse_keywords(&[Keyword::UNIQUE, Keyword::INDEX]) { self.parse_create_index(true) - } else if self.parse_keyword(Keyword::VIRTUAL) { + } else if self.parse_keyword(Keyword::VIRTUAL).is_some() { self.parse_create_virtual_table() - } else if self.parse_keyword(Keyword::SCHEMA) { + } else if self.parse_keyword(Keyword::SCHEMA).is_some() { self.parse_create_schema() - } else if self.parse_keyword(Keyword::DATABASE) { + } else if self.parse_keyword(Keyword::DATABASE).is_some() { self.parse_create_database() - } else if self.parse_keyword(Keyword::ROLE) { + } else if self.parse_keyword(Keyword::ROLE).is_some() { self.parse_create_role() - } else if self.parse_keyword(Keyword::SEQUENCE) { + } else if self.parse_keyword(Keyword::SEQUENCE).is_some() { self.parse_create_sequence(temporary) } else { self.expected("an object type after CREATE", self.peek_token()) @@ -2302,7 +2312,7 @@ impl<'a> Parser<'a> { /// Parse a CACHE TABLE statement pub fn parse_cache_table(&mut self) -> Result { let (mut table_flag, mut options, mut has_as, mut query) = (None, vec![], false, None); - if self.parse_keyword(Keyword::TABLE) { + if self.parse_keyword(Keyword::TABLE).is_some() { let table_name = self.parse_object_name()?; if self.peek_token().token != Token::EOF { if let Token::Word(word) = self.peek_token().token { @@ -2335,7 +2345,7 @@ impl<'a> Parser<'a> { } } else { table_flag = Some(self.parse_object_name()?); - if self.parse_keyword(Keyword::TABLE) { + if self.parse_keyword(Keyword::TABLE).is_some() { let table_name = self.parse_object_name()?; if self.peek_token() != Token::EOF { if let Token::Word(word) = self.peek_token().token { @@ -2391,7 +2401,7 @@ impl<'a> Parser<'a> { /// Parse a UNCACHE TABLE statement pub fn parse_uncache_table(&mut self) -> Result { - let has_table = self.parse_keyword(Keyword::TABLE); + let has_table = self.parse_keyword(Keyword::TABLE).is_some(); if has_table { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let table_name = self.parse_object_name()?; @@ -2440,12 +2450,12 @@ impl<'a> Parser<'a> { } fn parse_schema_name(&mut self) -> Result { - if self.parse_keyword(Keyword::AUTHORIZATION) { + if self.parse_keyword(Keyword::AUTHORIZATION).is_some() { Ok(SchemaName::UnnamedAuthorization(self.parse_identifier()?)) } else { let name = self.parse_object_name()?; - if self.parse_keyword(Keyword::AUTHORIZATION) { + if self.parse_keyword(Keyword::AUTHORIZATION).is_some() { Ok(SchemaName::NamedAuthorization( name, self.parse_identifier()?, @@ -2481,7 +2491,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_create_function_using( &mut self, ) -> Result, ParserError> { - if !self.parse_keyword(Keyword::USING) { + if !self.parse_keyword(Keyword::USING).is_some() { return Ok(None); }; let keyword = @@ -2535,7 +2545,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::RParen)?; - let return_type = if self.parse_keyword(Keyword::RETURNS) { + let return_type = if self.parse_keyword(Keyword::RETURNS).is_some() { Some(self.parse_data_type()?) } else { None @@ -2558,11 +2568,11 @@ impl<'a> Parser<'a> { } fn parse_function_arg(&mut self) -> Result { - let mode = if self.parse_keyword(Keyword::IN) { + let mode = if self.parse_keyword(Keyword::IN).is_some() { Some(ArgMode::In) - } else if self.parse_keyword(Keyword::OUT) { + } else if self.parse_keyword(Keyword::OUT).is_some() { Some(ArgMode::Out) - } else if self.parse_keyword(Keyword::INOUT) { + } else if self.parse_keyword(Keyword::INOUT).is_some() { Some(ArgMode::InOut) } else { None @@ -2577,12 +2587,12 @@ impl<'a> Parser<'a> { data_type = self.parse_data_type()?; } - let default_expr = if self.parse_keyword(Keyword::DEFAULT) || self.consume_token(&Token::Eq) - { - Some(self.parse_expr()?) - } else { - None - }; + let default_expr = + if self.parse_keyword(Keyword::DEFAULT).is_some() || self.consume_token(&Token::Eq) { + Some(self.parse_expr()?) + } else { + None + }; Ok(OperateFunctionArg { mode, name, @@ -2602,22 +2612,22 @@ impl<'a> Parser<'a> { } Ok(()) } - if self.parse_keyword(Keyword::AS) { + if self.parse_keyword(Keyword::AS).is_some() { ensure_not_set(&body.as_, "AS")?; body.as_ = Some(self.parse_function_definition()?); - } else if self.parse_keyword(Keyword::LANGUAGE) { + } else if self.parse_keyword(Keyword::LANGUAGE).is_some() { ensure_not_set(&body.language, "LANGUAGE")?; body.language = Some(self.parse_identifier()?); - } else if self.parse_keyword(Keyword::IMMUTABLE) { + } else if self.parse_keyword(Keyword::IMMUTABLE).is_some() { ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; body.behavior = Some(FunctionBehavior::Immutable); - } else if self.parse_keyword(Keyword::STABLE) { + } else if self.parse_keyword(Keyword::STABLE).is_some() { ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; body.behavior = Some(FunctionBehavior::Stable); - } else if self.parse_keyword(Keyword::VOLATILE) { + } else if self.parse_keyword(Keyword::VOLATILE).is_some() { ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; body.behavior = Some(FunctionBehavior::Volatile); - } else if self.parse_keyword(Keyword::RETURN) { + } else if self.parse_keyword(Keyword::RETURN).is_some() { ensure_not_set(&body.return_, "RETURN")?; body.return_ = Some(self.parse_expr()?); } else { @@ -2693,7 +2703,7 @@ impl<'a> Parser<'a> { } pub fn parse_create_view(&mut self, or_replace: bool) -> Result { - let materialized = self.parse_keyword(Keyword::MATERIALIZED); + let materialized = self.parse_keyword(Keyword::MATERIALIZED).is_some(); self.expect_keyword(Keyword::VIEW)?; // Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet). // ANSI SQL and Postgres support RECURSIVE here, but we don't support it either. @@ -2701,7 +2711,7 @@ impl<'a> Parser<'a> { let columns = self.parse_parenthesized_column_list(Optional, false)?; let with_options = self.parse_options(Keyword::WITH)?; - let cluster_by = if self.parse_keyword(Keyword::CLUSTER) { + let cluster_by = if self.parse_keyword(Keyword::CLUSTER).is_some() { self.expect_keyword(Keyword::BY)?; self.parse_parenthesized_column_list(Optional, false)? } else { @@ -2726,7 +2736,7 @@ impl<'a> Parser<'a> { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let names = self.parse_comma_separated(Parser::parse_object_name)?; - let _ = self.parse_keyword(Keyword::WITH); // [ WITH ] + let _ = self.parse_keyword(Keyword::WITH).is_some(); // [ WITH ] let optional_keywords = if dialect_of!(self is MsSqlDialect) { vec![Keyword::AUTHORIZATION] @@ -2847,7 +2857,7 @@ impl<'a> Parser<'a> { if password.is_some() { parser_err!("Found multiple PASSWORD") } else { - password = if self.parse_keyword(Keyword::NULL) { + password = if self.parse_keyword(Keyword::NULL).is_some() { Some(Password::NullPassword) } else { Some(Password::Password(Expr::Value(self.parse_value()?))) @@ -2874,14 +2884,14 @@ impl<'a> Parser<'a> { } } Keyword::IN => { - if self.parse_keyword(Keyword::ROLE) { + if self.parse_keyword(Keyword::ROLE).is_some() { if !in_role.is_empty() { parser_err!("Found multiple IN ROLE") } else { in_role = self.parse_comma_separated(Parser::parse_identifier)?; Ok(()) } - } else if self.parse_keyword(Keyword::GROUP) { + } else if self.parse_keyword(Keyword::GROUP).is_some() { if !in_group.is_empty() { parser_err!("Found multiple IN GROUP") } else { @@ -2943,19 +2953,19 @@ impl<'a> Parser<'a> { } pub fn parse_drop(&mut self) -> Result { - let object_type = if self.parse_keyword(Keyword::TABLE) { + let object_type = if self.parse_keyword(Keyword::TABLE).is_some() { ObjectType::Table - } else if self.parse_keyword(Keyword::VIEW) { + } else if self.parse_keyword(Keyword::VIEW).is_some() { ObjectType::View - } else if self.parse_keyword(Keyword::INDEX) { + } else if self.parse_keyword(Keyword::INDEX).is_some() { ObjectType::Index - } else if self.parse_keyword(Keyword::ROLE) { + } else if self.parse_keyword(Keyword::ROLE).is_some() { ObjectType::Role - } else if self.parse_keyword(Keyword::SCHEMA) { + } else if self.parse_keyword(Keyword::SCHEMA).is_some() { ObjectType::Schema - } else if self.parse_keyword(Keyword::SEQUENCE) { + } else if self.parse_keyword(Keyword::SEQUENCE).is_some() { ObjectType::Sequence - } else if self.parse_keyword(Keyword::FUNCTION) { + } else if self.parse_keyword(Keyword::FUNCTION).is_some() { return self.parse_drop_function(); } else { return self.expected( @@ -2967,9 +2977,9 @@ impl<'a> Parser<'a> { // specifying multiple objects to delete in a single statement let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let names = self.parse_comma_separated(Parser::parse_object_name)?; - let cascade = self.parse_keyword(Keyword::CASCADE); - let restrict = self.parse_keyword(Keyword::RESTRICT); - let purge = self.parse_keyword(Keyword::PURGE); + let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); + let restrict = self.parse_keyword(Keyword::RESTRICT).is_some(); + let purge = self.parse_keyword(Keyword::PURGE).is_some(); if cascade && restrict { return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP"); } @@ -3030,15 +3040,15 @@ impl<'a> Parser<'a> { pub fn parse_declare(&mut self) -> Result { let name = self.parse_identifier()?; - let binary = self.parse_keyword(Keyword::BINARY); - let sensitive = if self.parse_keyword(Keyword::INSENSITIVE) { + let binary = self.parse_keyword(Keyword::BINARY).is_some(); + let sensitive = if self.parse_keyword(Keyword::INSENSITIVE).is_some() { Some(true) - } else if self.parse_keyword(Keyword::ASENSITIVE) { + } else if self.parse_keyword(Keyword::ASENSITIVE).is_some() { Some(false) } else { None }; - let scroll = if self.parse_keyword(Keyword::SCROLL) { + let scroll = if self.parse_keyword(Keyword::SCROLL).is_some() { Some(true) } else if self.parse_keywords(&[Keyword::NO, Keyword::SCROLL]) { Some(false) @@ -3077,24 +3087,24 @@ impl<'a> Parser<'a> { // FETCH [ direction { FROM | IN } ] cursor INTO target; pub fn parse_fetch_statement(&mut self) -> Result { - let direction = if self.parse_keyword(Keyword::NEXT) { + let direction = if self.parse_keyword(Keyword::NEXT).is_some() { FetchDirection::Next - } else if self.parse_keyword(Keyword::PRIOR) { + } else if self.parse_keyword(Keyword::PRIOR).is_some() { FetchDirection::Prior - } else if self.parse_keyword(Keyword::FIRST) { + } else if self.parse_keyword(Keyword::FIRST).is_some() { FetchDirection::First - } else if self.parse_keyword(Keyword::LAST) { + } else if self.parse_keyword(Keyword::LAST).is_some() { FetchDirection::Last - } else if self.parse_keyword(Keyword::ABSOLUTE) { + } else if self.parse_keyword(Keyword::ABSOLUTE).is_some() { FetchDirection::Absolute { limit: self.parse_number_value()?, } - } else if self.parse_keyword(Keyword::RELATIVE) { + } else if self.parse_keyword(Keyword::RELATIVE).is_some() { FetchDirection::Relative { limit: self.parse_number_value()?, } - } else if self.parse_keyword(Keyword::FORWARD) { - if self.parse_keyword(Keyword::ALL) { + } else if self.parse_keyword(Keyword::FORWARD).is_some() { + if self.parse_keyword(Keyword::ALL).is_some() { FetchDirection::ForwardAll } else { FetchDirection::Forward { @@ -3102,8 +3112,8 @@ impl<'a> Parser<'a> { limit: Some(self.parse_number_value()?), } } - } else if self.parse_keyword(Keyword::BACKWARD) { - if self.parse_keyword(Keyword::ALL) { + } else if self.parse_keyword(Keyword::BACKWARD).is_some() { + if self.parse_keyword(Keyword::ALL).is_some() { FetchDirection::BackwardAll } else { FetchDirection::Backward { @@ -3111,7 +3121,7 @@ impl<'a> Parser<'a> { limit: Some(self.parse_number_value()?), } } - } else if self.parse_keyword(Keyword::ALL) { + } else if self.parse_keyword(Keyword::ALL).is_some() { FetchDirection::All } else { FetchDirection::Count { @@ -3123,7 +3133,7 @@ impl<'a> Parser<'a> { let name = self.parse_identifier()?; - let into = if self.parse_keyword(Keyword::INTO) { + let into = if self.parse_keyword(Keyword::INTO).is_some() { Some(self.parse_object_name()?) } else { None @@ -3137,13 +3147,15 @@ impl<'a> Parser<'a> { } pub fn parse_discard(&mut self) -> Result { - let object_type = if self.parse_keyword(Keyword::ALL) { + let object_type = if self.parse_keyword(Keyword::ALL).is_some() { DiscardObject::ALL - } else if self.parse_keyword(Keyword::PLANS) { + } else if self.parse_keyword(Keyword::PLANS).is_some() { DiscardObject::PLANS - } else if self.parse_keyword(Keyword::SEQUENCES) { + } else if self.parse_keyword(Keyword::SEQUENCES).is_some() { DiscardObject::SEQUENCES - } else if self.parse_keyword(Keyword::TEMP) || self.parse_keyword(Keyword::TEMPORARY) { + } else if self.parse_keyword(Keyword::TEMP).is_some() + || self.parse_keyword(Keyword::TEMPORARY).is_some() + { DiscardObject::TEMP } else { return self.expected( @@ -3159,7 +3171,7 @@ impl<'a> Parser<'a> { let index_name = self.parse_object_name()?; self.expect_keyword(Keyword::ON)?; let table_name = self.parse_object_name()?; - let using = if self.parse_keyword(Keyword::USING) { + let using = if self.parse_keyword(Keyword::USING).is_some() { Some(self.parse_identifier()?) } else { None @@ -3198,7 +3210,7 @@ impl<'a> Parser<'a> { } Some(Keyword::STORED) => { self.expect_keyword(Keyword::AS)?; - if self.parse_keyword(Keyword::INPUTFORMAT) { + if self.parse_keyword(Keyword::INPUTFORMAT).is_some() { let input_format = self.parse_expr()?; self.expect_keyword(Keyword::OUTPUTFORMAT)?; let output_format = self.parse_expr()?; @@ -3254,13 +3266,15 @@ impl<'a> Parser<'a> { None }; - let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) { + let like = if self.parse_keyword(Keyword::LIKE).is_some() + || self.parse_keyword(Keyword::ILIKE).is_some() + { self.parse_object_name().ok() } else { None }; - let clone = if self.parse_keyword(Keyword::CLONE) { + let clone = if self.parse_keyword(Keyword::CLONE).is_some() { self.parse_object_name().ok() } else { None @@ -3278,13 +3292,13 @@ impl<'a> Parser<'a> { let with_options = self.parse_options(Keyword::WITH)?; let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?; // Parse optional `AS ( query )` - let query = if self.parse_keyword(Keyword::AS) { + let query = if self.parse_keyword(Keyword::AS).is_some() { Some(Box::new(self.parse_query()?)) } else { None }; - let engine = if self.parse_keyword(Keyword::ENGINE) { + let engine = if self.parse_keyword(Keyword::ENGINE).is_some() { self.expect_token(&Token::Eq)?; let next_token = self.next_token(); match next_token.token { @@ -3387,14 +3401,14 @@ impl<'a> Parser<'a> { pub fn parse_column_def(&mut self) -> Result { let name = self.parse_identifier()?; let data_type = self.parse_data_type()?; - let collation = if self.parse_keyword(Keyword::COLLATE) { + let collation = if self.parse_keyword(Keyword::COLLATE).is_some() { Some(self.parse_object_name()?) } else { None }; let mut options = vec![]; loop { - if self.parse_keyword(Keyword::CONSTRAINT) { + if self.parse_keyword(Keyword::CONSTRAINT).is_some() { let name = Some(self.parse_identifier()?); if let Some(option) = self.parse_optional_column_option()? { options.push(ColumnOptionDef { name, option }); @@ -3429,15 +3443,15 @@ impl<'a> Parser<'a> { Token::SingleQuotedString(value, ..) => Ok(Some(ColumnOption::Comment(value))), _ => self.expected("string", next_token), } - } else if self.parse_keyword(Keyword::NULL) { + } else if self.parse_keyword(Keyword::NULL).is_some() { Ok(Some(ColumnOption::Null)) - } else if self.parse_keyword(Keyword::DEFAULT) { + } else if self.parse_keyword(Keyword::DEFAULT).is_some() { Ok(Some(ColumnOption::Default(self.parse_expr()?))) } else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) { Ok(Some(ColumnOption::Unique { is_primary: true })) - } else if self.parse_keyword(Keyword::UNIQUE) { + } else if self.parse_keyword(Keyword::UNIQUE).is_some() { Ok(Some(ColumnOption::Unique { is_primary: false })) - } else if self.parse_keyword(Keyword::REFERENCES) { + } else if self.parse_keyword(Keyword::REFERENCES).is_some() { 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 @@ -3461,19 +3475,19 @@ impl<'a> Parser<'a> { on_delete, on_update, })) - } else if self.parse_keyword(Keyword::CHECK) { + } else if self.parse_keyword(Keyword::CHECK).is_some() { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; Ok(Some(ColumnOption::Check(expr))) - } else if self.parse_keyword(Keyword::AUTO_INCREMENT) + } else if self.parse_keyword(Keyword::AUTO_INCREMENT).is_some() && dialect_of!(self is MySqlDialect | GenericDialect) { // Support AUTO_INCREMENT for MySQL Ok(Some(ColumnOption::DialectSpecific(vec![ Token::make_keyword("AUTO_INCREMENT"), ]))) - } else if self.parse_keyword(Keyword::AUTOINCREMENT) + } else if self.parse_keyword(Keyword::AUTOINCREMENT).is_some() && dialect_of!(self is SQLiteDialect | GenericDialect) { // Support AUTOINCREMENT for SQLite @@ -3491,9 +3505,9 @@ impl<'a> Parser<'a> { } pub fn parse_referential_action(&mut self) -> Result { - if self.parse_keyword(Keyword::RESTRICT) { + if self.parse_keyword(Keyword::RESTRICT).is_some() { Ok(ReferentialAction::Restrict) - } else if self.parse_keyword(Keyword::CASCADE) { + } else if self.parse_keyword(Keyword::CASCADE).is_some() { Ok(ReferentialAction::Cascade) } else if self.parse_keywords(&[Keyword::SET, Keyword::NULL]) { Ok(ReferentialAction::SetNull) @@ -3512,7 +3526,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_table_constraint( &mut self, ) -> Result, ParserError> { - let name = if self.parse_keyword(Keyword::CONSTRAINT) { + let name = if self.parse_keyword(Keyword::CONSTRAINT).is_some() { Some(self.parse_identifier()?) } else { None @@ -3577,7 +3591,7 @@ impl<'a> Parser<'a> { _ => self.maybe_parse(|parser| parser.parse_identifier()), }; - let index_type = if self.parse_keyword(Keyword::USING) { + let index_type = if self.parse_keyword(Keyword::USING).is_some() { Some(self.parse_index_type()?) } else { None @@ -3607,9 +3621,9 @@ impl<'a> Parser<'a> { let fulltext = w.keyword == Keyword::FULLTEXT; - let index_type_display = if self.parse_keyword(Keyword::KEY) { + let index_type_display = if self.parse_keyword(Keyword::KEY).is_some() { KeyOrIndexDisplay::Key - } else if self.parse_keyword(Keyword::INDEX) { + } else if self.parse_keyword(Keyword::INDEX).is_some() { KeyOrIndexDisplay::Index } else { KeyOrIndexDisplay::None @@ -3638,7 +3652,7 @@ impl<'a> Parser<'a> { } pub fn parse_options(&mut self, keyword: Keyword) -> Result, ParserError> { - if self.parse_keyword(keyword) { + if self.parse_keyword(keyword).is_some() { self.expect_token(&Token::LParen)?; let options = self.parse_comma_separated(Parser::parse_sql_option)?; self.expect_token(&Token::RParen)?; @@ -3649,9 +3663,9 @@ impl<'a> Parser<'a> { } pub fn parse_index_type(&mut self) -> Result { - if self.parse_keyword(Keyword::BTREE) { + if self.parse_keyword(Keyword::BTREE).is_some() { Ok(IndexType::BTree) - } else if self.parse_keyword(Keyword::HASH) { + } else if self.parse_keyword(Keyword::HASH).is_some() { Ok(IndexType::Hash) } else { self.expected("index type {BTREE | HASH}", self.peek_token()) @@ -3669,15 +3683,15 @@ impl<'a> Parser<'a> { let object_type = self.expect_one_of_keywords(&[Keyword::TABLE, Keyword::INDEX])?; match object_type { Keyword::TABLE => { - let _ = self.parse_keyword(Keyword::ONLY); // [ ONLY ] + let _ = self.parse_keyword(Keyword::ONLY).is_some(); // [ ONLY ] let table_name = self.parse_object_name()?; - let operation = if self.parse_keyword(Keyword::ADD) { + let operation = if self.parse_keyword(Keyword::ADD).is_some() { if let Some(constraint) = self.parse_optional_table_constraint()? { AlterTableOperation::AddConstraint(constraint) } else { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - if self.parse_keyword(Keyword::PARTITION) { + if self.parse_keyword(Keyword::PARTITION).is_some() { self.expect_token(&Token::LParen)?; let partitions = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; @@ -3686,7 +3700,7 @@ impl<'a> Parser<'a> { new_partitions: partitions, } } else { - let column_keyword = self.parse_keyword(Keyword::COLUMN); + let column_keyword = self.parse_keyword(Keyword::COLUMN).is_some(); let if_not_exists = if dialect_of!(self is PostgreSqlDialect | BigQueryDialect | GenericDialect) { @@ -3704,19 +3718,19 @@ impl<'a> Parser<'a> { } } } - } else if self.parse_keyword(Keyword::RENAME) { + } else if self.parse_keyword(Keyword::RENAME).is_some() { if dialect_of!(self is PostgreSqlDialect) - && self.parse_keyword(Keyword::CONSTRAINT) + && self.parse_keyword(Keyword::CONSTRAINT).is_some() { let old_name = self.parse_identifier()?; self.expect_keyword(Keyword::TO)?; let new_name = self.parse_identifier()?; AlterTableOperation::RenameConstraint { old_name, new_name } - } else if self.parse_keyword(Keyword::TO) { + } else if self.parse_keyword(Keyword::TO).is_some() { let table_name = self.parse_object_name()?; AlterTableOperation::RenameTable { table_name } } else { - let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] + let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] let old_column_name = self.parse_identifier()?; self.expect_keyword(Keyword::TO)?; let new_column_name = self.parse_identifier()?; @@ -3725,7 +3739,7 @@ impl<'a> Parser<'a> { new_column_name, } } - } else if self.parse_keyword(Keyword::DROP) { + } else if self.parse_keyword(Keyword::DROP).is_some() { if self.parse_keywords(&[Keyword::IF, Keyword::EXISTS, Keyword::PARTITION]) { self.expect_token(&Token::LParen)?; let partitions = self.parse_comma_separated(Parser::parse_expr)?; @@ -3734,7 +3748,7 @@ impl<'a> Parser<'a> { partitions, if_exists: true, } - } else if self.parse_keyword(Keyword::PARTITION) { + } else if self.parse_keyword(Keyword::PARTITION).is_some() { self.expect_token(&Token::LParen)?; let partitions = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; @@ -3742,10 +3756,10 @@ impl<'a> Parser<'a> { partitions, if_exists: false, } - } else if self.parse_keyword(Keyword::CONSTRAINT) { + } else if self.parse_keyword(Keyword::CONSTRAINT).is_some() { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let name = self.parse_identifier()?; - let cascade = self.parse_keyword(Keyword::CASCADE); + let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); AlterTableOperation::DropConstraint { if_exists, name, @@ -3756,17 +3770,17 @@ impl<'a> Parser<'a> { { AlterTableOperation::DropPrimaryKey } else { - let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] + let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let column_name = self.parse_identifier()?; - let cascade = self.parse_keyword(Keyword::CASCADE); + let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); AlterTableOperation::DropColumn { column_name, if_exists, cascade, } } - } else if self.parse_keyword(Keyword::PARTITION) { + } else if self.parse_keyword(Keyword::PARTITION).is_some() { self.expect_token(&Token::LParen)?; let before = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; @@ -3779,8 +3793,8 @@ impl<'a> Parser<'a> { old_partitions: before, new_partitions: renames, } - } else if self.parse_keyword(Keyword::CHANGE) { - let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] + } else if self.parse_keyword(Keyword::CHANGE).is_some() { + let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] let old_name = self.parse_identifier()?; let new_name = self.parse_identifier()?; let data_type = self.parse_data_type()?; @@ -3795,8 +3809,8 @@ impl<'a> Parser<'a> { data_type, options, } - } else if self.parse_keyword(Keyword::ALTER) { - let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] + } else if self.parse_keyword(Keyword::ALTER).is_some() { + let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] let column_name = self.parse_identifier()?; let is_postgresql = dialect_of!(self is PostgreSqlDialect); @@ -3811,10 +3825,11 @@ impl<'a> Parser<'a> { } else if self.parse_keywords(&[Keyword::DROP, Keyword::DEFAULT]) { AlterColumnOperation::DropDefault {} } else if self.parse_keywords(&[Keyword::SET, Keyword::DATA, Keyword::TYPE]) - || (is_postgresql && self.parse_keyword(Keyword::TYPE)) + || (is_postgresql && self.parse_keyword(Keyword::TYPE).is_some()) { let data_type = self.parse_data_type()?; - let using = if is_postgresql && self.parse_keyword(Keyword::USING) { + let using = if is_postgresql && self.parse_keyword(Keyword::USING).is_some() + { Some(self.parse_expr()?) } else { None @@ -3840,8 +3855,8 @@ impl<'a> Parser<'a> { } Keyword::INDEX => { let index_name = self.parse_object_name()?; - let operation = if self.parse_keyword(Keyword::RENAME) { - if self.parse_keyword(Keyword::TO) { + let operation = if self.parse_keyword(Keyword::RENAME).is_some() { + if self.parse_keyword(Keyword::TO).is_some() { let index_name = self.parse_object_name()?; AlterIndexOperation::RenameIndex { index_name } } else { @@ -3870,11 +3885,11 @@ impl<'a> Parser<'a> { Some(Keyword::TO) => true, _ => self.expected("FROM or TO", self.peek_token())?, }; - let target = if self.parse_keyword(Keyword::STDIN) { + let target = if self.parse_keyword(Keyword::STDIN).is_some() { CopyTarget::Stdin - } else if self.parse_keyword(Keyword::STDOUT) { + } else if self.parse_keyword(Keyword::STDOUT).is_some() { CopyTarget::Stdout - } else if self.parse_keyword(Keyword::PROGRAM) { + } else if self.parse_keyword(Keyword::PROGRAM).is_some() { CopyTarget::Program { command: self.parse_literal_string()?, } @@ -3883,7 +3898,7 @@ impl<'a> Parser<'a> { filename: self.parse_literal_string()?, } }; - let _ = self.parse_keyword(Keyword::WITH); // [ WITH ] + let _ = self.parse_keyword(Keyword::WITH).is_some(); // [ WITH ] let mut options = vec![]; if self.consume_token(&Token::LParen) { options = self.parse_comma_separated(Parser::parse_copy_option)?; @@ -3911,7 +3926,7 @@ impl<'a> Parser<'a> { } pub fn parse_close(&mut self) -> Result { - let cursor = if self.parse_keyword(Keyword::ALL) { + let cursor = if self.parse_keyword(Keyword::ALL).is_some() { CloseCursor::All } else { let name = self.parse_identifier()?; @@ -3973,11 +3988,11 @@ impl<'a> Parser<'a> { ]) { Some(Keyword::BINARY) => CopyLegacyOption::Binary, Some(Keyword::DELIMITER) => { - let _ = self.parse_keyword(Keyword::AS); // [ AS ] + let _ = self.parse_keyword(Keyword::AS).is_some(); // [ AS ] CopyLegacyOption::Delimiter(self.parse_literal_char()?) } Some(Keyword::NULL) => { - let _ = self.parse_keyword(Keyword::AS); // [ AS ] + let _ = self.parse_keyword(Keyword::AS).is_some(); // [ AS ] CopyLegacyOption::Null(self.parse_literal_string()?) } Some(Keyword::CSV) => CopyLegacyOption::Csv({ @@ -4003,11 +4018,11 @@ impl<'a> Parser<'a> { ]) { Some(Keyword::HEADER) => CopyLegacyCsvOption::Header, Some(Keyword::QUOTE) => { - let _ = self.parse_keyword(Keyword::AS); // [ AS ] + let _ = self.parse_keyword(Keyword::AS).is_some(); // [ AS ] CopyLegacyCsvOption::Quote(self.parse_literal_char()?) } Some(Keyword::ESCAPE) => { - let _ = self.parse_keyword(Keyword::AS); // [ AS ] + let _ = self.parse_keyword(Keyword::AS).is_some(); // [ AS ] CopyLegacyCsvOption::Escape(self.parse_literal_char()?) } Some(Keyword::FORCE) if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) => { @@ -4213,7 +4228,7 @@ impl<'a> Parser<'a> { Keyword::FLOAT => Ok(DataType::Float(self.parse_optional_precision()?)), Keyword::REAL => Ok(DataType::Real), Keyword::DOUBLE => { - if self.parse_keyword(Keyword::PRECISION) { + if self.parse_keyword(Keyword::PRECISION).is_some() { Ok(DataType::DoublePrecision) } else { Ok(DataType::Double) @@ -4221,7 +4236,7 @@ impl<'a> Parser<'a> { } Keyword::TINYINT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED) { + if self.parse_keyword(Keyword::UNSIGNED).is_some() { Ok(DataType::UnsignedTinyInt(optional_precision?)) } else { Ok(DataType::TinyInt(optional_precision?)) @@ -4229,7 +4244,7 @@ impl<'a> Parser<'a> { } Keyword::SMALLINT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED) { + if self.parse_keyword(Keyword::UNSIGNED).is_some() { Ok(DataType::UnsignedSmallInt(optional_precision?)) } else { Ok(DataType::SmallInt(optional_precision?)) @@ -4237,7 +4252,7 @@ impl<'a> Parser<'a> { } Keyword::MEDIUMINT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED) { + if self.parse_keyword(Keyword::UNSIGNED).is_some() { Ok(DataType::UnsignedMediumInt(optional_precision?)) } else { Ok(DataType::MediumInt(optional_precision?)) @@ -4245,7 +4260,7 @@ impl<'a> Parser<'a> { } Keyword::INT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED) { + if self.parse_keyword(Keyword::UNSIGNED).is_some() { Ok(DataType::UnsignedInt(optional_precision?)) } else { Ok(DataType::Int(optional_precision?)) @@ -4253,7 +4268,7 @@ impl<'a> Parser<'a> { } Keyword::INTEGER => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED) { + if self.parse_keyword(Keyword::UNSIGNED).is_some() { Ok(DataType::UnsignedInteger(optional_precision?)) } else { Ok(DataType::Integer(optional_precision?)) @@ -4261,7 +4276,7 @@ impl<'a> Parser<'a> { } Keyword::BIGINT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED) { + if self.parse_keyword(Keyword::UNSIGNED).is_some() { Ok(DataType::UnsignedBigInt(optional_precision?)) } else { Ok(DataType::BigInt(optional_precision?)) @@ -4270,7 +4285,7 @@ impl<'a> Parser<'a> { Keyword::VARCHAR => Ok(DataType::Varchar(self.parse_optional_character_length()?)), Keyword::NVARCHAR => Ok(DataType::Nvarchar(self.parse_optional_precision()?)), Keyword::CHARACTER => { - if self.parse_keyword(Keyword::VARYING) { + if self.parse_keyword(Keyword::VARYING).is_some() { Ok(DataType::CharacterVarying( self.parse_optional_character_length()?, )) @@ -4283,7 +4298,7 @@ impl<'a> Parser<'a> { } } Keyword::CHAR => { - if self.parse_keyword(Keyword::VARYING) { + if self.parse_keyword(Keyword::VARYING).is_some() { Ok(DataType::CharVarying( self.parse_optional_character_length()?, )) @@ -4302,10 +4317,10 @@ impl<'a> Parser<'a> { Keyword::DATETIME => Ok(DataType::Datetime(self.parse_optional_precision()?)), Keyword::TIMESTAMP => { let precision = self.parse_optional_precision()?; - let tz = if self.parse_keyword(Keyword::WITH) { + let tz = if self.parse_keyword(Keyword::WITH).is_some() { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithTimeZone - } else if self.parse_keyword(Keyword::WITHOUT) { + } else if self.parse_keyword(Keyword::WITHOUT).is_some() { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithoutTimeZone } else { @@ -4319,10 +4334,10 @@ impl<'a> Parser<'a> { )), Keyword::TIME => { let precision = self.parse_optional_precision()?; - let tz = if self.parse_keyword(Keyword::WITH) { + let tz = if self.parse_keyword(Keyword::WITH).is_some() { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithTimeZone - } else if self.parse_keyword(Keyword::WITHOUT) { + } else if self.parse_keyword(Keyword::WITHOUT).is_some() { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithoutTimeZone } else { @@ -4422,7 +4437,7 @@ impl<'a> Parser<'a> { &mut self, reserved_kwds: &[Keyword], ) -> Result, ParserError> { - let after_as = self.parse_keyword(Keyword::AS); + let after_as = self.parse_keyword(Keyword::AS).is_some(); let next_token = self.next_token(); match next_token.token { // Accept any identifier after `AS` (though many dialects have restrictions on @@ -4431,7 +4446,7 @@ impl<'a> Parser<'a> { // (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.contains(&w.keyword) => { - Ok(Some(w.to_ident())) + Ok(Some(w.to_ident(next_token.span))) } // MSSQL supports single-quoted strings as aliases for columns // We accept them as table aliases too, although MSSQL does not. @@ -4445,9 +4460,13 @@ impl<'a> Parser<'a> { // character. When it sees such a , your DBMS will // ignore the and treat the multiple strings as // a single ." - Token::SingleQuotedString(s) => Ok(Some(Ident::with_quote('\'', s))), + Token::SingleQuotedString(s) => { + Ok(Some(Ident::with_quote_and_span('\'', next_token.span, s))) + } // Support for MySql dialect double quoted string, `AS "HOUR"` for example - Token::DoubleQuotedString(s) => Ok(Some(Ident::with_quote('\"', s))), + Token::DoubleQuotedString(s) => { + Ok(Some(Ident::with_quote_and_span('\"', next_token.span, s))) + } _ => { if after_as { return self.expected("an identifier after AS", next_token); @@ -4492,9 +4511,10 @@ impl<'a> Parser<'a> { pub fn parse_identifiers(&mut self) -> Result, ParserError> { let mut idents = vec![]; loop { - match self.peek_token().token { + let token = self.peek_token(); + match token.token { Token::Word(w) => { - idents.push(w.to_ident()); + idents.push(w.to_ident(token.span)); } Token::EOF | Token::Eq => break, _ => {} @@ -4507,12 +4527,15 @@ impl<'a> Parser<'a> { /// Parse a simple one-word identifier (possibly quoted, possibly a keyword) pub fn parse_identifier(&mut self) -> Result { let next_token = self.next_token(); - match next_token.token { - Token::Word(w) => Ok(w.to_ident()), + let mut ident = match next_token.token { + Token::Word(w) => Ok(w.to_ident(next_token.span)), Token::SingleQuotedString(s) => Ok(Ident::with_quote('\'', s)), Token::DoubleQuotedString(s) => Ok(Ident::with_quote('\"', s)), - _ => self.expected("identifier", next_token), - } + _ => self.expected("identifier", next_token.clone()), + }?; + + ident.span = next_token.span; + Ok(ident) } /// Parse a parenthesized comma-separated list of unqualified, possibly quoted identifiers @@ -4568,9 +4591,9 @@ impl<'a> Parser<'a> { pub fn parse_character_length(&mut self) -> Result { let length = self.parse_literal_uint()?; - let unit = if self.parse_keyword(Keyword::CHARACTERS) { + let unit = if self.parse_keyword(Keyword::CHARACTERS).is_some() { Some(CharLengthUnits::Characters) - } else if self.parse_keyword(Keyword::OCTETS) { + } else if self.parse_keyword(Keyword::OCTETS).is_some() { Some(CharLengthUnits::Octets) } else { None @@ -4647,18 +4670,18 @@ impl<'a> Parser<'a> { pub fn parse_delete(&mut self) -> Result { self.expect_keyword(Keyword::FROM)?; let table_name = self.parse_table_factor()?; - let using = if self.parse_keyword(Keyword::USING) { + let using = if self.parse_keyword(Keyword::USING).is_some() { Some(self.parse_table_factor()?) } else { None }; - let selection = if self.parse_keyword(Keyword::WHERE) { + let selection = if self.parse_keyword(Keyword::WHERE).is_some() { Some(self.parse_expr()?) } else { None }; - let returning = if self.parse_keyword(Keyword::RETURNING) { + let returning = if self.parse_keyword(Keyword::RETURNING).is_some() { Some(self.parse_comma_separated(Parser::parse_select_item)?) } else { None @@ -4699,10 +4722,10 @@ impl<'a> Parser<'a> { } pub fn parse_explain(&mut self, describe_alias: bool) -> Result { - let analyze = self.parse_keyword(Keyword::ANALYZE); - let verbose = self.parse_keyword(Keyword::VERBOSE); + let analyze = self.parse_keyword(Keyword::ANALYZE).is_some(); + let verbose = self.parse_keyword(Keyword::VERBOSE).is_some(); let mut format = None; - if self.parse_keyword(Keyword::FORMAT) { + if self.parse_keyword(Keyword::FORMAT).is_some() { format = Some(self.parse_analyze_format()?); } @@ -4733,16 +4756,16 @@ impl<'a> Parser<'a> { /// expect the initial keyword to be already consumed pub fn parse_query(&mut self) -> Result { let _guard = self.recursion_counter.try_decrease()?; - let with = if self.parse_keyword(Keyword::WITH) { + let with = if self.parse_keyword(Keyword::WITH).is_some() { Some(With { - recursive: self.parse_keyword(Keyword::RECURSIVE), + recursive: self.parse_keyword(Keyword::RECURSIVE).is_some(), cte_tables: self.parse_comma_separated(Parser::parse_cte)?, }) } else { None }; - if !self.parse_keyword(Keyword::INSERT) { + if !self.parse_keyword(Keyword::INSERT).is_some() { let body = Box::new(self.parse_query_body(0)?); let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { @@ -4755,11 +4778,11 @@ impl<'a> Parser<'a> { let mut offset = None; for _x in 0..2 { - if limit.is_none() && self.parse_keyword(Keyword::LIMIT) { + if limit.is_none() && self.parse_keyword(Keyword::LIMIT).is_some() { limit = self.parse_limit()? } - if offset.is_none() && self.parse_keyword(Keyword::OFFSET) { + if offset.is_none() && self.parse_keyword(Keyword::OFFSET).is_some() { offset = Some(self.parse_offset()?) } @@ -4778,14 +4801,14 @@ impl<'a> Parser<'a> { } } - let fetch = if self.parse_keyword(Keyword::FETCH) { + let fetch = if self.parse_keyword(Keyword::FETCH).is_some() { Some(self.parse_fetch()?) } else { None }; let mut locks = Vec::new(); - while self.parse_keyword(Keyword::FOR) { + while self.parse_keyword(Keyword::FOR).is_some() { locks.push(self.parse_lock()?); } @@ -4817,7 +4840,7 @@ impl<'a> Parser<'a> { pub fn parse_cte(&mut self) -> Result { let name = self.parse_identifier()?; - let mut cte = if self.parse_keyword(Keyword::AS) { + let mut cte = if self.parse_keyword(Keyword::AS).is_some() { self.expect_token(&Token::LParen)?; let query = Box::new(self.parse_query()?); self.expect_token(&Token::RParen)?; @@ -4843,7 +4866,7 @@ impl<'a> Parser<'a> { from: None, } }; - if self.parse_keyword(Keyword::FROM) { + if self.parse_keyword(Keyword::FROM).is_some() { cte.from = Some(self.parse_identifier()?); } Ok(cte) @@ -4860,17 +4883,17 @@ impl<'a> Parser<'a> { pub 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(Keyword::SELECT) { + let mut expr = if self.peek_keyword(Keyword::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(Keyword::VALUES) { + } else if self.parse_keyword(Keyword::VALUES).is_some() { let is_mysql = dialect_of!(self is MySqlDialect); SetExpr::Values(self.parse_values(is_mysql)?) - } else if self.parse_keyword(Keyword::TABLE) { + } else if self.parse_keyword(Keyword::TABLE).is_some() { SetExpr::Table(Box::new(self.parse_as_table()?)) } else { return self.expected( @@ -4918,18 +4941,18 @@ impl<'a> Parser<'a> { pub fn parse_set_quantifier(&mut self, op: &Option) -> SetQuantifier { match op { Some(SetOperator::Union) => { - if self.parse_keyword(Keyword::ALL) { + if self.parse_keyword(Keyword::ALL).is_some() { SetQuantifier::All - } else if self.parse_keyword(Keyword::DISTINCT) { + } else if self.parse_keyword(Keyword::DISTINCT).is_some() { SetQuantifier::Distinct } else { SetQuantifier::None } } Some(SetOperator::Except) | Some(SetOperator::Intersect) => { - if self.parse_keyword(Keyword::ALL) { + if self.parse_keyword(Keyword::ALL).is_some() { SetQuantifier::All - } else if self.parse_keyword(Keyword::DISTINCT) { + } else if self.parse_keyword(Keyword::DISTINCT).is_some() { SetQuantifier::Distinct } else { SetQuantifier::None @@ -4940,11 +4963,14 @@ impl<'a> Parser<'a> { } /// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`), - /// assuming the initial `SELECT` was already consumed + /// assuming the initial `SELECT` was not already consumed pub fn parse_select(&mut self) -> Result { + let select_token = self.expect_keyword(Keyword::SELECT)?; + dbg!(&select_token); + let distinct = self.parse_all_or_distinct()?; - let top = if self.parse_keyword(Keyword::TOP) { + let top = if self.parse_keyword(Keyword::TOP).is_some() { Some(self.parse_top()?) } else { None @@ -4952,12 +4978,12 @@ impl<'a> Parser<'a> { let projection = self.parse_projection()?; - let into = if self.parse_keyword(Keyword::INTO) { + let into = if self.parse_keyword(Keyword::INTO).is_some() { let temporary = self .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY]) .is_some(); - let unlogged = self.parse_keyword(Keyword::UNLOGGED); - let table = self.parse_keyword(Keyword::TABLE); + let unlogged = self.parse_keyword(Keyword::UNLOGGED).is_some(); + let table = self.parse_keyword(Keyword::TABLE).is_some(); let name = self.parse_object_name()?; Some(SelectInto { temporary, @@ -4974,7 +5000,7 @@ impl<'a> Parser<'a> { // otherwise they may be parsed as an alias as part of the `projection` // or `from`. - let from = if self.parse_keyword(Keyword::FROM) { + let from = if self.parse_keyword(Keyword::FROM).is_some() { self.parse_comma_separated(Parser::parse_table_and_joins)? } else { vec![] @@ -4983,7 +5009,7 @@ impl<'a> Parser<'a> { let mut lateral_views = vec![]; loop { if self.parse_keywords(&[Keyword::LATERAL, Keyword::VIEW]) { - let outer = self.parse_keyword(Keyword::OUTER); + let outer = self.parse_keyword(Keyword::OUTER).is_some(); let lateral_view = self.parse_expr()?; let lateral_view_name = self.parse_object_name()?; let lateral_col_alias = self @@ -5011,7 +5037,7 @@ impl<'a> Parser<'a> { } } - let selection = if self.parse_keyword(Keyword::WHERE) { + let selection = if self.parse_keyword(Keyword::WHERE).is_some() { Some(self.parse_expr()?) } else { None @@ -5041,19 +5067,20 @@ impl<'a> Parser<'a> { vec![] }; - let having = if self.parse_keyword(Keyword::HAVING) { + let having = if self.parse_keyword(Keyword::HAVING).is_some() { Some(self.parse_expr()?) } else { None }; - let qualify = if self.parse_keyword(Keyword::QUALIFY) { + let qualify = if self.parse_keyword(Keyword::QUALIFY).is_some() { Some(self.parse_expr()?) } else { None }; Ok(Select { + select_token, distinct, top, projection, @@ -5120,14 +5147,14 @@ impl<'a> Parser<'a> { self.parse_one_of_keywords(&[Keyword::SESSION, Keyword::LOCAL, Keyword::HIVEVAR]); if let Some(Keyword::HIVEVAR) = modifier { self.expect_token(&Token::Colon)?; - } else if self.parse_keyword(Keyword::ROLE) { + } else if self.parse_keyword(Keyword::ROLE).is_some() { let context_modifier = match modifier { Some(keyword) if keyword == Keyword::LOCAL => ContextModifier::Local, Some(keyword) if keyword == Keyword::SESSION => ContextModifier::Session, _ => ContextModifier::None, }; - let role_name = if self.parse_keyword(Keyword::NONE) { + let role_name = if self.parse_keyword(Keyword::NONE).is_some() { None } else { Some(self.parse_identifier()?) @@ -5147,7 +5174,7 @@ impl<'a> Parser<'a> { if variable.to_string().eq_ignore_ascii_case("NAMES") && dialect_of!(self is MySqlDialect | GenericDialect) { - if self.parse_keyword(Keyword::DEFAULT) { + if self.parse_keyword(Keyword::DEFAULT).is_some() { return Ok(Statement::SetNamesDefault {}); } @@ -5162,7 +5189,7 @@ impl<'a> Parser<'a> { charset_name, collation_name, }) - } else if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { + } else if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO).is_some() { let mut values = vec![]; loop { let value = if let Ok(expr) = self.parse_expr() { @@ -5199,7 +5226,7 @@ impl<'a> Parser<'a> { session: true, }) } else if variable.to_string() == "TRANSACTION" && modifier.is_none() { - if self.parse_keyword(Keyword::SNAPSHOT) { + if self.parse_keyword(Keyword::SNAPSHOT).is_some() { let snaphot_id = self.parse_value()?; return Ok(Statement::SetTransaction { modes: vec![], @@ -5218,16 +5245,16 @@ impl<'a> Parser<'a> { } pub fn parse_show(&mut self) -> Result { - let extended = self.parse_keyword(Keyword::EXTENDED); - let full = self.parse_keyword(Keyword::FULL); + let extended = self.parse_keyword(Keyword::EXTENDED).is_some(); + let full = self.parse_keyword(Keyword::FULL).is_some(); if self .parse_one_of_keywords(&[Keyword::COLUMNS, Keyword::FIELDS]) .is_some() { Ok(self.parse_show_columns(extended, full)?) - } else if self.parse_keyword(Keyword::TABLES) { + } else if self.parse_keyword(Keyword::TABLES).is_some() { Ok(self.parse_show_tables(extended, full)?) - } else if self.parse_keyword(Keyword::FUNCTIONS) { + } else if self.parse_keyword(Keyword::FUNCTIONS).is_some() { Ok(self.parse_show_functions()?) } else if extended || full { Err(ParserError::ParserError( @@ -5235,9 +5262,9 @@ impl<'a> Parser<'a> { )) } else if self.parse_one_of_keywords(&[Keyword::CREATE]).is_some() { Ok(self.parse_show_create()?) - } else if self.parse_keyword(Keyword::COLLATION) { + } else if self.parse_keyword(Keyword::COLLATION).is_some() { Ok(self.parse_show_collation()?) - } else if self.parse_keyword(Keyword::VARIABLES) + } else if self.parse_keyword(Keyword::VARIABLES).is_some() && dialect_of!(self is MySqlDialect | GenericDialect) { // TODO: Support GLOBAL|SESSION @@ -5333,15 +5360,15 @@ impl<'a> Parser<'a> { pub fn parse_show_statement_filter( &mut self, ) -> Result, ParserError> { - if self.parse_keyword(Keyword::LIKE) { + if self.parse_keyword(Keyword::LIKE).is_some() { Ok(Some(ShowStatementFilter::Like( self.parse_literal_string()?, ))) - } else if self.parse_keyword(Keyword::ILIKE) { + } else if self.parse_keyword(Keyword::ILIKE).is_some() { Ok(Some(ShowStatementFilter::ILike( self.parse_literal_string()?, ))) - } else if self.parse_keyword(Keyword::WHERE) { + } else if self.parse_keyword(Keyword::WHERE).is_some() { Ok(Some(ShowStatementFilter::Where(self.parse_expr()?))) } else { Ok(None) @@ -5360,10 +5387,10 @@ impl<'a> Parser<'a> { // a table alias. let mut joins = vec![]; loop { - let join = if self.parse_keyword(Keyword::CROSS) { - let join_operator = if self.parse_keyword(Keyword::JOIN) { + let join = if self.parse_keyword(Keyword::CROSS).is_some() { + let join_operator = if self.parse_keyword(Keyword::JOIN).is_some() { JoinOperator::CrossJoin - } else if self.parse_keyword(Keyword::APPLY) { + } else if self.parse_keyword(Keyword::APPLY).is_some() { // MSSQL extension, similar to CROSS JOIN LATERAL JoinOperator::CrossApply } else { @@ -5373,7 +5400,7 @@ impl<'a> Parser<'a> { relation: self.parse_table_factor()?, join_operator, } - } else if self.parse_keyword(Keyword::OUTER) { + } else if self.parse_keyword(Keyword::OUTER).is_some() { // MSSQL extension, similar to LEFT JOIN LATERAL .. ON 1=1 self.expect_keyword(Keyword::APPLY)?; Join { @@ -5381,7 +5408,7 @@ impl<'a> Parser<'a> { join_operator: JoinOperator::OuterApply, } } else { - let natural = self.parse_keyword(Keyword::NATURAL); + let natural = self.parse_keyword(Keyword::NATURAL).is_some(); let peek_keyword = if let Token::Word(w) = self.peek_token().token { w.keyword } else { @@ -5390,7 +5417,7 @@ impl<'a> Parser<'a> { let join_operator_type = match peek_keyword { Keyword::INNER | Keyword::JOIN => { - let _ = self.parse_keyword(Keyword::INNER); // [ INNER ] + let _ = self.parse_keyword(Keyword::INNER).is_some(); // [ INNER ] self.expect_keyword(Keyword::JOIN)?; JoinOperator::Inner } @@ -5445,7 +5472,7 @@ impl<'a> Parser<'a> { } Keyword::FULL => { let _ = self.next_token(); // consume FULL - let _ = self.parse_keyword(Keyword::OUTER); // [ OUTER ] + let _ = self.parse_keyword(Keyword::OUTER).is_some(); // [ OUTER ] self.expect_keyword(Keyword::JOIN)?; JoinOperator::FullOuter } @@ -5471,13 +5498,13 @@ impl<'a> Parser<'a> { /// A table name or a parenthesized subquery, followed by optional `[AS] alias` pub fn parse_table_factor(&mut self) -> Result { - if self.parse_keyword(Keyword::LATERAL) { + if self.parse_keyword(Keyword::LATERAL).is_some() { // LATERAL must always be followed by a subquery. if !self.consume_token(&Token::LParen) { self.expected("subquery after LATERAL", self.peek_token())?; } self.parse_derived_table_factor(Lateral) - } else if self.parse_keyword(Keyword::TABLE) { + } else if self.parse_keyword(Keyword::TABLE).is_some() { // parse table function (SELECT * FROM TABLE () [ AS ]) self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; @@ -5580,7 +5607,7 @@ impl<'a> Parser<'a> { self.expected("joined table", self.peek_token()) } } else if dialect_of!(self is BigQueryDialect | GenericDialect) - && self.parse_keyword(Keyword::UNNEST) + && self.parse_keyword(Keyword::UNNEST).is_some() { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; @@ -5624,7 +5651,7 @@ impl<'a> Parser<'a> { 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(Keyword::WITH) { + if self.parse_keyword(Keyword::WITH).is_some() { if self.consume_token(&Token::LParen) { with_hints = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; @@ -5662,10 +5689,10 @@ impl<'a> Parser<'a> { pub fn parse_join_constraint(&mut self, natural: bool) -> Result { if natural { Ok(JoinConstraint::Natural) - } else if self.parse_keyword(Keyword::ON) { + } else if self.parse_keyword(Keyword::ON).is_some() { let constraint = self.parse_expr()?; Ok(JoinConstraint::On(constraint)) - } else if self.parse_keyword(Keyword::USING) { + } else if self.parse_keyword(Keyword::USING).is_some() { let columns = self.parse_parenthesized_column_list(Mandatory, false)?; Ok(JoinConstraint::Using(columns)) } else { @@ -5700,9 +5727,9 @@ impl<'a> Parser<'a> { pub fn parse_grant_revoke_privileges_objects( &mut self, ) -> Result<(Privileges, GrantObjects), ParserError> { - let privileges = if self.parse_keyword(Keyword::ALL) { + let privileges = if self.parse_keyword(Keyword::ALL).is_some() { Privileges::All { - with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES), + with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES).is_some(), } } else { let (actions, err): (Vec<_>, Vec<_>) = self @@ -5817,8 +5844,8 @@ impl<'a> Parser<'a> { .parse_keywords(&[Keyword::GRANTED, Keyword::BY]) .then(|| self.parse_identifier().unwrap()); - let cascade = self.parse_keyword(Keyword::CASCADE); - let restrict = self.parse_keyword(Keyword::RESTRICT); + let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); + let restrict = self.parse_keyword(Keyword::RESTRICT).is_some(); if cascade && restrict { return parser_err!("Cannot specify both CASCADE and RESTRICT in REVOKE"); } @@ -5846,7 +5873,7 @@ impl<'a> Parser<'a> { Some(SqliteOnConflict::Fail) } else if self.parse_keywords(&[Keyword::OR, Keyword::IGNORE]) { Some(SqliteOnConflict::Ignore) - } else if self.parse_keyword(Keyword::REPLACE) { + } else if self.parse_keyword(Keyword::REPLACE).is_some() { Some(SqliteOnConflict::Replace) } else { None @@ -5856,9 +5883,9 @@ impl<'a> Parser<'a> { let into = action == Some(Keyword::INTO); let overwrite = action == Some(Keyword::OVERWRITE); - let local = self.parse_keyword(Keyword::LOCAL); + let local = self.parse_keyword(Keyword::LOCAL).is_some(); - if self.parse_keyword(Keyword::DIRECTORY) { + if self.parse_keyword(Keyword::DIRECTORY).is_some() { let path = self.parse_literal_string()?; let file_format = if self.parse_keywords(&[Keyword::STORED, Keyword::AS]) { Some(self.parse_file_format()?) @@ -5875,12 +5902,12 @@ impl<'a> Parser<'a> { }) } else { // Hive lets you put table here regardless - let table = self.parse_keyword(Keyword::TABLE); + let table = self.parse_keyword(Keyword::TABLE).is_some(); let table_name = self.parse_object_name()?; let is_mysql = dialect_of!(self is MySqlDialect); let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; - let partitioned = if self.parse_keyword(Keyword::PARTITION) { + let partitioned = if self.parse_keyword(Keyword::PARTITION).is_some() { self.expect_token(&Token::LParen)?; let r = Some(self.parse_comma_separated(Parser::parse_expr)?); self.expect_token(&Token::RParen)?; @@ -5893,8 +5920,8 @@ impl<'a> Parser<'a> { let after_columns = self.parse_parenthesized_column_list(Optional, false)?; let source = Box::new(self.parse_query()?); - let on = if self.parse_keyword(Keyword::ON) { - if self.parse_keyword(Keyword::CONFLICT) { + let on = if self.parse_keyword(Keyword::ON).is_some() { + if self.parse_keyword(Keyword::CONFLICT).is_some() { let conflict_target = if self.parse_keywords(&[Keyword::ON, Keyword::CONSTRAINT]) { Some(ConflictTarget::OnConstraint(self.parse_object_name()?)) @@ -5907,13 +5934,13 @@ impl<'a> Parser<'a> { }; self.expect_keyword(Keyword::DO)?; - let action = if self.parse_keyword(Keyword::NOTHING) { + let action = if self.parse_keyword(Keyword::NOTHING).is_some() { OnConflictAction::DoNothing } else { self.expect_keyword(Keyword::UPDATE)?; self.expect_keyword(Keyword::SET)?; let assignments = self.parse_comma_separated(Parser::parse_assignment)?; - let selection = if self.parse_keyword(Keyword::WHERE) { + let selection = if self.parse_keyword(Keyword::WHERE).is_some() { Some(self.parse_expr()?) } else { None @@ -5940,7 +5967,7 @@ impl<'a> Parser<'a> { None }; - let returning = if self.parse_keyword(Keyword::RETURNING) { + let returning = if self.parse_keyword(Keyword::RETURNING).is_some() { Some(self.parse_comma_separated(Parser::parse_select_item)?) } else { None @@ -5966,19 +5993,19 @@ impl<'a> Parser<'a> { let table = self.parse_table_and_joins()?; self.expect_keyword(Keyword::SET)?; let assignments = self.parse_comma_separated(Parser::parse_assignment)?; - let from = if self.parse_keyword(Keyword::FROM) + let from = if self.parse_keyword(Keyword::FROM).is_some() && dialect_of!(self is GenericDialect | PostgreSqlDialect | BigQueryDialect | SnowflakeDialect | RedshiftSqlDialect | MsSqlDialect) { Some(self.parse_table_and_joins()?) } else { None }; - let selection = if self.parse_keyword(Keyword::WHERE) { + let selection = if self.parse_keyword(Keyword::WHERE).is_some() { Some(self.parse_expr()?) } else { None }; - let returning = if self.parse_keyword(Keyword::RETURNING) { + let returning = if self.parse_keyword(Keyword::RETURNING).is_some() { Some(self.parse_comma_separated(Parser::parse_select_item)?) } else { None @@ -6028,10 +6055,12 @@ impl<'a> Parser<'a> { match self.parse_wildcard_expr()? { WildcardExpr::Expr(expr) => { let expr: Expr = if self.dialect.supports_filter_during_aggregation() - && self.parse_keyword(Keyword::FILTER) + && self.parse_keyword(Keyword::FILTER).is_some() { let i = self.index - 1; - if self.consume_token(&Token::LParen) && self.parse_keyword(Keyword::WHERE) { + if self.consume_token(&Token::LParen) + && self.parse_keyword(Keyword::WHERE).is_some() + { let filter = self.parse_expr()?; self.expect_token(&Token::RParen)?; Expr::AggregateExpressionWithFilter { @@ -6096,7 +6125,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_select_item_exclude( &mut self, ) -> Result, ParserError> { - let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE) { + let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE).is_some() { if self.consume_token(&Token::LParen) { let columns = self.parse_comma_separated(|parser| parser.parse_identifier())?; self.expect_token(&Token::RParen)?; @@ -6118,7 +6147,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_select_item_except( &mut self, ) -> Result, ParserError> { - let opt_except = if self.parse_keyword(Keyword::EXCEPT) { + let opt_except = if self.parse_keyword(Keyword::EXCEPT).is_some() { let idents = self.parse_parenthesized_column_list(Mandatory, false)?; match &idents[..] { [] => { @@ -6143,7 +6172,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_select_item_rename( &mut self, ) -> Result, ParserError> { - let opt_rename = if self.parse_keyword(Keyword::RENAME) { + let opt_rename = if self.parse_keyword(Keyword::RENAME).is_some() { if self.consume_token(&Token::LParen) { let idents = self.parse_comma_separated(|parser| parser.parse_identifier_with_alias())?; @@ -6164,9 +6193,9 @@ impl<'a> Parser<'a> { pub fn parse_order_by_expr(&mut self) -> Result { let expr = self.parse_expr()?; - let asc = if self.parse_keyword(Keyword::ASC) { + let asc = if self.parse_keyword(Keyword::ASC).is_some() { Some(true) - } else if self.parse_keyword(Keyword::DESC) { + } else if self.parse_keyword(Keyword::DESC).is_some() { Some(false) } else { None @@ -6198,7 +6227,7 @@ impl<'a> Parser<'a> { Some(Expr::Value(self.parse_number_value()?)) }; - let percent = self.parse_keyword(Keyword::PERCENT); + let percent = self.parse_keyword(Keyword::PERCENT).is_some(); let with_ties = self.parse_keywords(&[Keyword::WITH, Keyword::TIES]); @@ -6211,7 +6240,7 @@ impl<'a> Parser<'a> { /// Parse a LIMIT clause pub fn parse_limit(&mut self) -> Result, ParserError> { - if self.parse_keyword(Keyword::ALL) { + if self.parse_keyword(Keyword::ALL).is_some() { Ok(None) } else { Ok(Some(self.parse_expr()?)) @@ -6221,9 +6250,9 @@ impl<'a> Parser<'a> { /// Parse an OFFSET clause pub fn parse_offset(&mut self) -> Result { let value = self.parse_expr()?; - let rows = if self.parse_keyword(Keyword::ROW) { + let rows = if self.parse_keyword(Keyword::ROW).is_some() { OffsetRows::Row - } else if self.parse_keyword(Keyword::ROWS) { + } else if self.parse_keyword(Keyword::ROWS).is_some() { OffsetRows::Rows } else { OffsetRows::None @@ -6241,11 +6270,11 @@ impl<'a> Parser<'a> { (None, false) } else { let quantity = Expr::Value(self.parse_value()?); - let percent = self.parse_keyword(Keyword::PERCENT); + let percent = self.parse_keyword(Keyword::PERCENT).is_some(); self.expect_one_of_keywords(&[Keyword::ROW, Keyword::ROWS])?; (Some(quantity), percent) }; - let with_ties = if self.parse_keyword(Keyword::ONLY) { + let with_ties = if self.parse_keyword(Keyword::ONLY).is_some() { false } else if self.parse_keywords(&[Keyword::WITH, Keyword::TIES]) { true @@ -6266,12 +6295,12 @@ impl<'a> Parser<'a> { Keyword::SHARE => LockType::Share, _ => unreachable!(), }; - let of = if self.parse_keyword(Keyword::OF) { + let of = if self.parse_keyword(Keyword::OF).is_some() { Some(self.parse_object_name()?) } else { None }; - let nonblock = if self.parse_keyword(Keyword::NOWAIT) { + let nonblock = if self.parse_keyword(Keyword::NOWAIT).is_some() { Some(NonBlock::Nowait) } else if self.parse_keywords(&[Keyword::SKIP, Keyword::LOCKED]) { Some(NonBlock::SkipLocked) @@ -6289,7 +6318,7 @@ impl<'a> Parser<'a> { let mut explicit_row = false; let rows = self.parse_comma_separated(|parser| { - if parser.parse_keyword(Keyword::ROW) { + if parser.parse_keyword(Keyword::ROW).is_some() { explicit_row = true; } @@ -6331,7 +6360,7 @@ impl<'a> Parser<'a> { TransactionIsolationLevel::ReadCommitted } else if self.parse_keywords(&[Keyword::REPEATABLE, Keyword::READ]) { TransactionIsolationLevel::RepeatableRead - } else if self.parse_keyword(Keyword::SERIALIZABLE) { + } else if self.parse_keyword(Keyword::SERIALIZABLE).is_some() { TransactionIsolationLevel::Serializable } else { self.expected("isolation level", self.peek_token())? @@ -6370,8 +6399,8 @@ impl<'a> Parser<'a> { pub fn parse_commit_rollback_chain(&mut self) -> Result { let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]); - if self.parse_keyword(Keyword::AND) { - let chain = !self.parse_keyword(Keyword::NO); + if self.parse_keyword(Keyword::AND).is_some() { + let chain = !self.parse_keyword(Keyword::NO).is_some(); self.expect_keyword(Keyword::CHAIN)?; Ok(chain) } else { @@ -6380,7 +6409,7 @@ impl<'a> Parser<'a> { } pub fn parse_deallocate(&mut self) -> Result { - let prepare = self.parse_keyword(Keyword::PREPARE); + let prepare = self.parse_keyword(Keyword::PREPARE).is_some(); let name = self.parse_identifier()?; Ok(Statement::Deallocate { name, prepare }) } @@ -6423,10 +6452,10 @@ impl<'a> Parser<'a> { } self.expect_keyword(Keyword::WHEN)?; - let is_not_matched = self.parse_keyword(Keyword::NOT); + let is_not_matched = self.parse_keyword(Keyword::NOT).is_some(); self.expect_keyword(Keyword::MATCHED)?; - let predicate = if self.parse_keyword(Keyword::AND) { + let predicate = if self.parse_keyword(Keyword::AND).is_some() { Some(self.parse_expr()?) } else { None @@ -6494,7 +6523,7 @@ impl<'a> Parser<'a> { } pub fn parse_merge(&mut self) -> Result { - let into = self.parse_keyword(Keyword::INTO); + let into = self.parse_keyword(Keyword::INTO).is_some(); let table = self.parse_table_factor()?; @@ -6566,7 +6595,7 @@ impl<'a> Parser<'a> { } } //[ MINVALUE minvalue | NO MINVALUE ] - if self.parse_keyword(Keyword::MINVALUE) { + if self.parse_keyword(Keyword::MINVALUE).is_some() { sequence_options.push(SequenceOptions::MinValue(MinMaxValue::Some(Expr::Value( self.parse_number_value()?, )))); @@ -6623,10 +6652,11 @@ impl<'a> Parser<'a> { } impl Word { - pub fn to_ident(&self) -> Ident { + pub fn to_ident(&self, span: Span) -> Ident { Ident { value: self.value.clone(), quote_style: self.quote_style, + span, } } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 2cbcf865e..5978161cd 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -24,9 +24,9 @@ use alloc::{ vec, vec::Vec, }; -use core::fmt; use core::iter::Peekable; use core::str::Chars; +use core::{cmp, fmt}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -330,7 +330,7 @@ impl fmt::Display for Whitespace { } /// Location in input string -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Eq, PartialEq, Hash, Clone, Copy, Ord, PartialOrd)] pub struct Location { /// Line number, starting from 1 pub line: u64, @@ -338,6 +338,12 @@ pub struct Location { pub column: u64, } +impl std::fmt::Debug for Location { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Location({}:{})", self.line, self.column) + } +} + impl Location { pub fn of(line: u64, column: u64) -> Self { Self { line, column } @@ -354,12 +360,18 @@ impl From<(u64, u64)> for Location { } } -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord, Copy)] pub struct Span { pub start: Location, pub end: Location, } +impl std::fmt::Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Span({:?}..{:?})", self.start, self.end) + } +} + impl Span { pub fn new(start: Location, end: Location) -> Span { Span { start, end } @@ -371,11 +383,24 @@ impl Span { end: Location { line: 0, column: 0 }, } } -} + pub fn union(&self, other: &Span) -> Span { + Span { + start: cmp::min(self.start, other.start), + end: cmp::max(self.end, other.end), + } + } + + pub fn union_opt(&self, other: &Option) -> Span { + match other { + Some(other) => self.union(&other), + None => self.clone(), + } + } +} /// A [Token] with [Location] attached to it -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, Hash, Clone)] pub struct TokenWithLocation { pub token: Token, pub span: Span, @@ -383,10 +408,7 @@ pub struct TokenWithLocation { impl TokenWithLocation { pub fn new(token: Token, span: Span) -> TokenWithLocation { - TokenWithLocation { - token, - span, - } + TokenWithLocation { token, span } } pub fn wrap(token: Token) -> TokenWithLocation { @@ -398,6 +420,24 @@ impl TokenWithLocation { } } +impl PartialEq for TokenWithLocation { + fn eq(&self, other: &TokenWithLocation) -> bool { + self.token == other.token + } +} + +impl PartialOrd for TokenWithLocation { + fn partial_cmp(&self, other: &TokenWithLocation) -> Option { + self.token.partial_cmp(&other.token) + } +} + +impl Ord for TokenWithLocation { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.token.cmp(&other.token) + } +} + impl PartialEq for TokenWithLocation { fn eq(&self, other: &Token) -> bool { &self.token == other @@ -492,6 +532,7 @@ impl<'a> Tokenizer<'a> { for token_with_location in twl { tokens.push(token_with_location.token); } + Ok(tokens) } @@ -508,14 +549,14 @@ impl<'a> Tokenizer<'a> { let mut location = state.location(); while let Some(token) = self.next_token(&mut state)? { let span = location.span_to(state.location()); + dbg!(span, &token); - tokens.push(TokenWithLocation { - token, - span, - }); + tokens.push(TokenWithLocation { token, span }); location = state.location(); } + + dbg!(&tokens); Ok(tokens) } @@ -1858,11 +1899,23 @@ mod tests { let tokens = tokenizer.tokenize_with_location().unwrap(); let expected = vec![ TokenWithLocation::at(Token::make_keyword("SELECT"), (1, 1).into(), (1, 7).into()), - TokenWithLocation::at(Token::Whitespace(Whitespace::Space), (1, 7).into(), (1, 8).into()), + TokenWithLocation::at( + Token::Whitespace(Whitespace::Space), + (1, 7).into(), + (1, 8).into(), + ), TokenWithLocation::at(Token::make_word("a", None), (1, 8).into(), (1, 9).into()), TokenWithLocation::at(Token::Comma, (1, 9).into(), (1, 10).into()), - TokenWithLocation::at(Token::Whitespace(Whitespace::Newline), (1, 10).into(), (2, 1).into()), - TokenWithLocation::at(Token::Whitespace(Whitespace::Space), (2, 1).into(), (2, 2).into()), + TokenWithLocation::at( + Token::Whitespace(Whitespace::Newline), + (1, 10).into(), + (2, 1).into(), + ), + TokenWithLocation::at( + Token::Whitespace(Whitespace::Space), + (2, 1).into(), + (2, 2).into(), + ), TokenWithLocation::at(Token::make_word("b", None), (2, 2).into(), (2, 3).into()), ]; compare(expected, tokens); diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 3fe5cd92b..afbe96035 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -15,6 +15,7 @@ mod test_utils; use sqlparser::ast::*; use sqlparser::dialect::{BigQueryDialect, GenericDialect}; +use sqlparser::tokenizer::Span; use test_utils::*; #[test] @@ -317,6 +318,7 @@ fn parse_map_access_offset() { column: Box::new(Expr::Identifier(Ident { value: "d".to_string(), quote_style: None, + span: Span::empty(), })), keys: vec![Expr::Function(Function { name: ObjectName(vec!["offset".into()]), diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 3e974d56d..7cfca4d80 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -16,6 +16,7 @@ #[macro_use] mod test_utils; +use sqlparser::tokenizer::{Span, Token, TokenWithLocation}; use test_utils::*; use sqlparser::ast::Expr::{BinaryOp, Identifier, MapAccess}; @@ -32,12 +33,14 @@ fn parse_map_access_expr() { let select = clickhouse().verified_only_select(sql); assert_eq!( Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: false, top: None, projection: vec![UnnamedExpr(MapAccess { column: Box::new(Identifier(Ident { value: "string_values".to_string(), quote_style: None, + span: Span::empty(), })), keys: vec![Expr::Function(Function { name: ObjectName(vec!["indexOf".into()]), diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index c880eee7c..20fd92a0e 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -26,8 +26,9 @@ use sqlparser::dialect::{ AnsiDialect, BigQueryDialect, ClickHouseDialect, GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, PostgreSqlDialect, RedshiftSqlDialect, SQLiteDialect, SnowflakeDialect, }; -use sqlparser::keywords::ALL_KEYWORDS; +use sqlparser::keywords::{Keyword, ALL_KEYWORDS}; use sqlparser::parser::{Parser, ParserError}; +use sqlparser::tokenizer::{Span, Token, TokenWithLocation}; use test_utils::{ all_dialects, assert_eq_vec, expr_from_projection, join, number, only, table, table_alias, TestedDialects, @@ -224,6 +225,7 @@ fn parse_update_set_from() { subquery: Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: false, top: None, projection: vec![ @@ -834,6 +836,7 @@ fn parse_select_with_date_column_name() { &Expr::Identifier(Ident { value: "date".into(), quote_style: None, + span: Span::empty(), }), expr_from_projection(only(&select.projection)), ); @@ -1060,6 +1063,7 @@ fn parse_null_like() { alias: Ident { value: "col_null".to_owned(), quote_style: None, + span: Span::empty(), }, }, select.projection[0] @@ -1075,6 +1079,7 @@ fn parse_null_like() { alias: Ident { value: "null_col".to_owned(), quote_style: None, + span: Span::empty(), }, }, select.projection[1] @@ -1909,6 +1914,7 @@ fn parse_listagg() { expr: Expr::Identifier(Ident { value: "id".to_string(), quote_style: None, + span: Span::empty(), }), asc: None, nulls_first: None, @@ -1917,6 +1923,7 @@ fn parse_listagg() { expr: Expr::Identifier(Ident { value: "username".to_string(), quote_style: None, + span: Span::empty(), }), asc: None, nulls_first: None, @@ -3442,11 +3449,13 @@ fn parse_interval_and_or_xor() { let expected_ast = vec![Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: false, top: None, projection: vec![UnnamedExpr(Expr::Identifier(Ident { value: "col".to_string(), quote_style: None, + span: Span::empty(), }))], into: None, from: vec![TableWithJoins { @@ -3454,6 +3463,7 @@ fn parse_interval_and_or_xor() { name: ObjectName(vec![Ident { value: "test".to_string(), quote_style: None, + span: Span::empty(), }]), alias: None, args: None, @@ -3467,12 +3477,14 @@ fn parse_interval_and_or_xor() { left: Box::new(Expr::Identifier(Ident { value: "d3_date".to_string(), quote_style: None, + span: Span::empty(), })), op: BinaryOperator::Gt, right: Box::new(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident { value: "d1_date".to_string(), quote_style: None, + span: Span::empty(), })), op: BinaryOperator::Plus, right: Box::new(Expr::Interval { @@ -3491,12 +3503,14 @@ fn parse_interval_and_or_xor() { left: Box::new(Expr::Identifier(Ident { value: "d2_date".to_string(), quote_style: None, + span: Span::empty(), })), op: BinaryOperator::Gt, right: Box::new(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident { value: "d1_date".to_string(), quote_style: None, + span: Span::empty(), })), op: BinaryOperator::Plus, right: Box::new(Expr::Interval { @@ -3557,6 +3571,7 @@ fn parse_at_timezone() { name: ObjectName(vec![Ident { value: "FROM_UNIXTIME".to_string(), quote_style: None, + span: Span::empty(), }]), args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(zero.clone()))], over: None, @@ -3576,6 +3591,7 @@ fn parse_at_timezone() { name: ObjectName(vec![Ident { value: "DATE_FORMAT".to_string(), quote_style: None, + span: Span::empty(), },],), args: vec![ FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::AtTimeZone { @@ -3583,6 +3599,7 @@ fn parse_at_timezone() { name: ObjectName(vec![Ident { value: "FROM_UNIXTIME".to_string(), quote_style: None, + span: Span::empty(), },],), args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(zero))], over: None, @@ -3602,6 +3619,7 @@ fn parse_at_timezone() { alias: Ident { value: "hour".to_string(), quote_style: Some('"'), + span: Span::empty(), }, }, only(&select.projection), @@ -4286,10 +4304,12 @@ fn parse_recursive_cte() { name: Ident { value: "nums".to_string(), quote_style: None, + span: Span::empty(), }, columns: vec![Ident { value: "val".to_string(), quote_style: None, + span: Span::empty(), }], }, query: Box::new(cte_query), @@ -5449,10 +5469,12 @@ fn parse_grant() { Ident { value: "shape".into(), quote_style: None, + span: Span::empty(), }, Ident { value: "size".into(), quote_style: None, + span: Span::empty(), }, ]) }, @@ -5642,6 +5664,7 @@ fn parse_merge() { subquery: Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: false, top: None, projection: vec![SelectItem::Wildcard( @@ -5676,6 +5699,7 @@ fn parse_merge() { name: Ident { value: "stg".to_string(), quote_style: None, + span: Span::empty(), }, columns: vec![], }), @@ -5822,7 +5846,8 @@ fn test_lock_table() { lock.of.unwrap().0, vec![Ident { value: "school".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }] ); assert!(lock.nonblock.is_none()); @@ -5836,7 +5861,8 @@ fn test_lock_table() { lock.of.unwrap().0, vec![Ident { value: "school".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }] ); assert!(lock.nonblock.is_none()); @@ -5850,7 +5876,8 @@ fn test_lock_table() { lock.of.unwrap().0, vec![Ident { value: "school".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }] ); assert!(lock.nonblock.is_none()); @@ -5860,7 +5887,8 @@ fn test_lock_table() { lock.of.unwrap().0, vec![Ident { value: "student".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }] ); assert!(lock.nonblock.is_none()); @@ -5877,7 +5905,8 @@ fn test_lock_nonblock() { lock.of.unwrap().0, vec![Ident { value: "school".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }] ); assert_eq!(lock.nonblock.unwrap(), NonBlock::SkipLocked); @@ -5891,7 +5920,8 @@ fn test_lock_nonblock() { lock.of.unwrap().0, vec![Ident { value: "school".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }] ); assert_eq!(lock.nonblock.unwrap(), NonBlock::Nowait); diff --git a/tests/sqlparser_custom_dialect.rs b/tests/sqlparser_custom_dialect.rs index a700dda11..e01c752d7 100644 --- a/tests/sqlparser_custom_dialect.rs +++ b/tests/sqlparser_custom_dialect.rs @@ -106,7 +106,7 @@ fn custom_statement_parser() -> Result<(), ParserError> { } fn parse_statement(&self, parser: &mut Parser) -> Option> { - if parser.parse_keyword(Keyword::SELECT) { + if parser.parse_keyword(Keyword::SELECT).is_some() { for _ in 0..3 { let _ = parser.next_token(); } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 41b0803e4..c7811049f 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -16,6 +16,7 @@ #[macro_use] mod test_utils; +use sqlparser::tokenizer::Span; use test_utils::*; use sqlparser::ast::*; @@ -132,7 +133,8 @@ fn parse_mssql_create_role() { authorization_owner, Some(ObjectName(vec![Ident { value: "helena".into(), - quote_style: None + quote_style: None, + span: Span::empty(), }])) ); } diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 508ae8461..45aecb34c 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -18,7 +18,9 @@ use sqlparser::ast::Expr; use sqlparser::ast::Value; use sqlparser::ast::*; use sqlparser::dialect::{GenericDialect, MySqlDialect}; +use sqlparser::tokenizer::Span; use sqlparser::tokenizer::Token; +use sqlparser::tokenizer::TokenWithLocation; use test_utils::*; #[macro_use] @@ -445,11 +447,13 @@ fn parse_quote_identifiers_2() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: false, top: None, projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident { value: "quoted ` identifier".into(), quote_style: Some('`'), + span: Span::empty(), }))], into: None, from: vec![], @@ -479,11 +483,13 @@ fn parse_quote_identifiers_3() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: false, top: None, projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident { value: "`quoted identifier`".into(), quote_style: Some('`'), + span: Span::empty(), }))], into: None, from: vec![], @@ -966,6 +972,8 @@ fn parse_alter_table_change_column() { #[test] #[cfg(not(feature = "bigdecimal"))] fn parse_substring_in_select() { + use sqlparser::tokenizer::Span; + let sql = "SELECT DISTINCT SUBSTRING(description, 0, 1) FROM test"; match mysql().one_statement_parses_to( sql, @@ -976,12 +984,14 @@ fn parse_substring_in_select() { Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: true, top: None, projection: vec![SelectItem::UnnamedExpr(Expr::Substring { expr: Box::new(Expr::Identifier(Ident { value: "description".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), })), substring_from: Some(Box::new(Expr::Value(Value::Number( "0".to_string(), @@ -997,7 +1007,8 @@ fn parse_substring_in_select() { relation: TableFactor::Table { name: ObjectName(vec![Ident { value: "test".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }]), alias: None, args: None, diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 496a61843..1baf37fa9 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -16,6 +16,7 @@ #[macro_use] mod test_utils; +use sqlparser::tokenizer::{Span, Token, TokenWithLocation}; use test_utils::*; use sqlparser::ast::*; @@ -792,7 +793,8 @@ fn parse_set() { variable: ObjectName(vec![Ident::new("a")]), value: vec![Expr::Identifier(Ident { value: "b".into(), - quote_style: None + quote_style: None, + span: Span::empty(), })], } ); @@ -834,7 +836,8 @@ fn parse_set() { variable: ObjectName(vec![Ident::new("a")]), value: vec![Expr::Identifier(Ident { value: "DEFAULT".into(), - quote_style: None + quote_style: None, + span: Span::empty(), })], } ); @@ -859,7 +862,8 @@ fn parse_set() { variable: ObjectName(vec![Ident::new("a"), Ident::new("b"), Ident::new("c")]), value: vec![Expr::Identifier(Ident { value: "b".into(), - quote_style: None + quote_style: None, + span: Span::empty(), })], } ); @@ -931,6 +935,7 @@ fn parse_set_role() { role_name: Some(Ident { value: "rolename".to_string(), quote_style: Some('\"'), + span: Span::empty(), }), } ); @@ -945,6 +950,7 @@ fn parse_set_role() { role_name: Some(Ident { value: "rolename".to_string(), quote_style: Some('\''), + span: Span::empty(), }), } ); @@ -1215,7 +1221,8 @@ fn parse_pg_on_conflict() { selection: Some(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident { value: "dsize".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), })), op: BinaryOperator::Gt, right: Box::new(Expr::Value(Value::Placeholder("$2".to_string()))) @@ -1252,7 +1259,8 @@ fn parse_pg_on_conflict() { selection: Some(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident { value: "dsize".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), })), op: BinaryOperator::Gt, right: Box::new(Expr::Value(Value::Placeholder("$2".to_string()))) @@ -1445,11 +1453,13 @@ fn parse_array_index_expr() { num[0].clone(), Expr::Identifier(Ident { value: "baz".to_string(), - quote_style: Some('"') + quote_style: Some('"'), + span: Span::empty(), }), Expr::Identifier(Ident { value: "fooz".to_string(), - quote_style: Some('"') + quote_style: Some('"'), + span: Span::empty(), }) ], }, @@ -1499,6 +1509,7 @@ fn parse_array_subquery_expr() { op: SetOperator::Union, set_quantifier: SetQuantifier::None, left: Box::new(SetExpr::Select(Box::new(Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: false, top: None, projection: vec![SelectItem::UnnamedExpr(Expr::Value(Value::Number( @@ -1520,6 +1531,7 @@ fn parse_array_subquery_expr() { qualify: None, }))), right: Box::new(SetExpr::Select(Box::new(Select { + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: false, top: None, projection: vec![SelectItem::UnnamedExpr(Expr::Value(Value::Number( @@ -1999,6 +2011,7 @@ fn parse_custom_operator() { left: Box::new(Expr::Identifier(Ident { value: "relname".into(), quote_style: None, + span: Span::empty(), })), op: BinaryOperator::PGCustomBinaryOperator(vec![ "database".into(), @@ -2018,6 +2031,7 @@ fn parse_custom_operator() { left: Box::new(Expr::Identifier(Ident { value: "relname".into(), quote_style: None, + span: Span::empty(), })), op: BinaryOperator::PGCustomBinaryOperator(vec!["pg_catalog".into(), "~".into()]), right: Box::new(Expr::Value(Value::SingleQuotedString("^(table)$".into()))) @@ -2033,6 +2047,7 @@ fn parse_custom_operator() { left: Box::new(Expr::Identifier(Ident { value: "relname".into(), quote_style: None, + span: Span::empty(), })), op: BinaryOperator::PGCustomBinaryOperator(vec!["~".into()]), right: Box::new(Expr::Value(Value::SingleQuotedString("^(table)$".into()))) @@ -2428,7 +2443,8 @@ fn parse_drop_function() { func_desc: vec![DropFunctionDesc { name: ObjectName(vec![Ident { value: "test_func".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }]), args: None }], @@ -2444,7 +2460,8 @@ fn parse_drop_function() { func_desc: vec![DropFunctionDesc { name: ObjectName(vec![Ident { value: "test_func".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }]), args: Some(vec![ OperateFunctionArg::with_name("a", DataType::Integer(None)), @@ -2469,7 +2486,8 @@ fn parse_drop_function() { DropFunctionDesc { name: ObjectName(vec![Ident { value: "test_func1".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }]), args: Some(vec![ OperateFunctionArg::with_name("a", DataType::Integer(None)), @@ -2487,7 +2505,8 @@ fn parse_drop_function() { DropFunctionDesc { name: ObjectName(vec![Ident { value: "test_func2".to_string(), - quote_style: None + quote_style: None, + span: Span::empty(), }]), args: Some(vec![ OperateFunctionArg::with_name("a", DataType::Varchar(None)), @@ -2556,6 +2575,7 @@ fn parse_dollar_quoted_string() { alias: Ident { value: "col_name".into(), quote_style: None, + span: Span::empty(), }, } ); diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 7597ee981..ca1ba03de 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -13,6 +13,7 @@ #[macro_use] mod test_utils; +use sqlparser::tokenizer::Span; use test_utils::*; use sqlparser::ast::*; @@ -25,7 +26,8 @@ fn test_square_brackets_over_db_schema_table_name() { select.projection[0], SelectItem::UnnamedExpr(Expr::Identifier(Ident { value: "col1".to_string(), - quote_style: Some('[') + quote_style: Some('['), + span: Span::empty(), })), ); assert_eq!( @@ -35,11 +37,13 @@ fn test_square_brackets_over_db_schema_table_name() { name: ObjectName(vec![ Ident { value: "test_schema".to_string(), - quote_style: Some('[') + quote_style: Some('['), + span: Span::empty(), }, Ident { value: "test_table".to_string(), - quote_style: Some('[') + quote_style: Some('['), + span: Span::empty(), } ]), alias: None, @@ -69,7 +73,8 @@ fn test_double_quotes_over_db_schema_table_name() { select.projection[0], SelectItem::UnnamedExpr(Expr::Identifier(Ident { value: "col1".to_string(), - quote_style: Some('"') + quote_style: Some('"'), + span: Span::empty(), })), ); assert_eq!( @@ -79,11 +84,13 @@ fn test_double_quotes_over_db_schema_table_name() { name: ObjectName(vec![ Ident { value: "test_schema".to_string(), - quote_style: Some('"') + quote_style: Some('"'), + span: Span::empty(), }, Ident { value: "test_table".to_string(), - quote_style: Some('"') + quote_style: Some('"'), + span: Span::empty(), } ]), alias: None, From 079a4e273faf3441527238b2c612ff25c391bcc0 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 17 Sep 2024 11:16:03 +0200 Subject: [PATCH 03/32] implement a bunch more stuff --- src/ast/query.rs | 1 + src/ast/spans.rs | 179 +++++++++++++++++++++++++++++++++++++---------- src/parser.rs | 3 +- src/tokenizer.rs | 14 ++-- 4 files changed, 155 insertions(+), 42 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 53ce5f9fb..1374180fb 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -314,6 +314,7 @@ impl fmt::Display for LateralView { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct With { + pub with_token: TokenWithLocation, pub recursive: bool, pub cte_tables: Vec, } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index c5169d392..9f152f0b4 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -3,7 +3,7 @@ use core::iter; use crate::ast; use crate::tokenizer::Span; -use super::{Expr, Join, Query, Select, SelectItem, SetExpr, TableFactor, TableWithJoins}; +use super::{Cte, Expr, Join, JoinConstraint, JoinOperator, Query, Select, SelectItem, SetExpr, TableAlias, TableFactor, TableWithJoins, With}; pub trait Spanned { fn span(&self) -> Span; @@ -11,7 +11,29 @@ pub trait Spanned { impl Spanned for Query { fn span(&self) -> Span { - self.body.span() + union_spans( + self.with.iter().map(|item| item.span()) + .chain(core::iter::once(self.body.span())) + ) + } +} + +impl Spanned for With { + fn span(&self) -> Span { + union_spans( + core::iter::once(self.with_token.span.clone()) + .chain(self.cte_tables.iter().map(|item| item.span())) + ) + } +} + +impl Spanned for Cte { + fn span(&self) -> Span { + union_spans( + core::iter::once(self.alias.span()) + .chain(core::iter::once(self.query.span())) + .chain(self.from.iter().map(|item| item.span)) + ) } } @@ -25,7 +47,7 @@ impl Spanned for SetExpr { set_quantifier, left, right, - } => todo!(), + } => left.span().union(&right.span()), SetExpr::Values(values) => todo!(), SetExpr::Insert(statement) => todo!(), SetExpr::Table(table) => todo!(), @@ -37,23 +59,23 @@ impl Spanned for Expr { fn span(&self) -> Span { match self { Expr::Identifier(ident) => ident.span, - Expr::CompoundIdentifier(vec) => todo!(), + Expr::CompoundIdentifier(vec) => union_spans(vec.iter().map(|i| i.span)), Expr::JsonAccess { left, - operator, + operator: _, right, - } => todo!(), - Expr::CompositeAccess { expr, key } => todo!(), - Expr::IsFalse(expr) => todo!(), - Expr::IsNotFalse(expr) => todo!(), - Expr::IsTrue(expr) => todo!(), - Expr::IsNotTrue(expr) => todo!(), - Expr::IsNull(expr) => todo!(), - Expr::IsNotNull(expr) => todo!(), - Expr::IsUnknown(expr) => todo!(), - Expr::IsNotUnknown(expr) => todo!(), - Expr::IsDistinctFrom(expr, expr1) => todo!(), - Expr::IsNotDistinctFrom(expr, expr1) => todo!(), + } => left.span().union(&right.span()), + Expr::CompositeAccess { expr, key } => expr.span().union(&key.span), + Expr::IsFalse(expr) => expr.span(), + Expr::IsNotFalse(expr) => expr.span(), + Expr::IsTrue(expr) => expr.span(), + Expr::IsNotTrue(expr) => expr.span(), + Expr::IsNull(expr) => expr.span(), + Expr::IsNotNull(expr) => expr.span(), + Expr::IsUnknown(expr) => expr.span(), + Expr::IsNotUnknown(expr) => expr.span(), + Expr::IsDistinctFrom(lhs, rhs) => lhs.span().union(&rhs.span()), + Expr::IsNotDistinctFrom(lhs, rhs) => lhs.span().union(&rhs.span()), Expr::InList { expr, list, @@ -75,7 +97,7 @@ impl Spanned for Expr { low, high, } => todo!(), - Expr::BinaryOp { left, op, right } => todo!(), + Expr::BinaryOp { left, op, right } => left.span().union(&right.span()), Expr::Like { negated, expr, @@ -205,7 +227,7 @@ impl Spanned for TableFactor { lateral, subquery, alias, - } => todo!(), + } => subquery.span().union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::TableFunction { expr, alias } => todo!(), TableFactor::UNNEST { alias, @@ -221,9 +243,47 @@ impl Spanned for TableFactor { } } +impl Spanned for TableAlias { + fn span(&self) -> Span { + union_spans( + iter::once(self.name.span) + .chain(self.columns.iter().map(|i| i.span)) + ) + } +} + impl Spanned for Join { fn span(&self) -> Span { - todo!() + self.relation.span().union(&self.join_operator.span()) + } +} + +impl Spanned for JoinOperator { + fn span(&self) -> Span { + match self { + JoinOperator::Inner(join_constraint) => join_constraint.span(), + JoinOperator::LeftOuter(join_constraint) => join_constraint.span(), + JoinOperator::RightOuter(join_constraint) => join_constraint.span(), + JoinOperator::FullOuter(join_constraint) => join_constraint.span(), + JoinOperator::CrossJoin => Span::empty(), + JoinOperator::LeftSemi(join_constraint) => join_constraint.span(), + JoinOperator::RightSemi(join_constraint) => join_constraint.span(), + JoinOperator::LeftAnti(join_constraint) => join_constraint.span(), + JoinOperator::RightAnti(join_constraint) => join_constraint.span(), + JoinOperator::CrossApply => Span::empty(), + JoinOperator::OuterApply => Span::empty(), + } + } +} + +impl Spanned for JoinConstraint { + fn span(&self) -> Span { + match self { + JoinConstraint::On(expr) => expr.span(), + JoinConstraint::Using(vec) => union_spans(vec.iter().map(|i| i.span)), + JoinConstraint::Natural => Span::empty(), + JoinConstraint::None => Span::empty(), + } } } @@ -250,37 +310,82 @@ impl Spanned for Select { } } -/** - * TODO: - * - * - CTE - * - With - * - SetExpr - * - Fetch - * - Lock Clause - */ -struct Ignore; - #[cfg(test)] pub mod tests { - use crate::dialect::{Dialect, GenericDialect}; + use ast::query; + + use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect}; use crate::tokenizer::Span; use super::*; #[test] - fn test_span() { + fn test_query_span() { let query = crate::parser::Parser::new(&GenericDialect::default()) - .try_with_sql("SELECT id, name FROM users") + .try_with_sql("SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id") .unwrap() .parse_query() .unwrap(); - dbg!(&query); - assert_eq!( query.span(), - Span::new((1, 1).into(), (1, 54 - 28 + 1).into()) + Span::new((1, 1).into(), (1, 109 - 28 + 1).into()) ); } + + + #[test] + pub fn test_union() { + let query = crate::parser::Parser::new(&GenericDialect::default()) + .try_with_sql("SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source") + .unwrap() + .parse_query() + .unwrap(); + + query.span(); + } + + #[test] + pub fn test_subquery() { + let query = crate::parser::Parser::new(&GenericDialect::default()) + .try_with_sql("SELECT a FROM (SELECT a FROM postgres.public.source) AS b") + .unwrap() + .parse_query() + .unwrap(); + + query.span(); + } + + #[test] + pub fn test_cte() { + let query = crate::parser::Parser::new(&GenericDialect::default()) + .try_with_sql("WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner") + .unwrap() + .parse_query() + .unwrap(); + + query.span(); + } + + #[test] + pub fn test_snowflake_lateral_flatten() { + let query = crate::parser::Parser::new(&SnowflakeDialect::default()) + .try_with_sql("SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED") + .unwrap() + .parse_query() + .unwrap(); + + query.span(); + } + + #[test] + pub fn test_wildcard_from_cte() { + let query = crate::parser::Parser::new(&GenericDialect::default()) + .try_with_sql("WITH cte AS (SELECT a FROM postgres.public.source) SELECT cte.* FROM cte") + .unwrap() + .parse_query() + .unwrap(); + + query.span(); + } } diff --git a/src/parser.rs b/src/parser.rs index 2446553d3..f74834789 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4756,8 +4756,9 @@ impl<'a> Parser<'a> { /// expect the initial keyword to be already consumed pub fn parse_query(&mut self) -> Result { let _guard = self.recursion_counter.try_decrease()?; - let with = if self.parse_keyword(Keyword::WITH).is_some() { + let with = if let Some(with_token) = self.parse_keyword(Keyword::WITH) { Some(With { + with_token, recursive: self.parse_keyword(Keyword::RECURSIVE).is_some(), cte_tables: self.parse_comma_separated(Parser::parse_cte)?, }) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 5978161cd..0a3118831 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -373,11 +373,13 @@ impl std::fmt::Debug for Span { } impl Span { + const EMPTY : Span = Self::empty(); + pub fn new(start: Location, end: Location) -> Span { Span { start, end } } - pub fn empty() -> Span { + pub const fn empty() -> Span { Span { start: Location { line: 0, column: 0 }, end: Location { line: 0, column: 0 }, @@ -385,9 +387,13 @@ impl Span { } pub fn union(&self, other: &Span) -> Span { - Span { - start: cmp::min(self.start, other.start), - end: cmp::max(self.end, other.end), + match (self, other) { + (&Span::EMPTY, _) => other.clone(), + (_, &Span::EMPTY) => self.clone(), + _ => Span { + start: cmp::min(self.start, other.start), + end: cmp::max(self.end, other.end), + }, } } From df9ab1e87ffdf01f938c306740ec9655c93ad3dc Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 17 Sep 2024 13:56:31 +0200 Subject: [PATCH 04/32] fix: restore old behaviour of location display --- src/tokenizer.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index a49e5455a..f0f861753 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -424,9 +424,11 @@ pub struct Location { pub column: u64, } - impl std::fmt::Display for Location { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.line == 0 { + return Ok(()); + } write!(f, " at Line: {}, Column: {}", self.line, self.column) } } @@ -466,7 +468,7 @@ impl std::fmt::Debug for Span { } impl Span { - const EMPTY : Span = Self::empty(); + const EMPTY: Span = Self::empty(); pub fn new(start: Location, end: Location) -> Span { Span { start, end } From b718c76359b4f57ef2715c597199994d29e007d4 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 17 Sep 2024 15:44:09 +0200 Subject: [PATCH 05/32] implement spans for eveeeeen more ast nodes --- src/ast/mod.rs | 4 +- src/ast/spans.rs | 344 ++++++++++++++++++++++++++++++++++++++-------- src/parser/mod.rs | 128 +++++++++-------- 3 files changed, 366 insertions(+), 110 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index ae73517f7..a9f1c088d 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -81,8 +81,10 @@ mod dml; pub mod helpers; mod operator; mod query; -mod trigger; mod spans; +pub use spans::Spanned; + +mod trigger; mod value; #[cfg(feature = "visitor")] diff --git a/src/ast/spans.rs b/src/ast/spans.rs index ed1c9c9e3..b29a91bb4 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -3,7 +3,13 @@ use core::iter; use crate::ast; use crate::tokenizer::Span; -use super::{Cte, Expr, FunctionArg, FunctionArgExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, Query, Select, SelectItem, SetExpr, TableAlias, TableFactor, TableWithJoins, With}; +use super::{ + Cte, ExceptSelectItem, ExcludeSelectItem, Expr, Function, FunctionArg, FunctionArgExpr, + FunctionArgumentClause, FunctionArgumentList, FunctionArguments, HavingBound, IlikeSelectItem, + Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, Query, RenameSelectItem, + ReplaceSelectElement, ReplaceSelectItem, Select, SelectItem, SetExpr, TableAlias, TableFactor, + TableWithJoins, Value, WildcardAdditionalOptions, With, +}; pub trait Spanned { fn span(&self) -> Span; @@ -12,8 +18,10 @@ pub trait Spanned { impl Spanned for Query { fn span(&self) -> Span { union_spans( - self.with.iter().map(|item| item.span()) - .chain(core::iter::once(self.body.span())) + self.with + .iter() + .map(|item| item.span()) + .chain(core::iter::once(self.body.span())), ) } } @@ -22,7 +30,7 @@ impl Spanned for With { fn span(&self) -> Span { union_spans( core::iter::once(self.with_token.span.clone()) - .chain(self.cte_tables.iter().map(|item| item.span())) + .chain(self.cte_tables.iter().map(|item| item.span())), ) } } @@ -32,7 +40,7 @@ impl Spanned for Cte { union_spans( core::iter::once(self.alias.span()) .chain(core::iter::once(self.query.span())) - .chain(self.from.iter().map(|item| item.span)) + .chain(self.from.iter().map(|item| item.span)), ) } } @@ -41,7 +49,7 @@ impl Spanned for SetExpr { fn span(&self) -> Span { match self { SetExpr::Select(select) => select.span(), - SetExpr::Query(query) => todo!(), + SetExpr::Query(query) => query.span(), SetExpr::SetOperation { op, set_quantifier, @@ -76,7 +84,9 @@ impl Spanned for Expr { expr, list, negated, - } => todo!(), + } => union_spans( + core::iter::once(expr.span()).chain(list.iter().map(|item| item.span())), + ), Expr::InSubquery { expr, subquery, @@ -122,11 +132,11 @@ impl Spanned for Expr { overlay_for, } => todo!(), Expr::Collate { expr, collation } => todo!(), - Expr::Nested(expr) => todo!(), - Expr::Value(value) => todo!(), + Expr::Nested(expr) => expr.span(), + Expr::Value(value) => value.span(), Expr::TypedString { data_type, value } => todo!(), Expr::MapAccess { column, keys } => todo!(), - Expr::Function(function) => todo!(), + Expr::Function(function) => function.span(), Expr::GroupingSets(vec) => todo!(), Expr::Cube(vec) => todo!(), Expr::Rollup(vec) => todo!(), @@ -138,20 +148,86 @@ impl Spanned for Expr { opt_search_modifier, } => todo!(), Expr::JsonAccess { value, path } => value.span().union(&path.span()), - Expr::RLike { negated, expr, pattern, regexp } => todo!(), - Expr::AnyOp { left, compare_op, right } => todo!(), - Expr::AllOp { left, compare_op, right } => todo!(), - Expr::UnaryOp { op, expr } => todo!(), - Expr::Convert { expr, data_type, charset, target_before_value, styles } => todo!(), - Expr::Cast { kind, expr, data_type, format } => expr.span(), - Expr::AtTimeZone { timestamp, time_zone } => todo!(), - Expr::Extract { field, syntax, expr } => todo!(), - Expr::Substring { expr, substring_from, substring_for, special } => todo!(), - Expr::Trim { expr, trim_where, trim_what, trim_characters } => todo!(), - Expr::IntroducedString { introducer, value } => todo!(), - Expr::Case { operand, conditions, results, else_result } => todo!(), - Expr::Exists { subquery, negated } => todo!(), - Expr::Subquery(query) => todo!(), + Expr::RLike { + negated, + expr, + pattern, + regexp, + } => todo!(), + Expr::AnyOp { + left, + compare_op, + right, + } => todo!(), + Expr::AllOp { + left, + compare_op, + right, + } => todo!(), + Expr::UnaryOp { op, expr } => expr.span(), + Expr::Convert { + expr, + data_type, + charset, + target_before_value, + styles, + } => todo!(), + Expr::Cast { + kind, + expr, + data_type, + format, + } => expr.span(), + Expr::AtTimeZone { + timestamp, + time_zone, + } => todo!(), + Expr::Extract { + field, + syntax, + expr, + } => todo!(), + Expr::Substring { + expr, + substring_from, + substring_for, + special, + } => union_spans( + core::iter::once(expr.span()) + .chain(substring_from.as_ref().map(|i| i.span())) + .chain(substring_for.as_ref().map(|i| i.span())), + ), + Expr::Trim { + expr, + trim_where, + trim_what, + trim_characters, + } => union_spans( + core::iter::once(expr.span()) + .chain(trim_what.as_ref().map(|i| i.span())) + .chain( + trim_characters + .as_ref() + .map(|items| union_spans(items.iter().map(|i| i.span()))), + ), + ), + Expr::IntroducedString { introducer, value } => value.span(), + Expr::Case { + operand, + conditions, + results, + else_result, + } => union_spans( + operand + .as_ref() + .map(|i| i.span()) + .into_iter() + .chain(conditions.iter().map(|i| i.span())) + .chain(results.iter().map(|i| i.span())) + .chain(else_result.as_ref().map(|i| i.span())), + ), + Expr::Exists { subquery, negated } => subquery.span(), + Expr::Subquery(query) => query.span(), Expr::Struct { values, fields } => todo!(), Expr::Named { expr, name } => todo!(), Expr::Dictionary(vec) => todo!(), @@ -167,13 +243,59 @@ impl Spanned for Expr { } } +impl Spanned for Function { + fn span(&self) -> Span { + union_spans( + self.name + .0 + .iter() + .map(|i| i.span) + .chain(iter::once(self.args.span())), + ) + } +} + +impl Spanned for FunctionArguments { + fn span(&self) -> Span { + match self { + FunctionArguments::None => Span::empty(), + FunctionArguments::Subquery(query) => query.span(), + FunctionArguments::List(list) => list.span(), + } + } +} + +impl Spanned for FunctionArgumentList { + fn span(&self) -> Span { + union_spans( + // # todo: duplicate-treatment span + self.args + .iter() + .map(|i| i.span()) + .chain(self.clauses.iter().map(|i| i.span())), + ) + } +} + +impl Spanned for FunctionArgumentClause { + fn span(&self) -> Span { + match self { + FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment) => Span::empty(), + FunctionArgumentClause::OrderBy(vec) => union_spans(vec.iter().map(|i| i.expr.span())), + FunctionArgumentClause::Limit(expr) => expr.span(), + FunctionArgumentClause::OnOverflow(list_agg_on_overflow) => Span::empty(), + FunctionArgumentClause::Having(HavingBound(kind, expr)) => expr.span(), + FunctionArgumentClause::Separator(value) => value.span(), + } + } +} + impl Spanned for JsonPath { fn span(&self) -> Span { union_spans(self.path.iter().map(|i| i.span())) } } - impl Spanned for JsonPathElem { fn span(&self) -> Span { match self { @@ -188,17 +310,80 @@ impl Spanned for SelectItem { match self { SelectItem::UnnamedExpr(expr) => expr.span(), SelectItem::ExprWithAlias { expr, alias } => expr.span().union(&alias.span), - SelectItem::QualifiedWildcard(object_name, wildcard_additional_options) => object_name - .0 - .iter() - .map(|i| i.span) - .reduce(|acc, item| acc.union(&item)) - .expect("Empty iterator"), - SelectItem::Wildcard(wildcard_additional_options) => todo!(), + SelectItem::QualifiedWildcard(object_name, wildcard_additional_options) => union_spans( + object_name + .0 + .iter() + .map(|i| i.span) + .chain(iter::once(wildcard_additional_options.span())), + ), + SelectItem::Wildcard(wildcard_additional_options) => wildcard_additional_options.span(), + } + } +} + +impl Spanned for WildcardAdditionalOptions { + fn span(&self) -> Span { + union_spans( + self.opt_ilike + .as_ref() + .map(|i| i.span()) + .into_iter() + .chain(self.opt_exclude.as_ref().map(|i| i.span()).into_iter()) + .chain(self.opt_rename.as_ref().map(|i| i.span()).into_iter()) + .chain(self.opt_replace.as_ref().map(|i| i.span()).into_iter()) + .chain(self.opt_except.as_ref().map(|i| i.span()).into_iter()), + ) + } +} + +impl Spanned for IlikeSelectItem { + fn span(&self) -> Span { + Span::empty() // # todo: missing span + } +} + +impl Spanned for ExcludeSelectItem { + fn span(&self) -> Span { + match self { + ExcludeSelectItem::Single(ident) => ident.span, + ExcludeSelectItem::Multiple(vec) => union_spans(vec.iter().map(|i| i.span)), } } } +impl Spanned for RenameSelectItem { + fn span(&self) -> Span { + match self { + RenameSelectItem::Single(ident) => ident.ident.span.union(&ident.alias.span), + RenameSelectItem::Multiple(vec) => { + union_spans(vec.iter().map(|i| i.ident.span.union(&i.alias.span))) + } + } + } +} + +impl Spanned for ExceptSelectItem { + fn span(&self) -> Span { + union_spans( + iter::once(self.first_element.span) + .chain(self.additional_elements.iter().map(|i| i.span)), + ) + } +} + +impl Spanned for ReplaceSelectItem { + fn span(&self) -> Span { + union_spans(self.items.iter().map(|i| i.span())) + } +} + +impl Spanned for ReplaceSelectElement { + fn span(&self) -> Span { + self.expr.span().union(&self.column_name.span) + } +} + impl Spanned for TableFactor { fn span(&self) -> Span { match self { @@ -227,7 +412,9 @@ impl Spanned for TableFactor { lateral, subquery, alias, - } => subquery.span().union_opt(&alias.as_ref().map(|alias| alias.span())), + } => subquery + .span() + .union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::TableFunction { expr, alias } => todo!(), TableFactor::UNNEST { alias, @@ -240,26 +427,62 @@ impl Spanned for TableFactor { table_with_joins, alias, } => todo!(), - TableFactor::Function { lateral, name, args, alias } => union_spans( - name.0.iter().map(|i| i.span) + TableFactor::Function { + lateral, + name, + args, + alias, + } => union_spans( + name.0 + .iter() + .map(|i| i.span) .chain(args.iter().map(|i| i.span())) .chain(alias.as_ref().map(|alias| alias.span())), ), - TableFactor::JsonTable { json_expr, json_path, columns, alias } => todo!(), - TableFactor::Pivot { table, aggregate_functions, value_column, value_source, default_on_null, alias } => todo!(), - TableFactor::Unpivot { table, value, name, columns, alias } => todo!(), - TableFactor::MatchRecognize { table, partition_by, order_by, measures, rows_per_match, after_match_skip, pattern, symbols, alias } => todo!(), + TableFactor::JsonTable { + json_expr, + json_path, + columns, + alias, + } => todo!(), + TableFactor::Pivot { + table, + aggregate_functions, + value_column, + value_source, + default_on_null, + alias, + } => todo!(), + TableFactor::Unpivot { + table, + value, + name, + columns, + alias, + } => todo!(), + TableFactor::MatchRecognize { + table, + partition_by, + order_by, + measures, + rows_per_match, + after_match_skip, + pattern, + symbols, + alias, + } => todo!(), } } } - impl Spanned for FunctionArg { fn span(&self) -> Span { match self { - FunctionArg::Named { name, arg, operator } => { - name.span.union(&arg.span()) - }, + FunctionArg::Named { + name, + arg, + operator, + } => name.span.union(&arg.span()), FunctionArg::Unnamed(arg) => arg.span(), } } @@ -269,7 +492,9 @@ impl Spanned for FunctionArgExpr { fn span(&self) -> Span { match self { FunctionArgExpr::Expr(expr) => expr.span(), - FunctionArgExpr::QualifiedWildcard(object_name) => union_spans(object_name.0.iter().map(|i| i.span)), + FunctionArgExpr::QualifiedWildcard(object_name) => { + union_spans(object_name.0.iter().map(|i| i.span)) + } FunctionArgExpr::Wildcard => Span::empty(), } } @@ -277,10 +502,13 @@ impl Spanned for FunctionArgExpr { impl Spanned for TableAlias { fn span(&self) -> Span { - union_spans( - iter::once(self.name.span) - .chain(self.columns.iter().map(|i| i.span)) - ) + union_spans(iter::once(self.name.span).chain(self.columns.iter().map(|i| i.span))) + } +} + +impl Spanned for Value { + fn span(&self) -> Span { + Span::empty() // # todo: Value needs to store spans before this is possible } } @@ -304,7 +532,10 @@ impl Spanned for JoinOperator { JoinOperator::RightAnti(join_constraint) => join_constraint.span(), JoinOperator::CrossApply => Span::empty(), JoinOperator::OuterApply => Span::empty(), - JoinOperator::AsOf { match_condition, constraint } => todo!(), + JoinOperator::AsOf { + match_condition, + constraint, + } => todo!(), } } } @@ -330,7 +561,7 @@ impl Spanned for TableWithJoins { pub fn union_spans>(iter: I) -> Span { iter.reduce(|acc, item| acc.union(&item)) - .expect("Empty iterator") + .unwrap_or(Span::empty()) } impl Spanned for Select { @@ -355,7 +586,9 @@ pub mod tests { #[test] fn test_query_span() { let query = crate::parser::Parser::new(&GenericDialect::default()) - .try_with_sql("SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id") + .try_with_sql( + "SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id", + ) .unwrap() .parse_query() .unwrap(); @@ -366,11 +599,12 @@ pub mod tests { ); } - #[test] pub fn test_union() { let query = crate::parser::Parser::new(&GenericDialect::default()) - .try_with_sql("SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source") + .try_with_sql( + "SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source", + ) .unwrap() .parse_query() .unwrap(); @@ -414,7 +648,9 @@ pub mod tests { #[test] pub fn test_wildcard_from_cte() { let query = crate::parser::Parser::new(&GenericDialect::default()) - .try_with_sql("WITH cte AS (SELECT a FROM postgres.public.source) SELECT cte.* FROM cte") + .try_with_sql( + "WITH cte AS (SELECT a FROM postgres.public.source) SELECT cte.* FROM cte", + ) .unwrap() .parse_query() .unwrap(); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 8d8c46cfa..d98528389 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2189,7 +2189,10 @@ impl<'a> Parser<'a> { let (fields, trailing_bracket) = self.parse_struct_type_def(Self::parse_struct_field_def)?; if trailing_bracket.0 { - return parser_err!("unmatched > in STRUCT literal", self.peek_token().span.start); + return parser_err!( + "unmatched > in STRUCT literal", + self.peek_token().span.start + ); } self.expect_token(&Token::LParen)?; @@ -2992,7 +2995,9 @@ impl<'a> Parser<'a> { }); } self.expect_token(&Token::LParen)?; - let in_op = if self.parse_keyword(Keyword::SELECT).is_some() || self.parse_keyword(Keyword::WITH).is_some() { + let in_op = if self.parse_keyword(Keyword::SELECT).is_some() + || self.parse_keyword(Keyword::WITH).is_some() + { self.prev_token(); Expr::InSubquery { expr: Box::new(expr), @@ -3197,9 +3202,7 @@ impl<'a> Parser<'a> { #[must_use] pub fn parse_keyword(&mut self, expected: Keyword) -> Option { match self.peek_token().token { - Token::Word(w) if expected == w.keyword => { - Some(self.next_token()) - } + Token::Word(w) if expected == w.keyword => Some(self.next_token()), _ => None, } } @@ -3547,7 +3550,9 @@ impl<'a> Parser<'a> { && self.parse_one_of_keywords(&[Keyword::PERSISTENT]).is_some(); if self.parse_keyword(Keyword::TABLE).is_some() { self.parse_create_table(or_replace, temporary, global, transient) - } else if self.parse_keyword(Keyword::MATERIALIZED).is_some() || self.parse_keyword(Keyword::VIEW).is_some() { + } else if self.parse_keyword(Keyword::MATERIALIZED).is_some() + || self.parse_keyword(Keyword::VIEW).is_some() + { self.prev_token(); self.parse_create_view(or_replace, temporary) } else if self.parse_keyword(Keyword::EXTERNAL).is_some() { @@ -4134,12 +4139,12 @@ impl<'a> Parser<'a> { data_type = self.parse_data_type()?; } - let default_expr = if self.parse_keyword(Keyword::DEFAULT).is_some() || self.consume_token(&Token::Eq) - { - Some(self.parse_expr()?) - } else { - None - }; + let default_expr = + if self.parse_keyword(Keyword::DEFAULT).is_some() || self.consume_token(&Token::Eq) { + Some(self.parse_expr()?) + } else { + None + }; Ok(OperateFunctionArg { mode, name, @@ -4219,7 +4224,8 @@ impl<'a> Parser<'a> { }; let condition = self - .parse_keyword(Keyword::WHEN).is_some() + .parse_keyword(Keyword::WHEN) + .is_some() .then(|| self.parse_expr()) .transpose()?; @@ -5300,7 +5306,9 @@ impl<'a> Parser<'a> { DiscardObject::PLANS } else if self.parse_keyword(Keyword::SEQUENCES).is_some() { DiscardObject::SEQUENCES - } else if self.parse_keyword(Keyword::TEMP).is_some() || self.parse_keyword(Keyword::TEMPORARY).is_some() { + } else if self.parse_keyword(Keyword::TEMP).is_some() + || self.parse_keyword(Keyword::TEMPORARY).is_some() + { DiscardObject::TEMP } else { return self.expected( @@ -5591,7 +5599,9 @@ impl<'a> Parser<'a> { // Clickhouse has `ON CLUSTER 'cluster'` syntax for DDLs let on_cluster = self.parse_optional_on_cluster()?; - let like = if self.parse_keyword(Keyword::LIKE).is_some() || self.parse_keyword(Keyword::ILIKE).is_some() { + let like = if self.parse_keyword(Keyword::LIKE).is_some() + || self.parse_keyword(Keyword::ILIKE).is_some() + { self.parse_object_name(allow_unquoted_hyphen).ok() } else { None @@ -6653,7 +6663,9 @@ impl<'a> Parser<'a> { } } } else if self.parse_keyword(Keyword::RENAME).is_some() { - if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::CONSTRAINT).is_some() { + if dialect_of!(self is PostgreSqlDialect) + && self.parse_keyword(Keyword::CONSTRAINT).is_some() + { let old_name = self.parse_identifier(false)?; self.expect_keyword(Keyword::TO)?; let new_name = self.parse_identifier(false)?; @@ -8091,20 +8103,22 @@ impl<'a> Parser<'a> { loop { match self.next_token().token { // ensure that optional period is succeeded by another identifier - Token::Period => { let next_token = self.next_token(); match next_token.token { - Token::Word(w) => idents.push(w.to_ident(next_token.span)), - Token::EOF => { - return Err(ParserError::ParserError( - "Trailing period in identifier".to_string(), - ))? - } - token => { - return Err(ParserError::ParserError(format!( - "Unexpected token following period in identifier: {token}" - )))? + Token::Period => { + let next_token = self.next_token(); + match next_token.token { + Token::Word(w) => idents.push(w.to_ident(next_token.span)), + Token::EOF => { + return Err(ParserError::ParserError( + "Trailing period in identifier".to_string(), + ))? + } + token => { + return Err(ParserError::ParserError(format!( + "Unexpected token following period in identifier: {token}" + )))? + } } } - }, Token::EOF => break, token => { return Err(ParserError::ParserError(format!( @@ -9068,28 +9082,28 @@ impl<'a> Parser<'a> { }; // Accept QUALIFY and WINDOW in any order and flag accordingly. - let (named_windows, qualify, window_before_qualify) = if self.parse_keyword(Keyword::WINDOW).is_some() - { - let named_windows = self.parse_comma_separated(Parser::parse_named_window)?; - if self.parse_keyword(Keyword::QUALIFY).is_some() { - (named_windows, Some(self.parse_expr()?), true) - } else { - (named_windows, None, true) - } - } else if self.parse_keyword(Keyword::QUALIFY).is_some() { - let qualify = Some(self.parse_expr()?); + let (named_windows, qualify, window_before_qualify) = if self.parse_keyword(Keyword::WINDOW).is_some() { - ( - self.parse_comma_separated(Parser::parse_named_window)?, - qualify, - false, - ) + let named_windows = self.parse_comma_separated(Parser::parse_named_window)?; + if self.parse_keyword(Keyword::QUALIFY).is_some() { + (named_windows, Some(self.parse_expr()?), true) + } else { + (named_windows, None, true) + } + } else if self.parse_keyword(Keyword::QUALIFY).is_some() { + let qualify = Some(self.parse_expr()?); + if self.parse_keyword(Keyword::WINDOW).is_some() { + ( + self.parse_comma_separated(Parser::parse_named_window)?, + qualify, + false, + ) + } else { + (Default::default(), qualify, false) + } } else { - (Default::default(), qualify, false) - } - } else { - Default::default() - }; + Default::default() + }; let connect_by = if self.dialect.supports_connect_by() && self @@ -10508,7 +10522,10 @@ impl<'a> Parser<'a> { /// Parse an REPLACE statement pub fn parse_replace(&mut self) -> Result { if !dialect_of!(self is MySqlDialect | GenericDialect) { - return parser_err!("Unsupported statement REPLACE", self.peek_token().span.start); + return parser_err!( + "Unsupported statement REPLACE", + self.peek_token().span.start + ); } let insert = &mut self.parse_insert()?; @@ -10589,12 +10606,13 @@ impl<'a> Parser<'a> { let table = self.parse_keyword(Keyword::TABLE).is_some(); let table_name = self.parse_object_name(false)?; - let table_alias = - if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS).is_some() { - Some(self.parse_identifier(false)?) - } else { - None - }; + let table_alias = if dialect_of!(self is PostgreSqlDialect) + && self.parse_keyword(Keyword::AS).is_some() + { + Some(self.parse_identifier(false)?) + } else { + None + }; let is_mysql = dialect_of!(self is MySqlDialect); From 8986a1ecf25e053f1fcd97b938a4cd23287f8b9d Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Thu, 19 Sep 2024 12:05:17 +0200 Subject: [PATCH 06/32] feat: more ast nodes --- src/ast/spans.rs | 43 ++++++++++++++++++++++++++++--------------- src/tokenizer.rs | 4 ++-- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index b29a91bb4..25c8e6dea 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -91,56 +91,69 @@ impl Spanned for Expr { expr, subquery, negated, - } => todo!(), + } => expr.span().union(&subquery.span()), Expr::InUnnest { expr, array_expr, negated, - } => todo!(), + } => expr.span().union(&array_expr.span()), Expr::Between { expr, negated, low, high, - } => todo!(), + } => expr.span().union(&low.span()).union(&high.span()), + Expr::BinaryOp { left, op, right } => left.span().union(&right.span()), Expr::Like { negated, expr, pattern, escape_char, - } => todo!(), + } => expr.span().union(&pattern.span()), Expr::ILike { negated, expr, pattern, escape_char, - } => todo!(), + } => expr.span().union(&pattern.span()), Expr::SimilarTo { negated, expr, pattern, escape_char, - } => todo!(), - Expr::Ceil { expr, field } => todo!(), - Expr::Floor { expr, field } => todo!(), - Expr::Position { expr, r#in } => todo!(), + } => expr.span().union(&pattern.span()), + Expr::Ceil { expr, field } => expr.span(), + Expr::Floor { expr, field } => expr.span(), + Expr::Position { expr, r#in } => expr.span().union(&r#in.span()), Expr::Overlay { expr, overlay_what, overlay_from, overlay_for, - } => todo!(), - Expr::Collate { expr, collation } => todo!(), + } => expr + .span() + .union(&overlay_what.span()) + .union(&overlay_from.span()) + .union_opt(&overlay_for.as_ref().map(|i| i.span())), + Expr::Collate { expr, collation } => expr + .span() + .union(&union_spans(collation.0.iter().map(|i| i.span))), Expr::Nested(expr) => expr.span(), Expr::Value(value) => value.span(), Expr::TypedString { data_type, value } => todo!(), Expr::MapAccess { column, keys } => todo!(), Expr::Function(function) => function.span(), - Expr::GroupingSets(vec) => todo!(), - Expr::Cube(vec) => todo!(), - Expr::Rollup(vec) => todo!(), - Expr::Tuple(vec) => todo!(), + Expr::GroupingSets(vec) => { + union_spans(vec.iter().map(|i| i.iter().map(|k| k.span())).flatten()) + } + Expr::Cube(vec) => { + union_spans(vec.iter().map(|i| i.iter().map(|k| k.span())).flatten()) + } + Expr::Rollup(vec) => { + union_spans(vec.iter().map(|i| i.iter().map(|k| k.span())).flatten()) + } + Expr::Tuple(vec) => union_spans(vec.iter().map(|i| i.span())), Expr::Array(array) => todo!(), Expr::MatchAgainst { columns, diff --git a/src/tokenizer.rs b/src/tokenizer.rs index f0f861753..5c9fcbf10 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -482,6 +482,8 @@ impl Span { } pub fn union(&self, other: &Span) -> Span { + // If either span is empty, return the other + // this prevents propagating (0, 0) through the tree match (self, other) { (&Span::EMPTY, _) => other.clone(), (_, &Span::EMPTY) => self.clone(), @@ -733,8 +735,6 @@ impl<'a> Tokenizer<'a> { while let Some(token) = self.next_token(&mut state)? { let span = location.span_to(state.location()); - location = state.location(); - buf.push(TokenWithLocation { token, span }); location = state.location(); From aeb4f3a76d608297feb2bc3a566b45c5494e98c6 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Fri, 20 Sep 2024 11:24:48 +0200 Subject: [PATCH 07/32] start working on better tests --- src/ast/query.rs | 2 ++ src/ast/spans.rs | 57 +++++++++++++++++++++++++++------------ src/parser/mod.rs | 12 +++++---- tests/sqlparser_common.rs | 1 + 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 3947d3b8f..2c4d0a7a8 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -544,6 +544,8 @@ pub struct Cte { pub query: Box, pub from: Option, pub materialized: Option, + // needed for accurate span reporting + pub closing_paren_token: TokenWithLocation, } impl fmt::Display for Cte { diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 25c8e6dea..f9ce7845e 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -17,12 +17,9 @@ pub trait Spanned { impl Spanned for Query { fn span(&self) -> Span { - union_spans( - self.with - .iter() - .map(|item| item.span()) - .chain(core::iter::once(self.body.span())), - ) + self.body + .span() + .union_opt(&self.with.as_ref().map(|i| i.span())) } } @@ -40,7 +37,8 @@ impl Spanned for Cte { union_spans( core::iter::once(self.alias.span()) .chain(core::iter::once(self.query.span())) - .chain(self.from.iter().map(|item| item.span)), + .chain(self.from.iter().map(|item| item.span)) + .chain(core::iter::once(self.closing_paren_token.span)), ) } } @@ -589,13 +587,25 @@ impl Spanned for Select { #[cfg(test)] pub mod tests { - use ast::query; - use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect}; + use crate::parser::Parser; use crate::tokenizer::Span; use super::*; + struct SpanTest<'a>(Parser<'a>, &'a str); + + impl<'a> SpanTest<'a> { + fn new(dialect: &'a dyn Dialect, sql: &'a str) -> Self { + Self(Parser::new(dialect).try_with_sql(sql).unwrap(), sql) + } + + fn get_source(&self, span: Span) -> &'a str { + // lines in spans are 1-indexed + &self.1[(span.start.column as usize - 1)..(span.end.column - 1) as usize] + } + } + #[test] fn test_query_span() { let query = crate::parser::Parser::new(&GenericDialect::default()) @@ -660,14 +670,27 @@ pub mod tests { #[test] pub fn test_wildcard_from_cte() { - let query = crate::parser::Parser::new(&GenericDialect::default()) - .try_with_sql( - "WITH cte AS (SELECT a FROM postgres.public.source) SELECT cte.* FROM cte", - ) - .unwrap() - .parse_query() - .unwrap(); + let dialect = &GenericDialect::default(); + let mut test = SpanTest::new( + dialect, + "WITH cte AS (SELECT a FROM postgres.public.source) SELECT cte.* FROM cte", + ); - query.span(); + let query = test.0.parse_query().unwrap(); + let cte_span = query.clone().with.unwrap().cte_tables[0].span(); + let cte_query_span = query.clone().with.unwrap().cte_tables[0].query.span(); + let body_span = query.body.span(); + + // the WITH keyboard is part of the query + assert_eq!( + test.get_source(cte_span), + "cte AS (SELECT a FROM postgres.public.source)" + ); + assert_eq!( + test.get_source(cte_query_span), + "SELECT a FROM postgres.public.source" + ); + + assert_eq!(test.get_source(body_span), "SELECT cte.* FROM cte"); } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d98528389..14c47000f 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3336,9 +3336,9 @@ impl<'a> Parser<'a> { } /// Bail out if the current token is not an expected keyword, or consume it if it is - pub fn expect_token(&mut self, expected: &Token) -> Result<(), ParserError> { - if self.consume_token(expected) { - Ok(()) + pub fn expect_token(&mut self, expected: &Token) -> Result { + if self.peek_token() == *expected { + Ok(self.next_token()) } else { self.expected(&expected.to_string(), self.peek_token()) } @@ -8802,7 +8802,7 @@ impl<'a> Parser<'a> { } self.expect_token(&Token::LParen)?; let query = self.parse_boxed_query()?; - self.expect_token(&Token::RParen)?; + let closing_paren_token = self.expect_token(&Token::RParen)?; let alias = TableAlias { name, columns: vec![], @@ -8812,6 +8812,7 @@ impl<'a> Parser<'a> { query, from: None, materialized: is_materialized, + closing_paren_token, } } else { let columns = self.parse_parenthesized_column_list(Optional, false)?; @@ -8826,13 +8827,14 @@ impl<'a> Parser<'a> { } self.expect_token(&Token::LParen)?; let query = self.parse_boxed_query()?; - self.expect_token(&Token::RParen)?; + let closing_paren_token = self.expect_token(&Token::RParen)?; let alias = TableAlias { name, columns }; Cte { alias, query, from: None, materialized: is_materialized, + closing_paren_token, } }; if self.parse_keyword(Keyword::FROM).is_some() { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 97730c71f..38edc0c78 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -6403,6 +6403,7 @@ fn parse_recursive_cte() { query: Box::new(cte_query), from: None, materialized: None, + closing_paren_token: TokenWithLocation::wrap(Token::RParen), }; assert_eq!(with.cte_tables.first().unwrap(), &expected); } From 4de3209c9570a475c1d2a8256cc34651fa9ef2d5 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Wed, 25 Sep 2024 14:31:11 +0200 Subject: [PATCH 08/32] feat: implement spans for Wildcard projections --- src/ast/mod.rs | 14 +++++++------- src/ast/query.rs | 21 +++++++++++++++++++-- src/ast/spans.rs | 16 ++++++++++------ src/parser/mod.rs | 25 +++++++++++++++---------- tests/sqlparser_common.rs | 8 +------- tests/sqlparser_duckdb.rs | 16 ++-------------- 6 files changed, 54 insertions(+), 46 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a9f1c088d..e7ef065d4 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -28,7 +28,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -use crate::tokenizer::Span; +use crate::tokenizer::{Span, TokenWithLocation}; pub use self::data_type::{ ArrayElemTypeDef, CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, @@ -922,10 +922,10 @@ pub enum Expr { /// `` opt_search_modifier: Option, }, - Wildcard, + Wildcard(TokenWithLocation), /// Qualified wildcard, e.g. `alias.*` or `schema.table.*`. /// (Same caveats apply to `QualifiedWildcard` as to `Wildcard`.) - QualifiedWildcard(ObjectName), + QualifiedWildcard(ObjectName, TokenWithLocation), /// Some dialects support an older syntax for outer joins where columns are /// marked with the `(+)` operator in the WHERE clause, for example: /// @@ -1214,8 +1214,8 @@ impl fmt::Display for Expr { Expr::MapAccess { column, keys } => { write!(f, "{column}{}", display_separated(keys, "")) } - Expr::Wildcard => f.write_str("*"), - Expr::QualifiedWildcard(prefix) => write!(f, "{}.*", prefix), + Expr::Wildcard(_) => f.write_str("*"), + Expr::QualifiedWildcard(prefix, _) => write!(f, "{}.*", prefix), Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")), Expr::IsTrue(ast) => write!(f, "{ast} IS TRUE"), Expr::IsNotTrue(ast) => write!(f, "{ast} IS NOT TRUE"), @@ -5210,8 +5210,8 @@ pub enum FunctionArgExpr { impl From for FunctionArgExpr { fn from(wildcard_expr: Expr) -> Self { match wildcard_expr { - Expr::QualifiedWildcard(prefix) => Self::QualifiedWildcard(prefix), - Expr::Wildcard => Self::Wildcard, + Expr::QualifiedWildcard(prefix, _) => Self::QualifiedWildcard(prefix), + Expr::Wildcard(_) => Self::Wildcard, expr => Self::Expr(expr), } } diff --git a/src/ast/query.rs b/src/ast/query.rs index 2c4d0a7a8..090f8b7b0 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -19,7 +19,10 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -use crate::{ast::*, tokenizer::TokenWithLocation}; +use crate::{ + ast::*, + tokenizer::{Token, TokenWithLocation}, +}; /// The most complete variant of a `SELECT` query expression, optionally /// including `WITH`, `UNION` / other set operations, and `ORDER BY`. @@ -597,10 +600,11 @@ impl fmt::Display for IdentWithAlias { } /// Additional options for wildcards, e.g. Snowflake `EXCLUDE`/`RENAME` and Bigquery `EXCEPT`. -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct WildcardAdditionalOptions { + pub wildcard_token: TokenWithLocation, /// `[ILIKE...]`. /// Snowflake syntax: pub opt_ilike: Option, @@ -618,6 +622,19 @@ pub struct WildcardAdditionalOptions { pub opt_rename: Option, } +impl Default for WildcardAdditionalOptions { + fn default() -> Self { + Self { + wildcard_token: TokenWithLocation::wrap(Token::Mul), + opt_ilike: None, + opt_exclude: None, + opt_except: None, + opt_replace: None, + opt_rename: None, + } + } +} + impl fmt::Display for WildcardAdditionalOptions { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(ilike) = &self.opt_ilike { diff --git a/src/ast/spans.rs b/src/ast/spans.rs index f9ce7845e..9071e0bcf 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -245,8 +245,14 @@ impl Spanned for Expr { Expr::Map(map) => todo!(), Expr::Subscript { expr, subscript } => todo!(), Expr::Interval(interval) => todo!(), - Expr::Wildcard => todo!(), - Expr::QualifiedWildcard(object_name) => todo!(), + Expr::Wildcard(token) => token.span, + Expr::QualifiedWildcard(object_name, token) => union_spans( + object_name + .0 + .iter() + .map(|i| i.span) + .chain(iter::once(token.span)), + ), Expr::OuterJoin(expr) => todo!(), Expr::Prior(expr) => todo!(), Expr::Lambda(lambda_function) => todo!(), @@ -336,10 +342,8 @@ impl Spanned for SelectItem { impl Spanned for WildcardAdditionalOptions { fn span(&self) -> Span { union_spans( - self.opt_ilike - .as_ref() - .map(|i| i.span()) - .into_iter() + core::iter::once(self.wildcard_token.span) + .chain(self.opt_ilike.as_ref().map(|i| i.span()).into_iter()) .chain(self.opt_exclude.as_ref().map(|i| i.span()).into_iter()) .chain(self.opt_rename.as_ref().map(|i| i.span()).into_iter()) .chain(self.opt_replace.as_ref().map(|i| i.span()).into_iter()) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 14c47000f..d50cf9f35 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -875,7 +875,10 @@ impl<'a> Parser<'a> { id_parts.push(Ident::with_quote('\'', s)) } Token::Mul => { - return Ok(Expr::QualifiedWildcard(ObjectName(id_parts))); + return Ok(Expr::QualifiedWildcard( + ObjectName(id_parts), + next_token, + )); } _ => { return self @@ -886,7 +889,7 @@ impl<'a> Parser<'a> { } } Token::Mul => { - return Ok(Expr::Wildcard); + return Ok(Expr::Wildcard(next_token)); } _ => (), }; @@ -1088,7 +1091,7 @@ impl<'a> Parser<'a> { _ => match self.peek_token().token { Token::LParen | Token::Period => { let mut id_parts: Vec = vec![w.to_ident(next_token.span)]; - let mut ends_with_wildcard = false; + let mut ending_wildcard: Option = None; while self.consume_token(&Token::Period) { let next_token = self.next_token(); match next_token.token { @@ -1097,7 +1100,7 @@ impl<'a> Parser<'a> { // Postgres explicitly allows funcnm(tablenm.*) and the // function array_agg traverses this control flow if dialect_of!(self is PostgreSqlDialect) { - ends_with_wildcard = true; + ending_wildcard = Some(next_token); break; } else { return self @@ -1114,8 +1117,8 @@ impl<'a> Parser<'a> { } } - if ends_with_wildcard { - Ok(Expr::QualifiedWildcard(ObjectName(id_parts))) + if let Some(ending_wildcard_token) = ending_wildcard { + Ok(Expr::QualifiedWildcard(ObjectName(id_parts), ending_wildcard_token)) } else if self.consume_token(&Token::LParen) { if dialect_of!(self is SnowflakeDialect | MsSqlDialect) && self.consume_tokens(&[Token::Plus, Token::RParen]) @@ -10952,12 +10955,12 @@ impl<'a> Parser<'a> { /// Parse a comma-delimited list of projections after SELECT pub fn parse_select_item(&mut self) -> Result { match self.parse_wildcard_expr()? { - Expr::QualifiedWildcard(prefix) => Ok(SelectItem::QualifiedWildcard( + Expr::QualifiedWildcard(prefix, token) => Ok(SelectItem::QualifiedWildcard( prefix, - self.parse_wildcard_additional_options()?, + self.parse_wildcard_additional_options(token)?, )), - Expr::Wildcard => Ok(SelectItem::Wildcard( - self.parse_wildcard_additional_options()?, + Expr::Wildcard(token) => Ok(SelectItem::Wildcard( + self.parse_wildcard_additional_options(token)?, )), Expr::Identifier(v) if v.value.to_lowercase() == "from" && v.quote_style.is_none() => { parser_err!( @@ -10979,6 +10982,7 @@ impl<'a> Parser<'a> { /// If it is not possible to parse it, will return an option. pub fn parse_wildcard_additional_options( &mut self, + wildcard_token: TokenWithLocation, ) -> Result { let opt_ilike = if dialect_of!(self is GenericDialect | SnowflakeDialect) { self.parse_optional_select_item_ilike()? @@ -11010,6 +11014,7 @@ impl<'a> Parser<'a> { }; Ok(WildcardAdditionalOptions { + wildcard_token, opt_ilike, opt_exclude, opt_except, diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 38edc0c78..2d373cc11 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -7332,13 +7332,7 @@ fn lateral_function() { select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: None, top: None, - projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions { - opt_ilike: None, - opt_exclude: None, - opt_except: None, - opt_rename: None, - opt_replace: None, - })], + projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], into: None, from: vec![TableWithJoins { relation: TableFactor::Table { diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index aded09104..9d8583691 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -261,13 +261,7 @@ fn test_select_union_by_name() { select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: None, top: None, - projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions { - opt_ilike: None, - opt_exclude: None, - opt_except: None, - opt_rename: None, - opt_replace: None, - })], + projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], into: None, from: vec![TableWithJoins { relation: TableFactor::Table { @@ -303,13 +297,7 @@ fn test_select_union_by_name() { select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), distinct: None, top: None, - projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions { - opt_ilike: None, - opt_exclude: None, - opt_except: None, - opt_rename: None, - opt_replace: None, - })], + projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], into: None, from: vec![TableWithJoins { relation: TableFactor::Table { From a04888ac785e6f668bdaef9d634bf3e1560f2f1b Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Thu, 26 Sep 2024 11:40:12 +0200 Subject: [PATCH 09/32] make union_spans public --- src/ast/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index e7ef065d4..5b343ee83 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -82,7 +82,7 @@ pub mod helpers; mod operator; mod query; mod spans; -pub use spans::Spanned; +pub use spans::{Spanned, union_spans}; mod trigger; mod value; From 1b2b03d055d922790a3f98dc1d88d0f1174e3b2a Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Mon, 30 Sep 2024 10:42:50 +0200 Subject: [PATCH 10/32] enable serde feat for spans and locations --- src/tokenizer.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 5c9fcbf10..e9ada1fcd 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -417,6 +417,7 @@ impl fmt::Display for Whitespace { /// Location in input string #[derive(Eq, PartialEq, Hash, Clone, Copy, Ord, PartialOrd)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Location { /// Line number, starting from 1 pub line: u64, @@ -456,6 +457,7 @@ impl From<(u64, u64)> for Location { } #[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord, Copy)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Span { pub start: Location, pub end: Location, @@ -504,6 +506,7 @@ impl Span { /// A [Token] with [Location] attached to it #[derive(Debug, Eq, Hash, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TokenWithLocation { pub token: Token, pub span: Span, From 5f60bdc6c20282d37e482ddcb5fcb18d9bd1cfdf Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Mon, 7 Oct 2024 13:27:34 +0200 Subject: [PATCH 11/32] feat: implement remaining ast nodes --- src/ast/mod.rs | 2 +- src/ast/spans.rs | 1224 ++++++++++++++++++++++++++++++++++++++++++--- src/parser/mod.rs | 22 +- src/tokenizer.rs | 8 +- 4 files changed, 1169 insertions(+), 87 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 5b343ee83..123af6522 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -82,7 +82,7 @@ pub mod helpers; mod operator; mod query; mod spans; -pub use spans::{Spanned, union_spans}; +pub use spans::{union_spans, Spanned}; mod trigger; mod value; diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 9071e0bcf..ee8cd2122 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1,17 +1,34 @@ use core::iter; -use crate::ast; use crate::tokenizer::Span; use super::{ - Cte, ExceptSelectItem, ExcludeSelectItem, Expr, Function, FunctionArg, FunctionArgExpr, - FunctionArgumentClause, FunctionArgumentList, FunctionArguments, HavingBound, IlikeSelectItem, - Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, Query, RenameSelectItem, - ReplaceSelectElement, ReplaceSelectItem, Select, SelectItem, SetExpr, TableAlias, TableFactor, - TableWithJoins, Value, WildcardAdditionalOptions, With, + AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Array, Assignment, + AssignmentTarget, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, + ConflictTarget, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, + CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, + ExprWithAlias, FromTable, Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, + FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, IlikeSelectItem, Insert, + Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, + MatchRecognizePattern, Measure, ObjectName, OnConflict, OnConflictAction, OnInsert, OrderBy, + OrderByExpr, Partition, PivotValueSource, ProjectionSelect, Query, ReferentialAction, + RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectItem, SetExpr, + SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableConstraint, TableFactor, + TableOptionsClustered, TableWithJoins, Use, Value, Values, ViewColumnDef, + WildcardAdditionalOptions, With, WithFill, }; +/// A trait for AST nodes that have a source span for use in diagnostics. +/// +/// Source spans are not guaranteed to be entirely accurate. They may +/// be missing keywords or other tokens. Some nodes may not have a computable +/// span at all, in which case they return `Span::empty()`. +/// +/// Some impl blocks may contain doc comments with information +/// on which nodes are missing spans. pub trait Spanned { + /// Compute the source span for this AST node, by recursively + /// combining the spans of its children. fn span(&self) -> Span; } @@ -26,7 +43,7 @@ impl Spanned for Query { impl Spanned for With { fn span(&self) -> Span { union_spans( - core::iter::once(self.with_token.span.clone()) + core::iter::once(self.with_token.span) .chain(self.cte_tables.iter().map(|item| item.span())), ) } @@ -43,6 +60,9 @@ impl Spanned for Cte { } } +/// # partial span +/// +/// [SetExpr::Table] is not implemented. impl Spanned for SetExpr { fn span(&self) -> Span { match self { @@ -54,14 +74,921 @@ impl Spanned for SetExpr { left, right, } => left.span().union(&right.span()), - SetExpr::Values(values) => todo!(), - SetExpr::Insert(statement) => todo!(), - SetExpr::Table(table) => todo!(), - SetExpr::Update(statement) => todo!(), + SetExpr::Values(values) => values.span(), + SetExpr::Insert(statement) => statement.span(), + SetExpr::Table(table) => Span::empty(), + SetExpr::Update(statement) => statement.span(), } } } +impl Spanned for Values { + fn span(&self) -> Span { + union_spans( + self.rows + .iter() + .map(|row| union_spans(row.iter().map(|expr| expr.span()))), + ) + } +} + +/// # partial span +/// +/// Missing spans: +/// - [Statement::CopyIntoSnowflake] +/// - [Statement::CreateSecret] +/// - [Statement::CreateRole] +/// - [Statement::AlterRole] +/// - [Statement::AttachDatabase] +/// - [Statement::AttachDuckDBDatabase] +/// - [Statement::DetachDuckDBDatabase] +/// - [Statement::Drop] +/// - [Statement::DropFunction] +/// - [Statement::DropProcedure] +/// - [Statement::DropSecret] +/// - [Statement::Declare] +/// - [Statement::CreateExtension] +/// - [Statement::Fetch] +/// - [Statement::Flush] +/// - [Statement::Discard] +/// - [Statement::SetRole] +/// - [Statement::SetVariable] +/// - [Statement::SetTimeZone] +/// - [Statement::SetNames] +/// - [Statement::SetNamesDefault] +/// - [Statement::ShowFunctions] +/// - [Statement::ShowVariable] +/// - [Statement::ShowStatus] +/// - [Statement::ShowVariables] +/// - [Statement::ShowCreate] +/// - [Statement::ShowColumns] +/// - [Statement::ShowTables] +/// - [Statement::ShowCollation] +/// - [Statement::StartTransaction] +/// - [Statement::SetTransaction] +/// - [Statement::Comment] +/// - [Statement::Commit] +/// - [Statement::Rollback] +/// - [Statement::CreateSchema] +/// - [Statement::CreateDatabase] +/// - [Statement::CreateFunction] +/// - [Statement::CreateTrigger] +/// - [Statement::DropTrigger] +/// - [Statement::CreateProcedure] +/// - [Statement::CreateMacro] +/// - [Statement::CreateStage] +/// - [Statement::Assert] +/// - [Statement::Grant] +/// - [Statement::Revoke] +/// - [Statement::Deallocate] +/// - [Statement::Execute] +/// - [Statement::Prepare] +/// - [Statement::Kill] +/// - [Statement::ExplainTable] +/// - [Statement::Explain] +/// - [Statement::Savepoint] +/// - [Statement::ReleaseSavepoint] +/// - [Statement::Merge] +/// - [Statement::Cache] +/// - [Statement::UNCache] +/// - [Statement::CreateSequence] +/// - [Statement::CreateType] +/// - [Statement::Pragma] +/// - [Statement::LockTables] +/// - [Statement::UnlockTables] +/// - [Statement::Unload] +/// - [Statement::OptimizeTable] +impl Spanned for Statement { + fn span(&self) -> Span { + match self { + Statement::Analyze { + table_name, + partitions, + for_columns: _, + columns, + cache_metadata: _, + noscan: _, + compute_statistics: _, + } => union_spans( + core::iter::once(table_name.span()) + .chain(partitions.iter().flat_map(|i| i.iter().map(|k| k.span()))) + .chain(columns.iter().map(|i| i.span)), + ), + Statement::Truncate { + table_names, + partitions, + table: _, + only: _, + identity: _, + cascade: _, + } => union_spans( + table_names + .iter() + .map(|i| i.name.span()) + .chain(partitions.iter().flat_map(|i| i.iter().map(|k| k.span()))), + ), + Statement::Msck { + table_name, + repair: _, + partition_action: _, + } => table_name.span(), + Statement::Query(query) => query.span(), + Statement::Insert(insert) => insert.span(), + Statement::Install { extension_name } => extension_name.span, + Statement::Load { extension_name } => extension_name.span, + Statement::Directory { + overwrite: _, + local: _, + path: _, + file_format: _, + source, + } => source.span(), + Statement::Call(function) => function.span(), + Statement::Copy { + source, + to: _, + target: _, + options: _, + legacy_options: _, + values: _, + } => source.span(), + Statement::CopyIntoSnowflake { + into: _, + from_stage: _, + from_stage_alias: _, + stage_params: _, + from_transformations: _, + files: _, + pattern: _, + file_format: _, + copy_options: _, + validation_mode: _, + } => Span::empty(), + Statement::Close { cursor } => match cursor { + CloseCursor::All => Span::empty(), + CloseCursor::Specific { name } => name.span, + }, + Statement::Update { + table, + assignments, + from, + selection, + returning, + } => union_spans( + core::iter::once(table.span()) + .chain(assignments.iter().map(|i| i.span())) + .chain(from.iter().map(|i| i.span())) + .chain(selection.iter().map(|i| i.span())) + .chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span()))), + ), + Statement::Delete(delete) => delete.span(), + Statement::CreateView { + or_replace: _, + materialized: _, + name, + columns, + query, + options, + cluster_by, + comment: _, + with_no_schema_binding: _, + if_not_exists: _, + temporary: _, + to, + } => union_spans( + core::iter::once(name.span()) + .chain(columns.iter().map(|i| i.span())) + .chain(core::iter::once(query.span())) + .chain(core::iter::once(options.span())) + .chain(cluster_by.iter().map(|i| i.span)) + .chain(to.iter().map(|i| i.span())), + ), + Statement::CreateTable(create_table) => create_table.span(), + Statement::CreateVirtualTable { + name, + if_not_exists, + module_name, + module_args, + } => union_spans( + core::iter::once(name.span()) + .chain(core::iter::once(module_name.span)) + .chain(module_args.iter().map(|i| i.span)), + ), + Statement::CreateIndex(create_index) => create_index.span(), + Statement::CreateRole { .. } => Span::empty(), + Statement::CreateSecret { .. } => Span::empty(), + Statement::AlterTable { + name, + if_exists, + only, + operations, + location, + on_cluster, + } => union_spans( + core::iter::once(name.span()) + .chain(operations.iter().map(|i| i.span())) + .chain(on_cluster.iter().map(|i| i.span)), + ), + Statement::AlterIndex { name, operation } => name.span().union(&operation.span()), + Statement::AlterView { + name, + columns, + query, + with_options, + } => union_spans( + core::iter::once(name.span()) + .chain(columns.iter().map(|i| i.span)) + .chain(core::iter::once(query.span())) + .chain(with_options.iter().map(|i| i.span())), + ), + Statement::AlterRole { .. } => Span::empty(), + Statement::AttachDatabase { .. } => Span::empty(), + Statement::AttachDuckDBDatabase { .. } => Span::empty(), + Statement::DetachDuckDBDatabase { .. } => Span::empty(), + Statement::Drop { .. } => Span::empty(), + Statement::DropFunction { .. } => Span::empty(), + Statement::DropProcedure { .. } => Span::empty(), + Statement::DropSecret { .. } => Span::empty(), + Statement::Declare { .. } => Span::empty(), + Statement::CreateExtension { .. } => Span::empty(), + Statement::Fetch { .. } => Span::empty(), + Statement::Flush { .. } => Span::empty(), + Statement::Discard { .. } => Span::empty(), + Statement::SetRole { .. } => Span::empty(), + Statement::SetVariable { .. } => Span::empty(), + Statement::SetTimeZone { .. } => Span::empty(), + Statement::SetNames { .. } => Span::empty(), + Statement::SetNamesDefault {} => Span::empty(), + Statement::ShowFunctions { .. } => Span::empty(), + Statement::ShowVariable { .. } => Span::empty(), + Statement::ShowStatus { .. } => Span::empty(), + Statement::ShowVariables { .. } => Span::empty(), + Statement::ShowCreate { .. } => Span::empty(), + Statement::ShowColumns { .. } => Span::empty(), + Statement::ShowTables { .. } => Span::empty(), + Statement::ShowCollation { .. } => Span::empty(), + Statement::Use(u) => u.span(), + Statement::StartTransaction { .. } => Span::empty(), + Statement::SetTransaction { .. } => Span::empty(), + Statement::Comment { .. } => Span::empty(), + Statement::Commit { chain } => Span::empty(), + Statement::Rollback { chain, savepoint } => Span::empty(), + Statement::CreateSchema { .. } => Span::empty(), + Statement::CreateDatabase { .. } => Span::empty(), + Statement::CreateFunction { .. } => Span::empty(), + Statement::CreateTrigger { .. } => Span::empty(), + Statement::DropTrigger { .. } => Span::empty(), + Statement::CreateProcedure { .. } => Span::empty(), + Statement::CreateMacro { .. } => Span::empty(), + Statement::CreateStage { .. } => Span::empty(), + Statement::Assert { condition, message } => Span::empty(), + Statement::Grant { .. } => Span::empty(), + Statement::Revoke { .. } => Span::empty(), + Statement::Deallocate { name, prepare } => Span::empty(), + Statement::Execute { .. } => Span::empty(), + Statement::Prepare { .. } => Span::empty(), + Statement::Kill { modifier, id } => Span::empty(), + Statement::ExplainTable { .. } => Span::empty(), + Statement::Explain { .. } => Span::empty(), + Statement::Savepoint { name } => Span::empty(), + Statement::ReleaseSavepoint { name } => Span::empty(), + Statement::Merge { .. } => Span::empty(), + Statement::Cache { .. } => Span::empty(), + Statement::UNCache { .. } => Span::empty(), + Statement::CreateSequence { .. } => Span::empty(), + Statement::CreateType { .. } => Span::empty(), + Statement::Pragma { name, value, is_eq } => Span::empty(), + Statement::LockTables { tables } => Span::empty(), + Statement::UnlockTables => Span::empty(), + Statement::Unload { query, to, with } => Span::empty(), + Statement::OptimizeTable { .. } => Span::empty(), + } + } +} + +impl Spanned for Use { + fn span(&self) -> Span { + match self { + Use::Catalog(object_name) => object_name.span(), + Use::Schema(object_name) => object_name.span(), + Use::Database(object_name) => object_name.span(), + Use::Warehouse(object_name) => object_name.span(), + Use::Object(object_name) => object_name.span(), + Use::Default => Span::empty(), + } + } +} + +impl Spanned for CreateTable { + fn span(&self) -> Span { + union_spans( + core::iter::once(self.name.span()) + .chain(self.columns.iter().map(|i| i.span())) + .chain(self.constraints.iter().map(|i| i.span())) + .chain(self.table_properties.iter().map(|i| i.span())) + .chain(self.with_options.iter().map(|i| i.span())) + .chain(self.query.iter().map(|i| i.span())) + .chain(self.like.iter().map(|i| i.span())) + .chain(self.clone.iter().map(|i| i.span())), + ) + } +} + +impl Spanned for ColumnDef { + fn span(&self) -> Span { + union_spans( + core::iter::once(self.name.span) + .chain(self.collation.iter().map(|i| i.span())) + .chain(self.options.iter().map(|i| i.span())), + ) + } +} + +impl Spanned for ColumnOptionDef { + fn span(&self) -> Span { + self.option + .span() + .union_opt(&self.name.as_ref().map(|i| i.span)) + } +} + +impl Spanned for TableConstraint { + fn span(&self) -> Span { + match self { + TableConstraint::Unique { + name, + index_name, + index_type_display: _, + index_type: _, + columns, + index_options: _, + characteristics, + } => union_spans( + name.iter() + .map(|i| i.span) + .chain(index_name.iter().map(|i| i.span)) + .chain(columns.iter().map(|i| i.span)) + .chain(characteristics.iter().map(|i| i.span())), + ), + TableConstraint::PrimaryKey { + name, + index_name, + index_type: _, + columns, + index_options: _, + characteristics, + } => union_spans( + name.iter() + .map(|i| i.span) + .chain(index_name.iter().map(|i| i.span)) + .chain(columns.iter().map(|i| i.span)) + .chain(characteristics.iter().map(|i| i.span())), + ), + TableConstraint::ForeignKey { + name, + columns, + foreign_table, + referred_columns, + on_delete, + on_update, + characteristics, + } => union_spans( + name.iter() + .map(|i| i.span) + .chain(columns.iter().map(|i| i.span)) + .chain(core::iter::once(foreign_table.span())) + .chain(referred_columns.iter().map(|i| i.span)) + .chain(on_delete.iter().map(|i| i.span())) + .chain(on_update.iter().map(|i| i.span())) + .chain(characteristics.iter().map(|i| i.span())), + ), + TableConstraint::Check { name, expr } => { + expr.span().union_opt(&name.as_ref().map(|i| i.span)) + } + TableConstraint::Index { + display_as_key, + name, + index_type, + columns, + } => union_spans( + name.iter() + .map(|i| i.span) + .chain(columns.iter().map(|i| i.span)), + ), + TableConstraint::FulltextOrSpatial { + fulltext, + index_type_display, + opt_index_name, + columns, + } => union_spans( + opt_index_name + .iter() + .map(|i| i.span) + .chain(columns.iter().map(|i| i.span)), + ), + } + } +} + +impl Spanned for CreateIndex { + fn span(&self) -> Span { + union_spans( + self.name + .iter() + .map(|i| i.span()) + .chain(core::iter::once(self.table_name.span())) + .chain(self.using.iter().map(|i| i.span)) + .chain(self.columns.iter().map(|i| i.span())) + .chain(self.include.iter().map(|i| i.span)) + .chain(self.with.iter().map(|i| i.span())) + .chain(self.predicate.iter().map(|i| i.span())), + ) + } +} + +/// # partial span +/// +/// Missing spans: +/// - [ColumnOption::Null] +/// - [ColumnOption::NotNull] +/// - [ColumnOption::Comment] +/// - [ColumnOption::Unique]¨ +/// - [ColumnOption::DialectSpecific] +/// - [ColumnOption::Generated] +impl Spanned for ColumnOption { + fn span(&self) -> Span { + match self { + ColumnOption::Null => Span::empty(), + ColumnOption::NotNull => Span::empty(), + ColumnOption::Default(expr) => expr.span(), + ColumnOption::Materialized(expr) => expr.span(), + ColumnOption::Ephemeral(expr) => expr.as_ref().map_or(Span::empty(), |e| e.span()), + ColumnOption::Alias(expr) => expr.span(), + ColumnOption::Unique { .. } => Span::empty(), + ColumnOption::ForeignKey { + foreign_table, + referred_columns, + on_delete, + on_update, + characteristics, + } => union_spans( + core::iter::once(foreign_table.span()) + .chain(referred_columns.iter().map(|i| i.span)) + .chain(on_delete.iter().map(|i| i.span())) + .chain(on_update.iter().map(|i| i.span())) + .chain(characteristics.iter().map(|i| i.span())), + ), + ColumnOption::Check(expr) => expr.span(), + ColumnOption::DialectSpecific(_) => Span::empty(), + ColumnOption::CharacterSet(object_name) => object_name.span(), + ColumnOption::Comment(_) => Span::empty(), + ColumnOption::OnUpdate(expr) => expr.span(), + ColumnOption::Generated { .. } => Span::empty(), + ColumnOption::Options(vec) => union_spans(vec.iter().map(|i| i.span())), + } + } +} + +/// # missing span +impl Spanned for ReferentialAction { + fn span(&self) -> Span { + Span::empty() + } +} + +/// # missing span +impl Spanned for ConstraintCharacteristics { + fn span(&self) -> Span { + Span::empty() + } +} + +/// # partial span +/// +/// Missing spans: +/// - [AlterColumnOperation::SetNotNull] +/// - [AlterColumnOperation::DropNotNull] +/// - [AlterColumnOperation::DropDefault] +/// - [AlterColumnOperation::AddGenerated] +impl Spanned for AlterColumnOperation { + fn span(&self) -> Span { + match self { + AlterColumnOperation::SetNotNull => Span::empty(), + AlterColumnOperation::DropNotNull => Span::empty(), + AlterColumnOperation::SetDefault { value } => value.span(), + AlterColumnOperation::DropDefault => Span::empty(), + AlterColumnOperation::SetDataType { + data_type: _, + using, + } => using.as_ref().map_or(Span::empty(), |u| u.span()), + AlterColumnOperation::AddGenerated { .. } => Span::empty(), + } + } +} + +impl Spanned for CopySource { + fn span(&self) -> Span { + match self { + CopySource::Table { + table_name, + columns, + } => union_spans( + core::iter::once(table_name.span()).chain(columns.iter().map(|i| i.span)), + ), + CopySource::Query(query) => query.span(), + } + } +} + +impl Spanned for Delete { + fn span(&self) -> Span { + union_spans( + self.tables + .iter() + .map(|i| i.span()) + .chain(core::iter::once(self.from.span())) + .chain( + self.using + .iter() + .map(|u| union_spans(u.iter().map(|i| i.span()))), + ) + .chain(self.selection.iter().map(|i| i.span())) + .chain( + self.returning + .iter() + .flat_map(|i| i.iter().map(|k| k.span())), + ) + .chain(self.order_by.iter().map(|i| i.span())) + .chain(self.limit.iter().map(|i| i.span())), + ) + } +} + +impl Spanned for FromTable { + fn span(&self) -> Span { + match self { + FromTable::WithFromKeyword(vec) => union_spans(vec.iter().map(|i| i.span())), + FromTable::WithoutKeyword(vec) => union_spans(vec.iter().map(|i| i.span())), + } + } +} + +impl Spanned for ViewColumnDef { + fn span(&self) -> Span { + union_spans( + core::iter::once(self.name.span) + .chain(self.options.iter().flat_map(|i| i.iter().map(|k| k.span()))), + ) + } +} + +impl Spanned for SqlOption { + fn span(&self) -> Span { + match self { + SqlOption::Clustered(table_options_clustered) => table_options_clustered.span(), + SqlOption::Ident(ident) => ident.span, + SqlOption::KeyValue { key, value } => key.span.union(&value.span()), + SqlOption::Partition { + column_name, + range_direction: _, + for_values, + } => union_spans( + core::iter::once(column_name.span).chain(for_values.iter().map(|i| i.span())), + ), + } + } +} + +/// # partial span +/// +/// Missing spans: +/// - [TableOptionsClustered::ColumnstoreIndex] +impl Spanned for TableOptionsClustered { + fn span(&self) -> Span { + match self { + TableOptionsClustered::ColumnstoreIndex => Span::empty(), + TableOptionsClustered::ColumnstoreIndexOrder(vec) => { + union_spans(vec.iter().map(|i| i.span)) + } + TableOptionsClustered::Index(vec) => union_spans(vec.iter().map(|i| i.span())), + } + } +} + +impl Spanned for ClusteredIndex { + fn span(&self) -> Span { + self.name.span + } +} + +impl Spanned for CreateTableOptions { + fn span(&self) -> Span { + match self { + CreateTableOptions::None => Span::empty(), + CreateTableOptions::With(vec) => union_spans(vec.iter().map(|i| i.span())), + CreateTableOptions::Options(vec) => union_spans(vec.iter().map(|i| i.span())), + } + } +} + +/// # partial span +/// +/// Missing spans: +/// - [AlterTableOperation::OwnerTo] +impl Spanned for AlterTableOperation { + fn span(&self) -> Span { + match self { + AlterTableOperation::AddConstraint(table_constraint) => table_constraint.span(), + AlterTableOperation::AddColumn { + column_keyword, + if_not_exists, + column_def, + column_position, + } => column_def.span(), + AlterTableOperation::AddProjection { + if_not_exists, + name, + select, + } => name.span.union(&select.span()), + AlterTableOperation::DropProjection { if_exists, name } => name.span, + AlterTableOperation::MaterializeProjection { + if_exists, + name, + partition, + } => name.span.union_opt(&partition.as_ref().map(|i| i.span)), + AlterTableOperation::ClearProjection { + if_exists, + name, + partition, + } => name.span.union_opt(&partition.as_ref().map(|i| i.span)), + AlterTableOperation::DisableRowLevelSecurity => Span::empty(), + AlterTableOperation::DisableRule { name } => name.span, + AlterTableOperation::DisableTrigger { name } => name.span, + AlterTableOperation::DropConstraint { + if_exists, + name, + cascade, + } => name.span, + AlterTableOperation::DropColumn { + column_name, + if_exists, + cascade, + } => column_name.span, + AlterTableOperation::AttachPartition { partition } => partition.span(), + AlterTableOperation::DetachPartition { partition } => partition.span(), + AlterTableOperation::FreezePartition { + partition, + with_name, + } => partition + .span() + .union_opt(&with_name.as_ref().map(|n| n.span)), + AlterTableOperation::UnfreezePartition { + partition, + with_name, + } => partition + .span() + .union_opt(&with_name.as_ref().map(|n| n.span)), + AlterTableOperation::DropPrimaryKey => Span::empty(), + AlterTableOperation::EnableAlwaysRule { name } => name.span, + AlterTableOperation::EnableAlwaysTrigger { name } => name.span, + AlterTableOperation::EnableReplicaRule { name } => name.span, + AlterTableOperation::EnableReplicaTrigger { name } => name.span, + AlterTableOperation::EnableRowLevelSecurity => Span::empty(), + AlterTableOperation::EnableRule { name } => name.span, + AlterTableOperation::EnableTrigger { name } => name.span, + AlterTableOperation::RenamePartitions { + old_partitions, + new_partitions, + } => union_spans( + old_partitions + .iter() + .map(|i| i.span()) + .chain(new_partitions.iter().map(|i| i.span())), + ), + AlterTableOperation::AddPartitions { + if_not_exists, + new_partitions, + } => union_spans(new_partitions.iter().map(|i| i.span())), + AlterTableOperation::DropPartitions { + partitions, + if_exists, + } => union_spans(partitions.iter().map(|i| i.span())), + AlterTableOperation::RenameColumn { + old_column_name, + new_column_name, + } => old_column_name.span.union(&new_column_name.span), + AlterTableOperation::RenameTable { table_name } => table_name.span(), + AlterTableOperation::ChangeColumn { + old_name, + new_name, + data_type, + options, + column_position, + } => union_spans( + core::iter::once(old_name.span) + .chain(core::iter::once(new_name.span)) + .chain(options.iter().map(|i| i.span())), + ), + AlterTableOperation::ModifyColumn { + col_name, + data_type, + options, + column_position, + } => { + union_spans(core::iter::once(col_name.span).chain(options.iter().map(|i| i.span()))) + } + AlterTableOperation::RenameConstraint { old_name, new_name } => { + old_name.span.union(&new_name.span) + } + AlterTableOperation::AlterColumn { column_name, op } => { + column_name.span.union(&op.span()) + } + AlterTableOperation::SwapWith { table_name } => table_name.span(), + AlterTableOperation::SetTblProperties { table_properties } => { + union_spans(table_properties.iter().map(|i| i.span())) + } + AlterTableOperation::OwnerTo { new_owner } => Span::empty(), + } + } +} + +impl Spanned for Partition { + fn span(&self) -> Span { + match self { + Partition::Identifier(ident) => ident.span, + Partition::Expr(expr) => expr.span(), + Partition::Part(expr) => expr.span(), + Partition::Partitions(vec) => union_spans(vec.iter().map(|i| i.span())), + } + } +} + +impl Spanned for ProjectionSelect { + fn span(&self) -> Span { + union_spans( + self.projection + .iter() + .map(|i| i.span()) + .chain(self.order_by.iter().map(|i| i.span())) + .chain(self.group_by.iter().map(|i| i.span())), + ) + } +} + +impl Spanned for OrderBy { + fn span(&self) -> Span { + union_spans( + self.exprs + .iter() + .map(|i| i.span()) + .chain(self.interpolate.iter().map(|i| i.span())), + ) + } +} + +/// # partial span +/// +/// Missing spans: +/// - [GroupByExpr::All] +impl Spanned for GroupByExpr { + fn span(&self) -> Span { + match self { + GroupByExpr::All(vec) => Span::empty(), + GroupByExpr::Expressions(exprs, _modifiers) => { + union_spans(exprs.iter().map(|i| i.span())) + } + } + } +} + +impl Spanned for Interpolate { + fn span(&self) -> Span { + union_spans(self.exprs.iter().flat_map(|i| i.iter().map(|e| e.span()))) + } +} + +impl Spanned for InterpolateExpr { + fn span(&self) -> Span { + self.column + .span + .union_opt(&self.expr.as_ref().map(|e| e.span())) + } +} + +impl Spanned for AlterIndexOperation { + fn span(&self) -> Span { + match self { + AlterIndexOperation::RenameIndex { index_name } => index_name.span(), + } + } +} + +/// # partial span +/// +/// Missing spans:ever +/// - [Insert::insert_alias] +impl Spanned for Insert { + fn span(&self) -> Span { + union_spans( + core::iter::once(self.table_name.span()) + .chain(self.table_alias.as_ref().map(|i| i.span)) + .chain(self.columns.iter().map(|i| i.span)) + .chain(self.source.as_ref().map(|q| q.span())) + .chain( + self.partitioned + .iter() + .flat_map(|i| i.iter().map(|k| k.span())), + ) + .chain(self.after_columns.iter().map(|i| i.span)) + .chain(self.on.as_ref().map(|i| i.span())) + .chain( + self.returning + .iter() + .flat_map(|i| i.iter().map(|k| k.span())), + ), + ) + } +} + +impl Spanned for OnInsert { + fn span(&self) -> Span { + match self { + OnInsert::DuplicateKeyUpdate(vec) => union_spans(vec.iter().map(|i| i.span())), + OnInsert::OnConflict(on_conflict) => on_conflict.span(), + } + } +} + +impl Spanned for OnConflict { + fn span(&self) -> Span { + self.action + .span() + .union_opt(&self.conflict_target.as_ref().map(|i| i.span())) + } +} + +impl Spanned for ConflictTarget { + fn span(&self) -> Span { + match self { + ConflictTarget::Columns(vec) => union_spans(vec.iter().map(|i| i.span)), + ConflictTarget::OnConstraint(object_name) => object_name.span(), + } + } +} + +/// # partial span +/// +/// Missing spans: +/// - [OnConflictAction::DoNothing] +impl Spanned for OnConflictAction { + fn span(&self) -> Span { + match self { + OnConflictAction::DoNothing => Span::empty(), + OnConflictAction::DoUpdate(do_update) => do_update.span(), + } + } +} + +impl Spanned for DoUpdate { + fn span(&self) -> Span { + union_spans( + self.assignments + .iter() + .map(|i| i.span()) + .chain(self.selection.iter().map(|i| i.span())), + ) + } +} + +impl Spanned for Assignment { + fn span(&self) -> Span { + self.target.span().union(&self.value.span()) + } +} + +impl Spanned for AssignmentTarget { + fn span(&self) -> Span { + match self { + AssignmentTarget::ColumnName(object_name) => object_name.span(), + AssignmentTarget::Tuple(vec) => union_spans(vec.iter().map(|i| i.span())), + } + } +} + +/// # partial span +/// +/// Most expressions are missing keywords in their spans. +/// f.e. `IS NULL ` reports as `::span`. +/// +/// Missing spans: +/// - [Expr::TypedString] +/// - [Expr::MatchAgainst] # MySQL specific +/// - [Expr::RLike] # MySQL specific +/// - [Expr::Struct] # BigQuery specific +/// - [Expr::Named] # BigQuery specific +/// - [Expr::Dictionary] # DuckDB specific +/// - [Expr::Map] # DuckDB specific +/// - [Expr::Lambda] impl Spanned for Expr { fn span(&self) -> Span { match self { @@ -139,42 +1066,40 @@ impl Spanned for Expr { .union(&union_spans(collation.0.iter().map(|i| i.span))), Expr::Nested(expr) => expr.span(), Expr::Value(value) => value.span(), - Expr::TypedString { data_type, value } => todo!(), - Expr::MapAccess { column, keys } => todo!(), + Expr::TypedString { data_type, value } => Span::empty(), + Expr::MapAccess { column, keys } => column + .span() + .union(&union_spans(keys.iter().map(|i| i.key.span()))), Expr::Function(function) => function.span(), Expr::GroupingSets(vec) => { - union_spans(vec.iter().map(|i| i.iter().map(|k| k.span())).flatten()) - } - Expr::Cube(vec) => { - union_spans(vec.iter().map(|i| i.iter().map(|k| k.span())).flatten()) - } - Expr::Rollup(vec) => { - union_spans(vec.iter().map(|i| i.iter().map(|k| k.span())).flatten()) + union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))) } + Expr::Cube(vec) => union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))), + Expr::Rollup(vec) => union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))), Expr::Tuple(vec) => union_spans(vec.iter().map(|i| i.span())), - Expr::Array(array) => todo!(), + Expr::Array(array) => array.span(), Expr::MatchAgainst { columns, match_value, opt_search_modifier, - } => todo!(), + } => Span::empty(), Expr::JsonAccess { value, path } => value.span().union(&path.span()), Expr::RLike { negated, expr, pattern, regexp, - } => todo!(), + } => Span::empty(), Expr::AnyOp { left, compare_op, right, - } => todo!(), + } => left.span().union(&right.span()), Expr::AllOp { left, compare_op, right, - } => todo!(), + } => left.span().union(&right.span()), Expr::UnaryOp { op, expr } => expr.span(), Expr::Convert { expr, @@ -182,7 +1107,11 @@ impl Spanned for Expr { charset, target_before_value, styles, - } => todo!(), + } => union_spans( + core::iter::once(expr.span()) + .chain(charset.as_ref().map(|i| i.span())) + .chain(styles.iter().map(|i| i.span())), + ), Expr::Cast { kind, expr, @@ -192,12 +1121,12 @@ impl Spanned for Expr { Expr::AtTimeZone { timestamp, time_zone, - } => todo!(), + } => timestamp.span().union(&time_zone.span()), Expr::Extract { field, syntax, expr, - } => todo!(), + } => expr.span(), Expr::Substring { expr, substring_from, @@ -222,7 +1151,7 @@ impl Spanned for Expr { .map(|items| union_spans(items.iter().map(|i| i.span()))), ), ), - Expr::IntroducedString { introducer, value } => value.span(), + Expr::IntroducedString { value, .. } => value.span(), Expr::Case { operand, conditions, @@ -237,14 +1166,14 @@ impl Spanned for Expr { .chain(results.iter().map(|i| i.span())) .chain(else_result.as_ref().map(|i| i.span())), ), - Expr::Exists { subquery, negated } => subquery.span(), + Expr::Exists { subquery, .. } => subquery.span(), Expr::Subquery(query) => query.span(), - Expr::Struct { values, fields } => todo!(), - Expr::Named { expr, name } => todo!(), - Expr::Dictionary(vec) => todo!(), - Expr::Map(map) => todo!(), - Expr::Subscript { expr, subscript } => todo!(), - Expr::Interval(interval) => todo!(), + Expr::Struct { .. } => Span::empty(), + Expr::Named { .. } => Span::empty(), + Expr::Dictionary(_) => Span::empty(), + Expr::Map(_) => Span::empty(), + Expr::Subscript { expr, subscript } => expr.span().union(&subscript.span()), + Expr::Interval(interval) => interval.value.span(), Expr::Wildcard(token) => token.span, Expr::QualifiedWildcard(object_name, token) => union_spans( object_name @@ -253,13 +1182,47 @@ impl Spanned for Expr { .map(|i| i.span) .chain(iter::once(token.span)), ), - Expr::OuterJoin(expr) => todo!(), - Expr::Prior(expr) => todo!(), - Expr::Lambda(lambda_function) => todo!(), + Expr::OuterJoin(expr) => expr.span(), + Expr::Prior(expr) => expr.span(), + Expr::Lambda(_) => Span::empty(), } } } +impl Spanned for Subscript { + fn span(&self) -> Span { + match self { + Subscript::Index { index } => index.span(), + Subscript::Slice { + lower_bound, + upper_bound, + stride, + } => union_spans( + [ + lower_bound.as_ref().map(|i| i.span()), + upper_bound.as_ref().map(|i| i.span()), + stride.as_ref().map(|i| i.span()), + ] + .iter() + .flatten() + .cloned(), + ), + } + } +} + +impl Spanned for ObjectName { + fn span(&self) -> Span { + union_spans(self.0.iter().map(|i| i.span)) + } +} + +impl Spanned for Array { + fn span(&self) -> Span { + union_spans(self.elem.iter().map(|i| i.span())) + } +} + impl Spanned for Function { fn span(&self) -> Span { union_spans( @@ -272,6 +1235,9 @@ impl Spanned for Function { } } +/// # partial span +/// +/// The span of [FunctionArguments::None] is empty. impl Spanned for FunctionArguments { fn span(&self) -> Span { match self { @@ -307,12 +1273,19 @@ impl Spanned for FunctionArgumentClause { } } +/// # partial span +/// +/// see Spanned impl for JsonPathElem for more information impl Spanned for JsonPath { fn span(&self) -> Span { union_spans(self.path.iter().map(|i| i.span())) } } +/// # partial span +/// +/// Missing spans: +/// - [JsonPathElem::Dot] impl Spanned for JsonPathElem { fn span(&self) -> Span { match self { @@ -343,18 +1316,19 @@ impl Spanned for WildcardAdditionalOptions { fn span(&self) -> Span { union_spans( core::iter::once(self.wildcard_token.span) - .chain(self.opt_ilike.as_ref().map(|i| i.span()).into_iter()) - .chain(self.opt_exclude.as_ref().map(|i| i.span()).into_iter()) - .chain(self.opt_rename.as_ref().map(|i| i.span()).into_iter()) - .chain(self.opt_replace.as_ref().map(|i| i.span()).into_iter()) - .chain(self.opt_except.as_ref().map(|i| i.span()).into_iter()), + .chain(self.opt_ilike.as_ref().map(|i| i.span())) + .chain(self.opt_exclude.as_ref().map(|i| i.span())) + .chain(self.opt_rename.as_ref().map(|i| i.span())) + .chain(self.opt_replace.as_ref().map(|i| i.span())) + .chain(self.opt_except.as_ref().map(|i| i.span())), ) } } +/// # missing span impl Spanned for IlikeSelectItem { fn span(&self) -> Span { - Span::empty() // # todo: missing span + Span::empty() } } @@ -399,6 +1373,10 @@ impl Spanned for ReplaceSelectElement { } } +/// # partial span +/// +/// Missing spans: +/// - [TableFactor::JsonTable] impl Spanned for TableFactor { fn span(&self) -> Span { match self { @@ -411,17 +1389,14 @@ impl Spanned for TableFactor { with_ordinality, partitions, } => union_spans( - name.0.iter().map(|i| i.span).chain( - alias - .as_ref() - .map(|alias| { - union_spans( - iter::once(alias.name.span) - .chain(alias.columns.iter().map(|i| i.span)), - ) - }) - .into_iter(), - ), + name.0 + .iter() + .map(|i| i.span) + .chain(alias.as_ref().map(|alias| { + union_spans( + iter::once(alias.name.span).chain(alias.columns.iter().map(|i| i.span)), + ) + })), ), TableFactor::Derived { lateral, @@ -430,18 +1405,28 @@ impl Spanned for TableFactor { } => subquery .span() .union_opt(&alias.as_ref().map(|alias| alias.span())), - TableFactor::TableFunction { expr, alias } => todo!(), + TableFactor::TableFunction { expr, alias } => expr + .span() + .union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::UNNEST { alias, with_offset, with_offset_alias, array_exprs, with_ordinality, - } => todo!(), + } => union_spans( + alias + .iter() + .map(|i| i.span()) + .chain(array_exprs.iter().map(|i| i.span())) + .chain(with_offset_alias.as_ref().map(|i| i.span)), + ), TableFactor::NestedJoin { table_with_joins, alias, - } => todo!(), + } => table_with_joins + .span() + .union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::Function { lateral, name, @@ -459,7 +1444,7 @@ impl Spanned for TableFactor { json_path, columns, alias, - } => todo!(), + } => Span::empty(), TableFactor::Pivot { table, aggregate_functions, @@ -467,14 +1452,27 @@ impl Spanned for TableFactor { value_source, default_on_null, alias, - } => todo!(), + } => union_spans( + core::iter::once(table.span()) + .chain(aggregate_functions.iter().map(|i| i.span())) + .chain(value_column.iter().map(|i| i.span)) + .chain(core::iter::once(value_source.span())) + .chain(default_on_null.as_ref().map(|i| i.span())) + .chain(alias.as_ref().map(|i| i.span())), + ), TableFactor::Unpivot { table, value, name, columns, alias, - } => todo!(), + } => union_spans( + core::iter::once(table.span()) + .chain(core::iter::once(value.span)) + .chain(core::iter::once(name.span)) + .chain(columns.iter().map(|i| i.span)) + .chain(alias.as_ref().map(|alias| alias.span())), + ), TableFactor::MatchRecognize { table, partition_by, @@ -485,11 +1483,76 @@ impl Spanned for TableFactor { pattern, symbols, alias, - } => todo!(), + } => union_spans( + core::iter::once(table.span()) + .chain(partition_by.iter().map(|i| i.span())) + .chain(order_by.iter().map(|i| i.span())) + .chain(measures.iter().map(|i| i.span())) + .chain(core::iter::once(pattern.span())) + .chain(symbols.iter().map(|i| i.span())) + .chain(alias.as_ref().map(|i| i.span())), + ), } } } +impl Spanned for PivotValueSource { + fn span(&self) -> Span { + match self { + PivotValueSource::List(vec) => union_spans(vec.iter().map(|i| i.span())), + PivotValueSource::Any(vec) => union_spans(vec.iter().map(|i| i.span())), + PivotValueSource::Subquery(query) => query.span(), + } + } +} + +impl Spanned for ExprWithAlias { + fn span(&self) -> Span { + self.expr + .span() + .union_opt(&self.alias.as_ref().map(|i| i.span)) + } +} + +/// # missing span +impl Spanned for MatchRecognizePattern { + fn span(&self) -> Span { + Span::empty() + } +} + +impl Spanned for SymbolDefinition { + fn span(&self) -> Span { + self.symbol.span.union(&self.definition.span()) + } +} + +impl Spanned for Measure { + fn span(&self) -> Span { + self.expr.span().union(&self.alias.span) + } +} + +impl Spanned for OrderByExpr { + fn span(&self) -> Span { + self.expr + .span() + .union_opt(&self.with_fill.as_ref().map(|f| f.span()).clone()) + } +} + +impl Spanned for WithFill { + fn span(&self) -> Span { + union_spans( + self.from + .iter() + .map(|f| f.span()) + .chain(self.to.iter().map(|t| t.span())) + .chain(self.step.iter().map(|s| s.span())), + ) + } +} + impl Spanned for FunctionArg { fn span(&self) -> Span { match self { @@ -503,6 +1566,10 @@ impl Spanned for FunctionArg { } } +/// # partial span +/// +/// Missing spans: +/// - [FunctionArgExpr::Wildcard] impl Spanned for FunctionArgExpr { fn span(&self) -> Span { match self { @@ -521,6 +1588,10 @@ impl Spanned for TableAlias { } } +/// # missing span +/// +/// The span of a `Value` is currently not implemented, as doing so +/// requires a breaking changes, which may be done in a future release. impl Spanned for Value { fn span(&self) -> Span { Span::empty() // # todo: Value needs to store spans before this is possible @@ -533,6 +1604,12 @@ impl Spanned for Join { } } +/// # partial span +/// +/// Missing spans: +/// - [JoinOperator::CrossJoin] +/// - [JoinOperator::CrossApply] +/// - [JoinOperator::OuterApply] impl Spanned for JoinOperator { fn span(&self) -> Span { match self { @@ -550,11 +1627,16 @@ impl Spanned for JoinOperator { JoinOperator::AsOf { match_condition, constraint, - } => todo!(), + } => match_condition.span().union(&constraint.span()), } } } +/// # partial span +/// +/// Missing spans: +/// - [JoinConstraint::Natural] +/// - [JoinConstraint::None] impl Spanned for JoinConstraint { fn span(&self) -> Span { match self { @@ -582,7 +1664,7 @@ pub fn union_spans>(iter: I) -> Span { impl Spanned for Select { fn span(&self) -> Span { union_spans( - core::iter::once(self.select_token.span.clone()) + core::iter::once(self.select_token.span) .chain(self.projection.iter().map(|item| item.span())) .chain(self.from.iter().map(|item| item.span())), ) @@ -612,7 +1694,7 @@ pub mod tests { #[test] fn test_query_span() { - let query = crate::parser::Parser::new(&GenericDialect::default()) + let query = crate::parser::Parser::new(&GenericDialect) .try_with_sql( "SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id", ) @@ -628,7 +1710,7 @@ pub mod tests { #[test] pub fn test_union() { - let query = crate::parser::Parser::new(&GenericDialect::default()) + let query = crate::parser::Parser::new(&GenericDialect) .try_with_sql( "SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source", ) @@ -641,7 +1723,7 @@ pub mod tests { #[test] pub fn test_subquery() { - let query = crate::parser::Parser::new(&GenericDialect::default()) + let query = crate::parser::Parser::new(&GenericDialect) .try_with_sql("SELECT a FROM (SELECT a FROM postgres.public.source) AS b") .unwrap() .parse_query() @@ -652,7 +1734,7 @@ pub mod tests { #[test] pub fn test_cte() { - let query = crate::parser::Parser::new(&GenericDialect::default()) + let query = crate::parser::Parser::new(&GenericDialect) .try_with_sql("WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner") .unwrap() .parse_query() @@ -663,7 +1745,7 @@ pub mod tests { #[test] pub fn test_snowflake_lateral_flatten() { - let query = crate::parser::Parser::new(&SnowflakeDialect::default()) + let query = crate::parser::Parser::new(&SnowflakeDialect) .try_with_sql("SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED") .unwrap() .parse_query() @@ -674,7 +1756,7 @@ pub mod tests { #[test] pub fn test_wildcard_from_cte() { - let dialect = &GenericDialect::default(); + let dialect = &GenericDialect; let mut test = SpanTest::new( dialect, "WITH cte AS (SELECT a FROM postgres.public.source) SELECT cte.* FROM cte", diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d50cf9f35..da716a326 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1550,7 +1550,7 @@ impl<'a> Parser<'a> { pub fn parse_case_expr(&mut self) -> Result { let mut operand = None; - if !self.parse_keyword(Keyword::WHEN).is_some() { + if self.parse_keyword(Keyword::WHEN).is_none() { operand = Some(Box::new(self.parse_expr()?)); self.expect_keyword(Keyword::WHEN)?; } @@ -1560,7 +1560,7 @@ impl<'a> Parser<'a> { conditions.push(self.parse_expr()?); self.expect_keyword(Keyword::THEN)?; results.push(self.parse_expr()?); - if !self.parse_keyword(Keyword::WHEN).is_some() { + if self.parse_keyword(Keyword::WHEN).is_none() { break; } } @@ -1906,7 +1906,7 @@ impl<'a> Parser<'a> { )?, }; let with_count = self.parse_keyword(Keyword::WITH).is_some(); - if !with_count && !self.parse_keyword(Keyword::WITHOUT).is_some() { + if !with_count && self.parse_keyword(Keyword::WITHOUT).is_none() { self.expected("either WITH or WITHOUT in LISTAGG", self.peek_token())?; } self.expect_keyword(Keyword::COUNT)?; @@ -3250,7 +3250,7 @@ impl<'a> Parser<'a> { pub fn parse_keywords(&mut self, keywords: &[Keyword]) -> bool { let index = self.index; for &keyword in keywords { - if !self.parse_keyword(keyword).is_some() { + if self.parse_keyword(keyword).is_none() { // println!("parse_keywords aborting .. did not find {:?}", keyword); // reset index and return immediately self.index = index; @@ -3450,7 +3450,7 @@ impl<'a> Parser<'a> { let mut values = vec![]; loop { values.push(f(self)?); - if !self.parse_keyword(keyword).is_some() { + if self.parse_keyword(keyword).is_none() { break; } } @@ -3835,7 +3835,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_create_function_using( &mut self, ) -> Result, ParserError> { - if !self.parse_keyword(Keyword::USING).is_some() { + if self.parse_keyword(Keyword::USING).is_none() { return Ok(None); }; let keyword = @@ -5325,7 +5325,7 @@ impl<'a> Parser<'a> { pub fn parse_create_index(&mut self, unique: bool) -> Result { let concurrently = self.parse_keyword(Keyword::CONCURRENTLY).is_some(); let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let index_name = if if_not_exists || !self.parse_keyword(Keyword::ON).is_some() { + let index_name = if if_not_exists || self.parse_keyword(Keyword::ON).is_none() { let index_name = self.parse_object_name(false)?; self.expect_keyword(Keyword::ON)?; Some(index_name) @@ -7940,7 +7940,7 @@ impl<'a> Parser<'a> { let mut modifiers = vec![]; if dialect_of!(self is ClickHouseDialect | GenericDialect) { loop { - if !self.parse_keyword(Keyword::WITH).is_some() { + if self.parse_keyword(Keyword::WITH).is_none() { break; } let keyword = self.expect_one_of_keywords(&[ @@ -8402,7 +8402,7 @@ impl<'a> Parser<'a> { } pub fn parse_delete(&mut self) -> Result { - let (tables, with_from_keyword) = if !self.parse_keyword(Keyword::FROM).is_some() { + let (tables, with_from_keyword) = if self.parse_keyword(Keyword::FROM).is_none() { // `FROM` keyword is optional in BigQuery SQL. // https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#delete_statement if dialect_of!(self is BigQueryDialect | GenericDialect) { @@ -11223,7 +11223,7 @@ impl<'a> Parser<'a> { // Parse a set of comma seperated INTERPOLATE expressions (ClickHouse dialect) // that follow the INTERPOLATE keyword in an ORDER BY clause with the WITH FILL modifier pub fn parse_interpolations(&mut self) -> Result, ParserError> { - if !self.parse_keyword(Keyword::INTERPOLATE).is_some() { + if self.parse_keyword(Keyword::INTERPOLATE).is_none() { return Ok(None); } @@ -11463,7 +11463,7 @@ impl<'a> Parser<'a> { pub fn parse_commit_rollback_chain(&mut self) -> Result { let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]); if self.parse_keyword(Keyword::AND).is_some() { - let chain = !self.parse_keyword(Keyword::NO).is_some(); + let chain = self.parse_keyword(Keyword::NO).is_none(); self.expect_keyword(Keyword::CHAIN)?; Ok(chain) } else { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index e9ada1fcd..a7568176e 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -487,8 +487,8 @@ impl Span { // If either span is empty, return the other // this prevents propagating (0, 0) through the tree match (self, other) { - (&Span::EMPTY, _) => other.clone(), - (_, &Span::EMPTY) => self.clone(), + (&Span::EMPTY, _) => *other, + (_, &Span::EMPTY) => *self, _ => Span { start: cmp::min(self.start, other.start), end: cmp::max(self.end, other.end), @@ -498,8 +498,8 @@ impl Span { pub fn union_opt(&self, other: &Option) -> Span { match other { - Some(other) => self.union(&other), - None => self.clone(), + Some(other) => self.union(other), + None => *self, } } } From ea8a6b1abf2ea5861c21bee413c12c84ccf2b2e8 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 8 Oct 2024 12:38:25 +0200 Subject: [PATCH 12/32] fix unused variable warnings --- src/ast/spans.rs | 184 ++++++++++++++++++++++------------------------- 1 file changed, 85 insertions(+), 99 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index ee8cd2122..b0df0ac15 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -69,14 +69,14 @@ impl Spanned for SetExpr { SetExpr::Select(select) => select.span(), SetExpr::Query(query) => query.span(), SetExpr::SetOperation { - op, - set_quantifier, + op: _, + set_quantifier: _, left, right, } => left.span().union(&right.span()), SetExpr::Values(values) => values.span(), SetExpr::Insert(statement) => statement.span(), - SetExpr::Table(table) => Span::empty(), + SetExpr::Table(_) => Span::empty(), SetExpr::Update(statement) => statement.span(), } } @@ -266,7 +266,7 @@ impl Spanned for Statement { Statement::CreateTable(create_table) => create_table.span(), Statement::CreateVirtualTable { name, - if_not_exists, + if_not_exists: _, module_name, module_args, } => union_spans( @@ -279,10 +279,10 @@ impl Spanned for Statement { Statement::CreateSecret { .. } => Span::empty(), Statement::AlterTable { name, - if_exists, - only, + if_exists: _, + only: _, operations, - location, + location: _, on_cluster, } => union_spans( core::iter::once(name.span()) @@ -331,8 +331,8 @@ impl Spanned for Statement { Statement::StartTransaction { .. } => Span::empty(), Statement::SetTransaction { .. } => Span::empty(), Statement::Comment { .. } => Span::empty(), - Statement::Commit { chain } => Span::empty(), - Statement::Rollback { chain, savepoint } => Span::empty(), + Statement::Commit { .. } => Span::empty(), + Statement::Rollback { .. } => Span::empty(), Statement::CreateSchema { .. } => Span::empty(), Statement::CreateDatabase { .. } => Span::empty(), Statement::CreateFunction { .. } => Span::empty(), @@ -341,26 +341,26 @@ impl Spanned for Statement { Statement::CreateProcedure { .. } => Span::empty(), Statement::CreateMacro { .. } => Span::empty(), Statement::CreateStage { .. } => Span::empty(), - Statement::Assert { condition, message } => Span::empty(), + Statement::Assert { .. } => Span::empty(), Statement::Grant { .. } => Span::empty(), Statement::Revoke { .. } => Span::empty(), - Statement::Deallocate { name, prepare } => Span::empty(), + Statement::Deallocate { .. } => Span::empty(), Statement::Execute { .. } => Span::empty(), Statement::Prepare { .. } => Span::empty(), - Statement::Kill { modifier, id } => Span::empty(), + Statement::Kill { .. } => Span::empty(), Statement::ExplainTable { .. } => Span::empty(), Statement::Explain { .. } => Span::empty(), - Statement::Savepoint { name } => Span::empty(), - Statement::ReleaseSavepoint { name } => Span::empty(), + Statement::Savepoint { .. } => Span::empty(), + Statement::ReleaseSavepoint { .. } => Span::empty(), Statement::Merge { .. } => Span::empty(), Statement::Cache { .. } => Span::empty(), Statement::UNCache { .. } => Span::empty(), Statement::CreateSequence { .. } => Span::empty(), Statement::CreateType { .. } => Span::empty(), - Statement::Pragma { name, value, is_eq } => Span::empty(), - Statement::LockTables { tables } => Span::empty(), + Statement::Pragma { .. } => Span::empty(), + Statement::LockTables { .. } => Span::empty(), Statement::UnlockTables => Span::empty(), - Statement::Unload { query, to, with } => Span::empty(), + Statement::Unload { .. } => Span::empty(), Statement::OptimizeTable { .. } => Span::empty(), } } @@ -466,9 +466,9 @@ impl Spanned for TableConstraint { expr.span().union_opt(&name.as_ref().map(|i| i.span)) } TableConstraint::Index { - display_as_key, + display_as_key: _, name, - index_type, + index_type: _, columns, } => union_spans( name.iter() @@ -476,8 +476,8 @@ impl Spanned for TableConstraint { .chain(columns.iter().map(|i| i.span)), ), TableConstraint::FulltextOrSpatial { - fulltext, - index_type_display, + fulltext: _, + index_type_display: _, opt_index_name, columns, } => union_spans( @@ -700,24 +700,24 @@ impl Spanned for AlterTableOperation { match self { AlterTableOperation::AddConstraint(table_constraint) => table_constraint.span(), AlterTableOperation::AddColumn { - column_keyword, - if_not_exists, + column_keyword: _, + if_not_exists: _, column_def, - column_position, + column_position: _, } => column_def.span(), AlterTableOperation::AddProjection { - if_not_exists, + if_not_exists: _, name, select, } => name.span.union(&select.span()), - AlterTableOperation::DropProjection { if_exists, name } => name.span, + AlterTableOperation::DropProjection { if_exists: _, name } => name.span, AlterTableOperation::MaterializeProjection { - if_exists, + if_exists: _, name, partition, } => name.span.union_opt(&partition.as_ref().map(|i| i.span)), AlterTableOperation::ClearProjection { - if_exists, + if_exists: _, name, partition, } => name.span.union_opt(&partition.as_ref().map(|i| i.span)), @@ -725,14 +725,14 @@ impl Spanned for AlterTableOperation { AlterTableOperation::DisableRule { name } => name.span, AlterTableOperation::DisableTrigger { name } => name.span, AlterTableOperation::DropConstraint { - if_exists, + if_exists: _, name, - cascade, + cascade: _, } => name.span, AlterTableOperation::DropColumn { column_name, - if_exists, - cascade, + if_exists: _, + cascade: _, } => column_name.span, AlterTableOperation::AttachPartition { partition } => partition.span(), AlterTableOperation::DetachPartition { partition } => partition.span(), @@ -766,12 +766,12 @@ impl Spanned for AlterTableOperation { .chain(new_partitions.iter().map(|i| i.span())), ), AlterTableOperation::AddPartitions { - if_not_exists, + if_not_exists: _, new_partitions, } => union_spans(new_partitions.iter().map(|i| i.span())), AlterTableOperation::DropPartitions { partitions, - if_exists, + if_exists: _, } => union_spans(partitions.iter().map(|i| i.span())), AlterTableOperation::RenameColumn { old_column_name, @@ -781,9 +781,9 @@ impl Spanned for AlterTableOperation { AlterTableOperation::ChangeColumn { old_name, new_name, - data_type, + data_type: _, options, - column_position, + column_position: _, } => union_spans( core::iter::once(old_name.span) .chain(core::iter::once(new_name.span)) @@ -791,9 +791,9 @@ impl Spanned for AlterTableOperation { ), AlterTableOperation::ModifyColumn { col_name, - data_type, + data_type: _, options, - column_position, + column_position: _, } => { union_spans(core::iter::once(col_name.span).chain(options.iter().map(|i| i.span()))) } @@ -807,7 +807,7 @@ impl Spanned for AlterTableOperation { AlterTableOperation::SetTblProperties { table_properties } => { union_spans(table_properties.iter().map(|i| i.span())) } - AlterTableOperation::OwnerTo { new_owner } => Span::empty(), + AlterTableOperation::OwnerTo { .. } => Span::empty(), } } } @@ -853,7 +853,7 @@ impl Spanned for OrderBy { impl Spanned for GroupByExpr { fn span(&self) -> Span { match self { - GroupByExpr::All(vec) => Span::empty(), + GroupByExpr::All(_) => Span::empty(), GroupByExpr::Expressions(exprs, _modifiers) => { union_spans(exprs.iter().map(|i| i.span())) } @@ -1008,48 +1008,48 @@ impl Spanned for Expr { Expr::InList { expr, list, - negated, + negated: _, } => union_spans( core::iter::once(expr.span()).chain(list.iter().map(|item| item.span())), ), Expr::InSubquery { expr, subquery, - negated, + negated: _, } => expr.span().union(&subquery.span()), Expr::InUnnest { expr, array_expr, - negated, + negated: _, } => expr.span().union(&array_expr.span()), Expr::Between { expr, - negated, + negated: _, low, high, } => expr.span().union(&low.span()).union(&high.span()), - Expr::BinaryOp { left, op, right } => left.span().union(&right.span()), + Expr::BinaryOp { left, op: _, right } => left.span().union(&right.span()), Expr::Like { - negated, + negated: _, expr, pattern, - escape_char, + escape_char: _, } => expr.span().union(&pattern.span()), Expr::ILike { - negated, + negated: _, expr, pattern, - escape_char, + escape_char: _, } => expr.span().union(&pattern.span()), Expr::SimilarTo { - negated, + negated: _, expr, pattern, - escape_char, + escape_char: _, } => expr.span().union(&pattern.span()), - Expr::Ceil { expr, field } => expr.span(), - Expr::Floor { expr, field } => expr.span(), + Expr::Ceil { expr, field: _ } => expr.span(), + Expr::Floor { expr, field: _ } => expr.span(), Expr::Position { expr, r#in } => expr.span().union(&r#in.span()), Expr::Overlay { expr, @@ -1066,7 +1066,7 @@ impl Spanned for Expr { .union(&union_spans(collation.0.iter().map(|i| i.span))), Expr::Nested(expr) => expr.span(), Expr::Value(value) => value.span(), - Expr::TypedString { data_type, value } => Span::empty(), + Expr::TypedString { .. } => Span::empty(), Expr::MapAccess { column, keys } => column .span() .union(&union_spans(keys.iter().map(|i| i.key.span()))), @@ -1078,34 +1078,25 @@ impl Spanned for Expr { Expr::Rollup(vec) => union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))), Expr::Tuple(vec) => union_spans(vec.iter().map(|i| i.span())), Expr::Array(array) => array.span(), - Expr::MatchAgainst { - columns, - match_value, - opt_search_modifier, - } => Span::empty(), + Expr::MatchAgainst { .. } => Span::empty(), Expr::JsonAccess { value, path } => value.span().union(&path.span()), - Expr::RLike { - negated, - expr, - pattern, - regexp, - } => Span::empty(), + Expr::RLike { .. } => Span::empty(), Expr::AnyOp { left, - compare_op, + compare_op: _, right, } => left.span().union(&right.span()), Expr::AllOp { left, - compare_op, + compare_op: _, right, } => left.span().union(&right.span()), - Expr::UnaryOp { op, expr } => expr.span(), + Expr::UnaryOp { op: _, expr } => expr.span(), Expr::Convert { expr, - data_type, + data_type: _, charset, - target_before_value, + target_before_value: _, styles, } => union_spans( core::iter::once(expr.span()) @@ -1113,25 +1104,25 @@ impl Spanned for Expr { .chain(styles.iter().map(|i| i.span())), ), Expr::Cast { - kind, + kind: _, expr, - data_type, - format, + data_type: _, + format: _, } => expr.span(), Expr::AtTimeZone { timestamp, time_zone, } => timestamp.span().union(&time_zone.span()), Expr::Extract { - field, - syntax, + field: _, + syntax: _, expr, } => expr.span(), Expr::Substring { expr, substring_from, substring_for, - special, + special: _, } => union_spans( core::iter::once(expr.span()) .chain(substring_from.as_ref().map(|i| i.span())) @@ -1139,7 +1130,7 @@ impl Spanned for Expr { ), Expr::Trim { expr, - trim_where, + trim_where: _, trim_what, trim_characters, } => union_spans( @@ -1263,11 +1254,11 @@ impl Spanned for FunctionArgumentList { impl Spanned for FunctionArgumentClause { fn span(&self) -> Span { match self { - FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment) => Span::empty(), + FunctionArgumentClause::IgnoreOrRespectNulls(_) => Span::empty(), FunctionArgumentClause::OrderBy(vec) => union_spans(vec.iter().map(|i| i.expr.span())), FunctionArgumentClause::Limit(expr) => expr.span(), - FunctionArgumentClause::OnOverflow(list_agg_on_overflow) => Span::empty(), - FunctionArgumentClause::Having(HavingBound(kind, expr)) => expr.span(), + FunctionArgumentClause::OnOverflow(_) => Span::empty(), + FunctionArgumentClause::Having(HavingBound(_kind, expr)) => expr.span(), FunctionArgumentClause::Separator(value) => value.span(), } } @@ -1289,7 +1280,7 @@ impl Spanned for JsonPath { impl Spanned for JsonPathElem { fn span(&self) -> Span { match self { - JsonPathElem::Dot { key, quoted } => Span::empty(), + JsonPathElem::Dot { .. } => Span::empty(), JsonPathElem::Bracket { key } => key.span(), } } @@ -1383,11 +1374,11 @@ impl Spanned for TableFactor { TableFactor::Table { name, alias, - args, - with_hints, - version, - with_ordinality, - partitions, + args: _, + with_hints: _, + version: _, + with_ordinality: _, + partitions: _, } => union_spans( name.0 .iter() @@ -1399,7 +1390,7 @@ impl Spanned for TableFactor { })), ), TableFactor::Derived { - lateral, + lateral: _, subquery, alias, } => subquery @@ -1410,10 +1401,10 @@ impl Spanned for TableFactor { .union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::UNNEST { alias, - with_offset, + with_offset: _, with_offset_alias, array_exprs, - with_ordinality, + with_ordinality: _, } => union_spans( alias .iter() @@ -1428,7 +1419,7 @@ impl Spanned for TableFactor { .span() .union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::Function { - lateral, + lateral: _, name, args, alias, @@ -1439,12 +1430,7 @@ impl Spanned for TableFactor { .chain(args.iter().map(|i| i.span())) .chain(alias.as_ref().map(|alias| alias.span())), ), - TableFactor::JsonTable { - json_expr, - json_path, - columns, - alias, - } => Span::empty(), + TableFactor::JsonTable { .. } => Span::empty(), TableFactor::Pivot { table, aggregate_functions, @@ -1478,8 +1464,8 @@ impl Spanned for TableFactor { partition_by, order_by, measures, - rows_per_match, - after_match_skip, + rows_per_match: _, + after_match_skip: _, pattern, symbols, alias, @@ -1559,7 +1545,7 @@ impl Spanned for FunctionArg { FunctionArg::Named { name, arg, - operator, + operator: _, } => name.span.union(&arg.span()), FunctionArg::Unnamed(arg) => arg.span(), } From 6a9250aee884f6bf96a196e58e396c5f0fcc4223 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 8 Oct 2024 12:50:09 +0200 Subject: [PATCH 13/32] undo parse_keyword signature change --- src/dialect/mysql.rs | 8 +- src/dialect/postgresql.rs | 4 +- src/dialect/snowflake.rs | 44 +- src/dialect/sqlite.rs | 2 +- src/parser/alter.rs | 18 +- src/parser/mod.rs | 1017 ++++++++++++++--------------- tests/sqlparser_custom_dialect.rs | 2 +- 7 files changed, 544 insertions(+), 551 deletions(-) diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index e171733ca..b8c4631fd 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -64,7 +64,7 @@ impl Dialect for MySqlDialect { _precedence: u8, ) -> Option> { // Parse DIV as an operator - if parser.parse_keyword(Keyword::DIV).is_some() { + if parser.parse_keyword(Keyword::DIV) { Some(Ok(Expr::BinaryOp { left: Box::new(expr.clone()), op: BinaryOperator::MyIntegerDivide, @@ -113,13 +113,13 @@ fn parse_lock_table(parser: &mut Parser) -> Result { // READ [LOCAL] | [LOW_PRIORITY] WRITE fn parse_lock_tables_type(parser: &mut Parser) -> Result { - if parser.parse_keyword(Keyword::READ).is_some() { - if parser.parse_keyword(Keyword::LOCAL).is_some() { + if parser.parse_keyword(Keyword::READ) { + if parser.parse_keyword(Keyword::LOCAL) { Ok(LockTableType::Read { local: true }) } else { Ok(LockTableType::Read { local: false }) } - } else if parser.parse_keyword(Keyword::WRITE).is_some() { + } else if parser.parse_keyword(Keyword::WRITE) { Ok(LockTableType::Write { low_priority: false, }) diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index 745d823f7..eba3a6989 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -119,7 +119,7 @@ impl Dialect for PostgreSqlDialect { } fn parse_statement(&self, parser: &mut Parser) -> Option> { - if parser.parse_keyword(Keyword::COMMENT).is_some() { + if parser.parse_keyword(Keyword::COMMENT) { Some(parse_comment(parser)) } else { None @@ -187,7 +187,7 @@ pub fn parse_comment(parser: &mut Parser) -> Result { }; parser.expect_keyword(Keyword::IS)?; - let comment = if parser.parse_keyword(Keyword::NULL).is_some() { + let comment = if parser.parse_keyword(Keyword::NULL) { None } else { Some(parser.parse_literal_string()?) diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 9d1c1ae69..4f37004b1 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -90,7 +90,7 @@ impl Dialect for SnowflakeDialect { } fn parse_statement(&self, parser: &mut Parser) -> Option> { - if parser.parse_keyword(Keyword::CREATE).is_some() { + if parser.parse_keyword(Keyword::CREATE) { // possibly CREATE STAGE //[ OR REPLACE ] let or_replace = parser.parse_keywords(&[Keyword::OR, Keyword::REPLACE]); @@ -117,10 +117,10 @@ impl Dialect for SnowflakeDialect { _ => {} } - if parser.parse_keyword(Keyword::STAGE).is_some() { + if parser.parse_keyword(Keyword::STAGE) { // OK - this is CREATE STAGE statement return Some(parse_create_stage(or_replace, temporary, parser)); - } else if parser.parse_keyword(Keyword::TABLE).is_some() { + } else if parser.parse_keyword(Keyword::TABLE) { return Some(parse_create_table( or_replace, global, temporary, volatile, transient, parser, )); @@ -375,25 +375,25 @@ pub fn parse_create_stage( let stage_params = parse_stage_params(parser)?; // [ directoryTableParams ] - if parser.parse_keyword(Keyword::DIRECTORY).is_some() { + if parser.parse_keyword(Keyword::DIRECTORY) { parser.expect_token(&Token::Eq)?; directory_table_params = parse_parentheses_options(parser)?; } // [ file_format] - if parser.parse_keyword(Keyword::FILE_FORMAT).is_some() { + if parser.parse_keyword(Keyword::FILE_FORMAT) { parser.expect_token(&Token::Eq)?; file_format = parse_parentheses_options(parser)?; } // [ copy_options ] - if parser.parse_keyword(Keyword::COPY_OPTIONS).is_some() { + if parser.parse_keyword(Keyword::COPY_OPTIONS) { parser.expect_token(&Token::Eq)?; copy_options = parse_parentheses_options(parser)?; } // [ comment ] - if parser.parse_keyword(Keyword::COMMENT).is_some() { + if parser.parse_keyword(Keyword::COMMENT) { parser.expect_token(&Token::Eq)?; comment = Some(match parser.next_token().token { Token::SingleQuotedString(word) => Ok(word), @@ -485,7 +485,7 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result { stage_params = parse_stage_params(parser)?; // as - from_stage_alias = if parser.parse_keyword(Keyword::AS).is_some() { + from_stage_alias = if parser.parse_keyword(Keyword::AS) { Some(match parser.next_token().token { Token::Word(w) => Ok(Ident::new(w.value)), _ => parser.expected("stage alias", parser.peek_token()), @@ -501,7 +501,7 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result { stage_params = parse_stage_params(parser)?; // as - from_stage_alias = if parser.parse_keyword(Keyword::AS).is_some() { + from_stage_alias = if parser.parse_keyword(Keyword::AS) { Some(match parser.next_token().token { Token::Word(w) => Ok(Ident::new(w.value)), _ => parser.expected("stage alias", parser.peek_token()), @@ -513,7 +513,7 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result { }; // [ files ] - if parser.parse_keyword(Keyword::FILES).is_some() { + if parser.parse_keyword(Keyword::FILES) { parser.expect_token(&Token::Eq)?; parser.expect_token(&Token::LParen)?; let mut continue_loop = true; @@ -535,7 +535,7 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result { // [ pattern ] let mut pattern = None; - if parser.parse_keyword(Keyword::PATTERN).is_some() { + if parser.parse_keyword(Keyword::PATTERN) { parser.expect_token(&Token::Eq)?; let next_token = parser.next_token(); pattern = Some(match next_token.token { @@ -546,21 +546,21 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result { // [ file_format] let mut file_format = Vec::new(); - if parser.parse_keyword(Keyword::FILE_FORMAT).is_some() { + if parser.parse_keyword(Keyword::FILE_FORMAT) { parser.expect_token(&Token::Eq)?; file_format = parse_parentheses_options(parser)?; } // [ copy_options ] let mut copy_options = Vec::new(); - if parser.parse_keyword(Keyword::COPY_OPTIONS).is_some() { + if parser.parse_keyword(Keyword::COPY_OPTIONS) { parser.expect_token(&Token::Eq)?; copy_options = parse_parentheses_options(parser)?; } // [ VALIDATION_MODE ] let mut validation_mode = None; - if parser.parse_keyword(Keyword::VALIDATION_MODE).is_some() { + if parser.parse_keyword(Keyword::VALIDATION_MODE) { parser.expect_token(&Token::Eq)?; validation_mode = Some(parser.next_token().token.to_string()); } @@ -640,7 +640,7 @@ fn parse_select_items_for_data_load( } // as - if parser.parse_keyword(Keyword::AS).is_some() { + if parser.parse_keyword(Keyword::AS) { item_as = Some(match parser.next_token().token { Token::Word(w) => Ok(Ident::new(w.value)), _ => parser.expected("column item alias", parser.peek_token()), @@ -673,7 +673,7 @@ fn parse_stage_params(parser: &mut Parser) -> Result Ok(word), @@ -682,13 +682,13 @@ fn parse_stage_params(parser: &mut Parser) -> Result Ok(word), @@ -697,7 +697,7 @@ fn parse_stage_params(parser: &mut Parser) -> Result Result Result break, Token::Word(key) => { parser.expect_token(&Token::Eq)?; - if parser.parse_keyword(Keyword::TRUE).is_some() { + if parser.parse_keyword(Keyword::TRUE) { options.push(DataLoadingOption { option_name: key.value, option_type: DataLoadingOptionType::BOOLEAN, value: "TRUE".to_string(), }); Ok(()) - } else if parser.parse_keyword(Keyword::FALSE).is_some() { + } else if parser.parse_keyword(Keyword::FALSE) { options.push(DataLoadingOption { option_name: key.value, option_type: DataLoadingOptionType::BOOLEAN, diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs index 87f12262a..cc08d7961 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -57,7 +57,7 @@ impl Dialect for SQLiteDialect { } fn parse_statement(&self, parser: &mut Parser) -> Option> { - if parser.parse_keyword(Keyword::REPLACE).is_some() { + if parser.parse_keyword(Keyword::REPLACE) { parser.prev_token(); Some(parser.parse_insert()) } else { diff --git a/src/parser/alter.rs b/src/parser/alter.rs index c6cbf5684..7bf99af66 100644 --- a/src/parser/alter.rs +++ b/src/parser/alter.rs @@ -72,15 +72,15 @@ impl<'a> Parser<'a> { None }; - let operation = if self.parse_keyword(Keyword::RENAME).is_some() { - if self.parse_keyword(Keyword::TO).is_some() { + let operation = if self.parse_keyword(Keyword::RENAME) { + if self.parse_keyword(Keyword::TO) { let role_name = self.parse_identifier(false)?; AlterRoleOperation::RenameRole { role_name } } else { return self.expected("TO after RENAME", self.peek_token()); } // SET - } else if self.parse_keyword(Keyword::SET).is_some() { + } else if self.parse_keyword(Keyword::SET) { let config_name = self.parse_object_name(false)?; // FROM CURRENT if self.parse_keywords(&[Keyword::FROM, Keyword::CURRENT]) { @@ -90,8 +90,8 @@ impl<'a> Parser<'a> { in_database, } // { TO | = } { value | DEFAULT } - } else if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO).is_some() { - if self.parse_keyword(Keyword::DEFAULT).is_some() { + } else if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { + if self.parse_keyword(Keyword::DEFAULT) { AlterRoleOperation::Set { config_name, config_value: SetConfigValue::Default, @@ -110,8 +110,8 @@ impl<'a> Parser<'a> { self.expected("'TO' or '=' or 'FROM CURRENT'", self.peek_token())? } // RESET - } else if self.parse_keyword(Keyword::RESET).is_some() { - if self.parse_keyword(Keyword::ALL).is_some() { + } else if self.parse_keyword(Keyword::RESET) { + if self.parse_keyword(Keyword::ALL) { AlterRoleOperation::Reset { config_name: ResetConfig::ALL, in_database, @@ -126,7 +126,7 @@ impl<'a> Parser<'a> { // option } else { // [ WITH ] - let _ = self.parse_keyword(Keyword::WITH).is_some(); + let _ = self.parse_keyword(Keyword::WITH); // option let mut options = vec![]; while let Some(opt) = self.maybe_parse(|parser| parser.parse_pg_role_option()) { @@ -181,7 +181,7 @@ impl<'a> Parser<'a> { Some(Keyword::LOGIN) => RoleOption::Login(true), Some(Keyword::NOLOGIN) => RoleOption::Login(false), Some(Keyword::PASSWORD) => { - let password = if self.parse_keyword(Keyword::NULL).is_some() { + let password = if self.parse_keyword(Keyword::NULL) { Password::NullPassword } else { Password::Password(Expr::Value(self.parse_value()?)) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index da716a326..e9ecfa5c0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -566,9 +566,9 @@ impl<'a> Parser<'a> { return parser_err!("Unsupported statement FLUSH", self.peek_token().span.start); } - let location = if self.parse_keyword(Keyword::NO_WRITE_TO_BINLOG).is_some() { + let location = if self.parse_keyword(Keyword::NO_WRITE_TO_BINLOG) { Some(FlushLocation::NoWriteToBinlog) - } else if self.parse_keyword(Keyword::LOCAL).is_some() { + } else if self.parse_keyword(Keyword::LOCAL) { Some(FlushLocation::Local) } else { None @@ -584,9 +584,9 @@ impl<'a> Parser<'a> { FlushType::GeneralLogs } else if self.parse_keywords(&[Keyword::HOSTS]) { FlushType::Hosts - } else if self.parse_keyword(Keyword::PRIVILEGES).is_some() { + } else if self.parse_keyword(Keyword::PRIVILEGES) { FlushType::Privileges - } else if self.parse_keyword(Keyword::OPTIMIZER_COSTS).is_some() { + } else if self.parse_keyword(Keyword::OPTIMIZER_COSTS) { FlushType::OptimizerCosts } else if self.parse_keywords(&[Keyword::RELAY, Keyword::LOGS]) { if self.parse_keywords(&[Keyword::FOR, Keyword::CHANNEL]) { @@ -595,9 +595,9 @@ impl<'a> Parser<'a> { FlushType::RelayLogs } else if self.parse_keywords(&[Keyword::SLOW, Keyword::LOGS]) { FlushType::SlowLogs - } else if self.parse_keyword(Keyword::STATUS).is_some() { + } else if self.parse_keyword(Keyword::STATUS) { FlushType::Status - } else if self.parse_keyword(Keyword::USER_RESOURCES).is_some() { + } else if self.parse_keyword(Keyword::USER_RESOURCES) { FlushType::UserResources } else if self.parse_keywords(&[Keyword::LOGS]) { FlushType::Logs @@ -610,7 +610,7 @@ impl<'a> Parser<'a> { read_lock = self.parse_keywords(&[Keyword::READ, Keyword::LOCK]); } Keyword::FOR => { - export = self.parse_keyword(Keyword::EXPORT).is_some(); + export = self.parse_keyword(Keyword::EXPORT); } Keyword::NoKeyword => { self.prev_token(); @@ -644,7 +644,7 @@ impl<'a> Parser<'a> { } pub fn parse_msck(&mut self) -> Result { - let repair = self.parse_keyword(Keyword::REPAIR).is_some(); + let repair = self.parse_keyword(Keyword::REPAIR); self.expect_keyword(Keyword::TABLE)?; let table_name = self.parse_object_name(false)?; let partition_action = self @@ -671,8 +671,8 @@ impl<'a> Parser<'a> { } pub fn parse_truncate(&mut self) -> Result { - let table = self.parse_keyword(Keyword::TABLE).is_some(); - let only = self.parse_keyword(Keyword::ONLY).is_some(); + let table = self.parse_keyword(Keyword::TABLE); + let only = self.parse_keyword(Keyword::ONLY); let table_names = self .parse_comma_separated(|p| p.parse_object_name(false))? @@ -681,7 +681,7 @@ impl<'a> Parser<'a> { .collect(); let mut partitions = None; - if self.parse_keyword(Keyword::PARTITION).is_some() { + if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; partitions = Some(self.parse_comma_separated(Parser::parse_expr)?); self.expect_token(&Token::RParen)?; @@ -699,9 +699,9 @@ impl<'a> Parser<'a> { None }; - cascade = if self.parse_keyword(Keyword::CASCADE).is_some() { + cascade = if self.parse_keyword(Keyword::CASCADE) { Some(TruncateCascadeOption::Cascade) - } else if self.parse_keyword(Keyword::RESTRICT).is_some() { + } else if self.parse_keyword(Keyword::RESTRICT) { Some(TruncateCascadeOption::Restrict) } else { None @@ -727,16 +727,16 @@ impl<'a> Parser<'a> { let mut options = vec![]; loop { - if self.parse_keyword(Keyword::READ_ONLY).is_some() { - let boolean = if self.parse_keyword(Keyword::TRUE).is_some() { + if self.parse_keyword(Keyword::READ_ONLY) { + let boolean = if self.parse_keyword(Keyword::TRUE) { Some(true) - } else if self.parse_keyword(Keyword::FALSE).is_some() { + } else if self.parse_keyword(Keyword::FALSE) { Some(false) } else { None }; options.push(AttachDuckDBDatabaseOption::ReadOnly(boolean)); - } else if self.parse_keyword(Keyword::TYPE).is_some() { + } else if self.parse_keyword(Keyword::TYPE) { let ident = self.parse_identifier(false)?; options.push(AttachDuckDBDatabaseOption::Type(ident)); } else { @@ -754,10 +754,10 @@ impl<'a> Parser<'a> { } pub fn parse_attach_duckdb_database(&mut self) -> Result { - let database = self.parse_keyword(Keyword::DATABASE).is_some(); + let database = self.parse_keyword(Keyword::DATABASE); let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let database_path = self.parse_identifier(false)?; - let database_alias = if self.parse_keyword(Keyword::AS).is_some() { + let database_alias = if self.parse_keyword(Keyword::AS) { Some(self.parse_identifier(false)?) } else { None @@ -774,7 +774,7 @@ impl<'a> Parser<'a> { } pub fn parse_detach_duckdb_database(&mut self) -> Result { - let database = self.parse_keyword(Keyword::DATABASE).is_some(); + let database = self.parse_keyword(Keyword::DATABASE); let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let database_alias = self.parse_identifier(false)?; Ok(Statement::DetachDuckDBDatabase { @@ -785,7 +785,7 @@ impl<'a> Parser<'a> { } pub fn parse_attach_database(&mut self) -> Result { - let database = self.parse_keyword(Keyword::DATABASE).is_some(); + let database = self.parse_keyword(Keyword::DATABASE); let database_file_name = self.parse_expr()?; self.expect_keyword(Keyword::AS)?; let schema_name = self.parse_identifier(false)?; @@ -924,7 +924,7 @@ impl<'a> Parser<'a> { pub fn parse_assert(&mut self) -> Result { let condition = self.parse_expr()?; - let message = if self.parse_keyword(Keyword::AS).is_some() { + let message = if self.parse_keyword(Keyword::AS) { Some(self.parse_expr()?) } else { None @@ -939,7 +939,7 @@ impl<'a> Parser<'a> { } pub fn parse_release(&mut self) -> Result { - let _ = self.parse_keyword(Keyword::SAVEPOINT).is_some(); + let _ = self.parse_keyword(Keyword::SAVEPOINT); let name = self.parse_identifier(false)?; Ok(Statement::ReleaseSavepoint { name }) @@ -1267,7 +1267,7 @@ impl<'a> Parser<'a> { _ => self.expected("an expression:", next_token), }?; - if self.parse_keyword(Keyword::COLLATE).is_some() { + if self.parse_keyword(Keyword::COLLATE) { Ok(Expr::Collate { expr: Box::new(expr), collation: self.parse_object_name(false)?, @@ -1351,9 +1351,9 @@ impl<'a> Parser<'a> { }; let filter = if self.dialect.supports_filter_during_aggregation() - && self.parse_keyword(Keyword::FILTER).is_some() + && self.parse_keyword(Keyword::FILTER) && self.consume_token(&Token::LParen) - && self.parse_keyword(Keyword::WHERE).is_some() + && self.parse_keyword(Keyword::WHERE) { let filter = Some(Box::new(self.parse_expr()?)); self.expect_token(&Token::RParen)?; @@ -1374,7 +1374,7 @@ impl<'a> Parser<'a> { None }; - let over = if self.parse_keyword(Keyword::OVER).is_some() { + let over = if self.parse_keyword(Keyword::OVER) { if self.consume_token(&Token::LParen) { let window_spec = self.parse_window_spec()?; Some(WindowType::WindowSpec(window_spec)) @@ -1444,7 +1444,7 @@ impl<'a> Parser<'a> { pub fn parse_window_frame(&mut self) -> Result { let units = self.parse_window_frame_units()?; - let (start_bound, end_bound) = if self.parse_keyword(Keyword::BETWEEN).is_some() { + let (start_bound, end_bound) = if self.parse_keyword(Keyword::BETWEEN) { let start_bound = self.parse_window_frame_bound()?; self.expect_keyword(Keyword::AND)?; let end_bound = Some(self.parse_window_frame_bound()?); @@ -1464,7 +1464,7 @@ impl<'a> Parser<'a> { if self.parse_keywords(&[Keyword::CURRENT, Keyword::ROW]) { Ok(WindowFrameBound::CurrentRow) } else { - let rows = if self.parse_keyword(Keyword::UNBOUNDED).is_some() { + let rows = if self.parse_keyword(Keyword::UNBOUNDED) { None } else { Some(Box::new(match self.peek_token().token { @@ -1472,9 +1472,9 @@ impl<'a> Parser<'a> { _ => self.parse_expr()?, })) }; - if self.parse_keyword(Keyword::PRECEDING).is_some() { + if self.parse_keyword(Keyword::PRECEDING) { Ok(WindowFrameBound::Preceding(rows)) - } else if self.parse_keyword(Keyword::FOLLOWING).is_some() { + } else if self.parse_keyword(Keyword::FOLLOWING) { Ok(WindowFrameBound::Following(rows)) } else { self.expected("PRECEDING or FOLLOWING", self.peek_token()) @@ -1490,12 +1490,12 @@ impl<'a> Parser<'a> { let result = self.parse_comma_separated(|p| p.parse_tuple(false, true))?; self.expect_token(&Token::RParen)?; Ok(Expr::GroupingSets(result)) - } else if self.parse_keyword(Keyword::CUBE).is_some() { + } else if self.parse_keyword(Keyword::CUBE) { self.expect_token(&Token::LParen)?; let result = self.parse_comma_separated(|p| p.parse_tuple(true, true))?; self.expect_token(&Token::RParen)?; Ok(Expr::Cube(result)) - } else if self.parse_keyword(Keyword::ROLLUP).is_some() { + } else if self.parse_keyword(Keyword::ROLLUP) { self.expect_token(&Token::LParen)?; let result = self.parse_comma_separated(|p| p.parse_tuple(true, true))?; self.expect_token(&Token::RParen)?; @@ -1550,7 +1550,7 @@ impl<'a> Parser<'a> { pub fn parse_case_expr(&mut self) -> Result { let mut operand = None; - if self.parse_keyword(Keyword::WHEN).is_none() { + if !self.parse_keyword(Keyword::WHEN) { operand = Some(Box::new(self.parse_expr()?)); self.expect_keyword(Keyword::WHEN)?; } @@ -1560,11 +1560,11 @@ impl<'a> Parser<'a> { conditions.push(self.parse_expr()?); self.expect_keyword(Keyword::THEN)?; results.push(self.parse_expr()?); - if self.parse_keyword(Keyword::WHEN).is_none() { + if !self.parse_keyword(Keyword::WHEN) { break; } } - let else_result = if self.parse_keyword(Keyword::ELSE).is_some() { + let else_result = if self.parse_keyword(Keyword::ELSE) { Some(Box::new(self.parse_expr()?)) } else { None @@ -1579,7 +1579,7 @@ impl<'a> Parser<'a> { } pub fn parse_optional_cast_format(&mut self) -> Result, ParserError> { - if self.parse_keyword(Keyword::FORMAT).is_some() { + if self.parse_keyword(Keyword::FORMAT) { let value = self.parse_value()?; match self.parse_optional_time_zone()? { Some(tz) => Ok(Some(CastFormat::ValueAtTimeZone(value, tz))), @@ -1629,7 +1629,7 @@ impl<'a> Parser<'a> { } self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; - if self.parse_keyword(Keyword::USING).is_some() { + if self.parse_keyword(Keyword::USING) { let charset = self.parse_object_name(false)?; self.expect_token(&Token::RParen)?; return Ok(Expr::Convert { @@ -1688,7 +1688,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let field = self.parse_date_time_field()?; - let syntax = if self.parse_keyword(Keyword::FROM).is_some() { + let syntax = if self.parse_keyword(Keyword::FROM) { ExtractSyntax::From } else if self.consume_token(&Token::Comma) && dialect_of!(self is SnowflakeDialect | GenericDialect) @@ -1713,7 +1713,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; // Parse `CEIL/FLOOR(expr)` - let field = if self.parse_keyword(Keyword::TO).is_some() { + let field = if self.parse_keyword(Keyword::TO) { // Parse `CEIL/FLOOR(expr TO DateTimeField)` CeilFloorKind::DateTimeField(self.parse_date_time_field()?) } else if self.consume_token(&Token::Comma) { @@ -1773,12 +1773,12 @@ impl<'a> Parser<'a> { let expr = self.parse_expr()?; let mut from_expr = None; let special = self.consume_token(&Token::Comma); - if special || self.parse_keyword(Keyword::FROM).is_some() { + if special || self.parse_keyword(Keyword::FROM) { from_expr = Some(self.parse_expr()?); } let mut to_expr = None; - if self.parse_keyword(Keyword::FOR).is_some() || self.consume_token(&Token::Comma) { + if self.parse_keyword(Keyword::FOR) || self.consume_token(&Token::Comma) { to_expr = Some(self.parse_expr()?); } self.expect_token(&Token::RParen)?; @@ -1800,7 +1800,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::FROM)?; let from_expr = self.parse_expr()?; let mut for_expr = None; - if self.parse_keyword(Keyword::FOR).is_some() { + if self.parse_keyword(Keyword::FOR) { for_expr = Some(self.parse_expr()?); } self.expect_token(&Token::RParen)?; @@ -1830,7 +1830,7 @@ impl<'a> Parser<'a> { } } let expr = self.parse_expr()?; - if self.parse_keyword(Keyword::FROM).is_some() { + if self.parse_keyword(Keyword::FROM) { let trim_what = Box::new(expr); let expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; @@ -1885,7 +1885,7 @@ impl<'a> Parser<'a> { pub fn parse_listagg_on_overflow(&mut self) -> Result, ParserError> { if self.parse_keywords(&[Keyword::ON, Keyword::OVERFLOW]) { - if self.parse_keyword(Keyword::ERROR).is_some() { + if self.parse_keyword(Keyword::ERROR) { Ok(Some(ListAggOnOverflow::Error)) } else { self.expect_keyword(Keyword::TRUNCATE)?; @@ -1905,8 +1905,8 @@ impl<'a> Parser<'a> { self.peek_token(), )?, }; - let with_count = self.parse_keyword(Keyword::WITH).is_some(); - if !with_count && self.parse_keyword(Keyword::WITHOUT).is_none() { + let with_count = self.parse_keyword(Keyword::WITH); + if !with_count && !self.parse_keyword(Keyword::WITHOUT) { self.expected("either WITH or WITHOUT in LISTAGG", self.peek_token())?; } self.expect_keyword(Keyword::COUNT)?; @@ -1992,7 +1992,7 @@ impl<'a> Parser<'a> { Token::Word(w) => match w.keyword { Keyword::EXISTS => { let negated = true; - let _ = self.parse_keyword(Keyword::EXISTS).is_some(); + let _ = self.parse_keyword(Keyword::EXISTS); self.parse_exists_expr(negated) } _ => Ok(Expr::UnaryOp { @@ -2119,7 +2119,7 @@ impl<'a> Parser<'a> { (leading_precision, last_field, fsec_precision) } else { let leading_precision = self.parse_optional_precision()?; - if self.parse_keyword(Keyword::TO).is_some() { + if self.parse_keyword(Keyword::TO) { let last_field = Some(self.parse_date_time_field()?); let fsec_precision = if last_field == Some(DateTimeField::Second) { self.parse_optional_precision()? @@ -2221,7 +2221,7 @@ impl<'a> Parser<'a> { /// [3]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#typeless_struct_syntax fn parse_struct_field_expr(&mut self, typed_syntax: bool) -> Result { let expr = self.parse_expr()?; - if self.parse_keyword(Keyword::AS).is_some() { + if self.parse_keyword(Keyword::AS) { if typed_syntax { return parser_err!("Typed syntax does not allow AS", { self.prev_token(); @@ -2663,7 +2663,7 @@ impl<'a> Parser<'a> { } else if let Token::Word(w) = &tok.token { match w.keyword { Keyword::IS => { - if self.parse_keyword(Keyword::NULL).is_some() { + if self.parse_keyword(Keyword::NULL) { Ok(Expr::IsNull(Box::new(expr))) } else if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) { Ok(Expr::IsNotNull(Box::new(expr))) @@ -2709,9 +2709,9 @@ impl<'a> Parser<'a> { | Keyword::REGEXP | Keyword::RLIKE => { self.prev_token(); - let negated = self.parse_keyword(Keyword::NOT).is_some(); - let regexp = self.parse_keyword(Keyword::REGEXP).is_some(); - let rlike = self.parse_keyword(Keyword::RLIKE).is_some(); + let negated = self.parse_keyword(Keyword::NOT); + let regexp = self.parse_keyword(Keyword::REGEXP); + let rlike = self.parse_keyword(Keyword::RLIKE); if regexp || rlike { Ok(Expr::RLike { negated, @@ -2721,11 +2721,11 @@ impl<'a> Parser<'a> { ), regexp, }) - } else if self.parse_keyword(Keyword::IN).is_some() { + } else if self.parse_keyword(Keyword::IN) { self.parse_in(expr, negated) - } else if self.parse_keyword(Keyword::BETWEEN).is_some() { + } else if self.parse_keyword(Keyword::BETWEEN) { self.parse_between(expr, negated) - } else if self.parse_keyword(Keyword::LIKE).is_some() { + } else if self.parse_keyword(Keyword::LIKE) { Ok(Expr::Like { negated, expr: Box::new(expr), @@ -2734,7 +2734,7 @@ impl<'a> Parser<'a> { ), escape_char: self.parse_escape_char()?, }) - } else if self.parse_keyword(Keyword::ILIKE).is_some() { + } else if self.parse_keyword(Keyword::ILIKE) { Ok(Expr::ILike { negated, expr: Box::new(expr), @@ -2798,7 +2798,7 @@ impl<'a> Parser<'a> { /// Parse the `ESCAPE CHAR` portion of `LIKE`, `ILIKE`, and `SIMILAR TO` pub fn parse_escape_char(&mut self) -> Result, ParserError> { - if self.parse_keyword(Keyword::ESCAPE).is_some() { + if self.parse_keyword(Keyword::ESCAPE) { Ok(Some(self.parse_literal_string()?)) } else { Ok(None) @@ -2987,7 +2987,7 @@ impl<'a> Parser<'a> { pub fn parse_in(&mut self, expr: Expr, negated: bool) -> Result { // BigQuery allows `IN UNNEST(array_expression)` // https://cloud.google.com/bigquery/docs/reference/standard-sql/operators#in_operators - if self.parse_keyword(Keyword::UNNEST).is_some() { + if self.parse_keyword(Keyword::UNNEST) { self.expect_token(&Token::LParen)?; let array_expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; @@ -2998,9 +2998,7 @@ impl<'a> Parser<'a> { }); } self.expect_token(&Token::LParen)?; - let in_op = if self.parse_keyword(Keyword::SELECT).is_some() - || self.parse_keyword(Keyword::WITH).is_some() - { + let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) { self.prev_token(); Expr::InSubquery { expr: Box::new(expr), @@ -3203,7 +3201,12 @@ impl<'a> Parser<'a> { /// If the current token is the `expected` keyword, consume it and returns /// true. Otherwise, no tokens are consumed and returns false. #[must_use] - pub fn parse_keyword(&mut self, expected: Keyword) -> Option { + pub fn parse_keyword(&mut self, expected: Keyword) -> bool { + self.parse_keyword_token(expected).is_some() + } + + #[must_use] + pub fn parse_keyword_token(&mut self, expected: Keyword) -> Option { match self.peek_token().token { Token::Word(w) if expected == w.keyword => Some(self.next_token()), _ => None, @@ -3250,7 +3253,7 @@ impl<'a> Parser<'a> { pub fn parse_keywords(&mut self, keywords: &[Keyword]) -> bool { let index = self.index; for &keyword in keywords { - if self.parse_keyword(keyword).is_none() { + if !self.parse_keyword(keyword) { // println!("parse_keywords aborting .. did not find {:?}", keyword); // reset index and return immediately self.index = index; @@ -3296,7 +3299,7 @@ impl<'a> Parser<'a> { /// If the current token is the `expected` keyword, consume the token. /// Otherwise, return an error. pub fn expect_keyword(&mut self, expected: Keyword) -> Result { - if let Some(token) = self.parse_keyword(expected) { + if let Some(token) = self.parse_keyword_token(expected) { Ok(token) } else { self.expected(format!("{:?}", &expected).as_str(), self.peek_token()) @@ -3450,7 +3453,7 @@ impl<'a> Parser<'a> { let mut values = vec![]; loop { values.push(f(self)?); - if self.parse_keyword(keyword).is_none() { + if !self.parse_keyword(keyword) { break; } } @@ -3508,15 +3511,15 @@ impl<'a> Parser<'a> { /// and results in a [`ParserError`] if both `ALL` and `DISTINCT` are found. pub fn parse_all_or_distinct(&mut self) -> Result, ParserError> { let loc = self.peek_token().span.start; - let all = self.parse_keyword(Keyword::ALL).is_some(); - let distinct = self.parse_keyword(Keyword::DISTINCT).is_some(); + let all = self.parse_keyword(Keyword::ALL); + let distinct = self.parse_keyword(Keyword::DISTINCT); if !distinct { return Ok(None); } if all { return parser_err!("Cannot specify both ALL and DISTINCT".to_string(), loc); } - let on = self.parse_keyword(Keyword::ON).is_some(); + let on = self.parse_keyword(Keyword::ON); if !on { return Ok(Some(Distinct::Distinct)); } @@ -3551,49 +3554,47 @@ impl<'a> Parser<'a> { .is_some(); let persistent = dialect_of!(self is DuckDbDialect) && self.parse_one_of_keywords(&[Keyword::PERSISTENT]).is_some(); - if self.parse_keyword(Keyword::TABLE).is_some() { + if self.parse_keyword(Keyword::TABLE) { self.parse_create_table(or_replace, temporary, global, transient) - } else if self.parse_keyword(Keyword::MATERIALIZED).is_some() - || self.parse_keyword(Keyword::VIEW).is_some() - { + } else if self.parse_keyword(Keyword::MATERIALIZED) || self.parse_keyword(Keyword::VIEW) { self.prev_token(); self.parse_create_view(or_replace, temporary) - } else if self.parse_keyword(Keyword::EXTERNAL).is_some() { + } else if self.parse_keyword(Keyword::EXTERNAL) { self.parse_create_external_table(or_replace) - } else if self.parse_keyword(Keyword::FUNCTION).is_some() { + } else if self.parse_keyword(Keyword::FUNCTION) { self.parse_create_function(or_replace, temporary) - } else if self.parse_keyword(Keyword::TRIGGER).is_some() { + } else if self.parse_keyword(Keyword::TRIGGER) { self.parse_create_trigger(or_replace, false) } else if self.parse_keywords(&[Keyword::CONSTRAINT, Keyword::TRIGGER]) { self.parse_create_trigger(or_replace, true) - } else if self.parse_keyword(Keyword::MACRO).is_some() { + } else if self.parse_keyword(Keyword::MACRO) { self.parse_create_macro(or_replace, temporary) - } else if self.parse_keyword(Keyword::SECRET).is_some() { + } else if self.parse_keyword(Keyword::SECRET) { self.parse_create_secret(or_replace, temporary, persistent) } else if or_replace { self.expected( "[EXTERNAL] TABLE or [MATERIALIZED] VIEW or FUNCTION after CREATE OR REPLACE", self.peek_token(), ) - } else if self.parse_keyword(Keyword::EXTENSION).is_some() { + } else if self.parse_keyword(Keyword::EXTENSION) { self.parse_create_extension() - } else if self.parse_keyword(Keyword::INDEX).is_some() { + } else if self.parse_keyword(Keyword::INDEX) { self.parse_create_index(false) } else if self.parse_keywords(&[Keyword::UNIQUE, Keyword::INDEX]) { self.parse_create_index(true) - } else if self.parse_keyword(Keyword::VIRTUAL).is_some() { + } else if self.parse_keyword(Keyword::VIRTUAL) { self.parse_create_virtual_table() - } else if self.parse_keyword(Keyword::SCHEMA).is_some() { + } else if self.parse_keyword(Keyword::SCHEMA) { self.parse_create_schema() - } else if self.parse_keyword(Keyword::DATABASE).is_some() { + } else if self.parse_keyword(Keyword::DATABASE) { self.parse_create_database() - } else if self.parse_keyword(Keyword::ROLE).is_some() { + } else if self.parse_keyword(Keyword::ROLE) { self.parse_create_role() - } else if self.parse_keyword(Keyword::SEQUENCE).is_some() { + } else if self.parse_keyword(Keyword::SEQUENCE) { self.parse_create_sequence(temporary) - } else if self.parse_keyword(Keyword::TYPE).is_some() { + } else if self.parse_keyword(Keyword::TYPE) { self.parse_create_type() - } else if self.parse_keyword(Keyword::PROCEDURE).is_some() { + } else if self.parse_keyword(Keyword::PROCEDURE) { self.parse_create_procedure(or_alter) } else { self.expected("an object type after CREATE", self.peek_token()) @@ -3612,7 +3613,7 @@ impl<'a> Parser<'a> { let mut storage_specifier = None; let mut name = None; if self.peek_token() != Token::LParen { - if self.parse_keyword(Keyword::IN).is_some() { + if self.parse_keyword(Keyword::IN) { storage_specifier = self.parse_identifier(false).ok() } else { name = self.parse_identifier(false).ok(); @@ -3621,7 +3622,7 @@ impl<'a> Parser<'a> { // Storage specifier may follow the name if storage_specifier.is_none() && self.peek_token() != Token::LParen - && self.parse_keyword(Keyword::IN).is_some() + && self.parse_keyword(Keyword::IN) { storage_specifier = self.parse_identifier(false).ok(); } @@ -3662,7 +3663,7 @@ impl<'a> Parser<'a> { /// Parse a CACHE TABLE statement pub fn parse_cache_table(&mut self) -> Result { let (mut table_flag, mut options, mut has_as, mut query) = (None, vec![], false, None); - if self.parse_keyword(Keyword::TABLE).is_some() { + if self.parse_keyword(Keyword::TABLE) { let table_name = self.parse_object_name(false)?; if self.peek_token().token != Token::EOF { if let Token::Word(word) = self.peek_token().token { @@ -3695,7 +3696,7 @@ impl<'a> Parser<'a> { } } else { table_flag = Some(self.parse_object_name(false)?); - if self.parse_keyword(Keyword::TABLE).is_some() { + if self.parse_keyword(Keyword::TABLE) { let table_name = self.parse_object_name(false)?; if self.peek_token() != Token::EOF { if let Token::Word(word) = self.peek_token().token { @@ -3792,14 +3793,14 @@ impl<'a> Parser<'a> { } fn parse_schema_name(&mut self) -> Result { - if self.parse_keyword(Keyword::AUTHORIZATION).is_some() { + if self.parse_keyword(Keyword::AUTHORIZATION) { Ok(SchemaName::UnnamedAuthorization( self.parse_identifier(false)?, )) } else { let name = self.parse_object_name(false)?; - if self.parse_keyword(Keyword::AUTHORIZATION).is_some() { + if self.parse_keyword(Keyword::AUTHORIZATION) { Ok(SchemaName::NamedAuthorization( name, self.parse_identifier(false)?, @@ -3835,7 +3836,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_create_function_using( &mut self, ) -> Result, ParserError> { - if self.parse_keyword(Keyword::USING).is_none() { + if !self.parse_keyword(Keyword::USING) { return Ok(None); }; let keyword = @@ -3892,7 +3893,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::RParen)?; - let return_type = if self.parse_keyword(Keyword::RETURNS).is_some() { + let return_type = if self.parse_keyword(Keyword::RETURNS) { Some(self.parse_data_type()?) } else { None @@ -3916,21 +3917,21 @@ impl<'a> Parser<'a> { } Ok(()) } - if self.parse_keyword(Keyword::AS).is_some() { + if self.parse_keyword(Keyword::AS) { ensure_not_set(&body.function_body, "AS")?; body.function_body = Some(CreateFunctionBody::AsBeforeOptions( self.parse_create_function_body_string()?, )); - } else if self.parse_keyword(Keyword::LANGUAGE).is_some() { + } else if self.parse_keyword(Keyword::LANGUAGE) { ensure_not_set(&body.language, "LANGUAGE")?; body.language = Some(self.parse_identifier(false)?); - } else if self.parse_keyword(Keyword::IMMUTABLE).is_some() { + } else if self.parse_keyword(Keyword::IMMUTABLE) { ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; body.behavior = Some(FunctionBehavior::Immutable); - } else if self.parse_keyword(Keyword::STABLE).is_some() { + } else if self.parse_keyword(Keyword::STABLE) { ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; body.behavior = Some(FunctionBehavior::Stable); - } else if self.parse_keyword(Keyword::VOLATILE).is_some() { + } else if self.parse_keyword(Keyword::VOLATILE) { ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; body.behavior = Some(FunctionBehavior::Volatile); } else if self.parse_keywords(&[ @@ -3956,24 +3957,24 @@ impl<'a> Parser<'a> { "CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT", )?; body.called_on_null = Some(FunctionCalledOnNull::ReturnsNullOnNullInput); - } else if self.parse_keyword(Keyword::STRICT).is_some() { + } else if self.parse_keyword(Keyword::STRICT) { ensure_not_set( &body.called_on_null, "CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT", )?; body.called_on_null = Some(FunctionCalledOnNull::Strict); - } else if self.parse_keyword(Keyword::PARALLEL).is_some() { + } else if self.parse_keyword(Keyword::PARALLEL) { ensure_not_set(&body.parallel, "PARALLEL { UNSAFE | RESTRICTED | SAFE }")?; - if self.parse_keyword(Keyword::UNSAFE).is_some() { + if self.parse_keyword(Keyword::UNSAFE) { body.parallel = Some(FunctionParallel::Unsafe); - } else if self.parse_keyword(Keyword::RESTRICTED).is_some() { + } else if self.parse_keyword(Keyword::RESTRICTED) { body.parallel = Some(FunctionParallel::Restricted); - } else if self.parse_keyword(Keyword::SAFE).is_some() { + } else if self.parse_keyword(Keyword::SAFE) { body.parallel = Some(FunctionParallel::Safe); } else { return self.expected("one of UNSAFE | RESTRICTED | SAFE", self.peek_token()); } - } else if self.parse_keyword(Keyword::RETURN).is_some() { + } else if self.parse_keyword(Keyword::RETURN) { ensure_not_set(&body.function_body, "RETURN")?; body.function_body = Some(CreateFunctionBody::Return(self.parse_expr()?)); } else { @@ -4059,13 +4060,13 @@ impl<'a> Parser<'a> { let args = self.parse_comma_separated0(parse_function_param, Token::RParen)?; self.expect_token(&Token::RParen)?; - let return_type = if self.parse_keyword(Keyword::RETURNS).is_some() { + let return_type = if self.parse_keyword(Keyword::RETURNS) { Some(self.parse_data_type()?) } else { None }; - let determinism_specifier = if self.parse_keyword(Keyword::DETERMINISTIC).is_some() { + let determinism_specifier = if self.parse_keyword(Keyword::DETERMINISTIC) { Some(FunctionDeterminismSpecifier::Deterministic) } else if self.parse_keywords(&[Keyword::NOT, Keyword::DETERMINISTIC]) { Some(FunctionDeterminismSpecifier::NotDeterministic) @@ -4073,7 +4074,7 @@ impl<'a> Parser<'a> { None }; - let language = if self.parse_keyword(Keyword::LANGUAGE).is_some() { + let language = if self.parse_keyword(Keyword::LANGUAGE) { Some(self.parse_identifier(false)?) } else { None @@ -4123,11 +4124,11 @@ impl<'a> Parser<'a> { } fn parse_function_arg(&mut self) -> Result { - let mode = if self.parse_keyword(Keyword::IN).is_some() { + let mode = if self.parse_keyword(Keyword::IN) { Some(ArgMode::In) - } else if self.parse_keyword(Keyword::OUT).is_some() { + } else if self.parse_keyword(Keyword::OUT) { Some(ArgMode::Out) - } else if self.parse_keyword(Keyword::INOUT).is_some() { + } else if self.parse_keyword(Keyword::INOUT) { Some(ArgMode::InOut) } else { None @@ -4142,12 +4143,12 @@ impl<'a> Parser<'a> { data_type = self.parse_data_type()?; } - let default_expr = - if self.parse_keyword(Keyword::DEFAULT).is_some() || self.consume_token(&Token::Eq) { - Some(self.parse_expr()?) - } else { - None - }; + let default_expr = if self.parse_keyword(Keyword::DEFAULT) || self.consume_token(&Token::Eq) + { + Some(self.parse_expr()?) + } else { + None + }; Ok(OperateFunctionArg { mode, name, @@ -4202,7 +4203,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::ON)?; let table_name = self.parse_object_name(false)?; - let referenced_table_name = if self.parse_keyword(Keyword::FROM).is_some() { + let referenced_table_name = if self.parse_keyword(Keyword::FROM) { self.parse_object_name(true).ok() } else { None @@ -4211,14 +4212,14 @@ impl<'a> Parser<'a> { let characteristics = self.parse_constraint_characteristics()?; let mut referencing = vec![]; - if self.parse_keyword(Keyword::REFERENCING).is_some() { + if self.parse_keyword(Keyword::REFERENCING) { while let Some(refer) = self.parse_trigger_referencing()? { referencing.push(refer); } } self.expect_keyword(Keyword::FOR)?; - let include_each = self.parse_keyword(Keyword::EACH).is_some(); + let include_each = self.parse_keyword(Keyword::EACH); let trigger_object = match self.expect_one_of_keywords(&[Keyword::ROW, Keyword::STATEMENT])? { Keyword::ROW => TriggerObject::Row, @@ -4228,7 +4229,6 @@ impl<'a> Parser<'a> { let condition = self .parse_keyword(Keyword::WHEN) - .is_some() .then(|| self.parse_expr()) .transpose()?; @@ -4280,7 +4280,7 @@ impl<'a> Parser<'a> { ])? { Keyword::INSERT => TriggerEvent::Insert, Keyword::UPDATE => { - if self.parse_keyword(Keyword::OF).is_some() { + if self.parse_keyword(Keyword::OF) { let cols = self.parse_comma_separated(|ident| { Parser::parse_identifier(ident, false) })?; @@ -4298,10 +4298,10 @@ impl<'a> Parser<'a> { pub fn parse_trigger_referencing(&mut self) -> Result, ParserError> { let refer_type = match self.parse_one_of_keywords(&[Keyword::OLD, Keyword::NEW]) { - Some(Keyword::OLD) if self.parse_keyword(Keyword::TABLE).is_some() => { + Some(Keyword::OLD) if self.parse_keyword(Keyword::TABLE) => { TriggerReferencingType::OldTable } - Some(Keyword::NEW) if self.parse_keyword(Keyword::TABLE).is_some() => { + Some(Keyword::NEW) if self.parse_keyword(Keyword::TABLE) => { TriggerReferencingType::NewTable } _ => { @@ -4309,7 +4309,7 @@ impl<'a> Parser<'a> { } }; - let is_as = self.parse_keyword(Keyword::AS).is_some(); + let is_as = self.parse_keyword(Keyword::AS); let transition_relation_name = self.parse_object_name(false)?; Ok(Some(TriggerReferencing { refer_type, @@ -4354,7 +4354,7 @@ impl<'a> Parser<'a> { temporary, name, args, - definition: if self.parse_keyword(Keyword::TABLE).is_some() { + definition: if self.parse_keyword(Keyword::TABLE) { MacroDefinition::Table(self.parse_query()?) } else { MacroDefinition::Expr(self.parse_expr()?) @@ -4449,7 +4449,7 @@ impl<'a> Parser<'a> { or_replace: bool, temporary: bool, ) -> Result { - let materialized = self.parse_keyword(Keyword::MATERIALIZED).is_some(); + let materialized = self.parse_keyword(Keyword::MATERIALIZED); self.expect_keyword(Keyword::VIEW)?; let if_not_exists = dialect_of!(self is BigQueryDialect|SQLiteDialect|GenericDialect) && self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); @@ -4464,7 +4464,7 @@ impl<'a> Parser<'a> { options = CreateTableOptions::With(with_options); } - let cluster_by = if self.parse_keyword(Keyword::CLUSTER).is_some() { + let cluster_by = if self.parse_keyword(Keyword::CLUSTER) { self.expect_keyword(Keyword::BY)?; self.parse_parenthesized_column_list(Optional, false)? } else { @@ -4480,7 +4480,7 @@ impl<'a> Parser<'a> { } let to = if dialect_of!(self is ClickHouseDialect | GenericDialect) - && self.parse_keyword(Keyword::TO).is_some() + && self.parse_keyword(Keyword::TO) { Some(self.parse_object_name(false)?) } else { @@ -4488,7 +4488,7 @@ impl<'a> Parser<'a> { }; let comment = if dialect_of!(self is SnowflakeDialect | GenericDialect) - && self.parse_keyword(Keyword::COMMENT).is_some() + && self.parse_keyword(Keyword::COMMENT) { self.expect_token(&Token::Eq)?; let next_token = self.next_token(); @@ -4532,7 +4532,7 @@ impl<'a> Parser<'a> { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; - let _ = self.parse_keyword(Keyword::WITH).is_some(); // [ WITH ] + let _ = self.parse_keyword(Keyword::WITH); // [ WITH ] let optional_keywords = if dialect_of!(self is MsSqlDialect) { vec![Keyword::AUTHORIZATION] @@ -4657,7 +4657,7 @@ impl<'a> Parser<'a> { if password.is_some() { parser_err!("Found multiple PASSWORD", loc) } else { - password = if self.parse_keyword(Keyword::NULL).is_some() { + password = if self.parse_keyword(Keyword::NULL) { Some(Password::NullPassword) } else { Some(Password::Password(Expr::Value(self.parse_value()?))) @@ -4684,14 +4684,14 @@ impl<'a> Parser<'a> { } } Keyword::IN => { - if self.parse_keyword(Keyword::ROLE).is_some() { + if self.parse_keyword(Keyword::ROLE) { if !in_role.is_empty() { parser_err!("Found multiple IN ROLE", loc) } else { in_role = self.parse_comma_separated(|p| p.parse_identifier(false))?; Ok(()) } - } else if self.parse_keyword(Keyword::GROUP).is_some() { + } else if self.parse_keyword(Keyword::GROUP) { if !in_group.is_empty() { parser_err!("Found multiple IN GROUP", loc) } else { @@ -4755,31 +4755,31 @@ impl<'a> Parser<'a> { pub fn parse_drop(&mut self) -> Result { // MySQL dialect supports `TEMPORARY` let temporary = dialect_of!(self is MySqlDialect | GenericDialect | DuckDbDialect) - && self.parse_keyword(Keyword::TEMPORARY).is_some(); + && self.parse_keyword(Keyword::TEMPORARY); let persistent = dialect_of!(self is DuckDbDialect) && self.parse_one_of_keywords(&[Keyword::PERSISTENT]).is_some(); - let object_type = if self.parse_keyword(Keyword::TABLE).is_some() { + let object_type = if self.parse_keyword(Keyword::TABLE) { ObjectType::Table - } else if self.parse_keyword(Keyword::VIEW).is_some() { + } else if self.parse_keyword(Keyword::VIEW) { ObjectType::View - } else if self.parse_keyword(Keyword::INDEX).is_some() { + } else if self.parse_keyword(Keyword::INDEX) { ObjectType::Index - } else if self.parse_keyword(Keyword::ROLE).is_some() { + } else if self.parse_keyword(Keyword::ROLE) { ObjectType::Role - } else if self.parse_keyword(Keyword::SCHEMA).is_some() { + } else if self.parse_keyword(Keyword::SCHEMA) { ObjectType::Schema - } else if self.parse_keyword(Keyword::SEQUENCE).is_some() { + } else if self.parse_keyword(Keyword::SEQUENCE) { ObjectType::Sequence - } else if self.parse_keyword(Keyword::STAGE).is_some() { + } else if self.parse_keyword(Keyword::STAGE) { ObjectType::Stage - } else if self.parse_keyword(Keyword::FUNCTION).is_some() { + } else if self.parse_keyword(Keyword::FUNCTION) { return self.parse_drop_function(); - } else if self.parse_keyword(Keyword::PROCEDURE).is_some() { + } else if self.parse_keyword(Keyword::PROCEDURE) { return self.parse_drop_procedure(); - } else if self.parse_keyword(Keyword::SECRET).is_some() { + } else if self.parse_keyword(Keyword::SECRET) { return self.parse_drop_secret(temporary, persistent); - } else if self.parse_keyword(Keyword::TRIGGER).is_some() { + } else if self.parse_keyword(Keyword::TRIGGER) { return self.parse_drop_trigger(); } else { return self.expected( @@ -4793,9 +4793,9 @@ impl<'a> Parser<'a> { let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; let loc = self.peek_token().span.start; - let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); - let restrict = self.parse_keyword(Keyword::RESTRICT).is_some(); - let purge = self.parse_keyword(Keyword::PURGE).is_some(); + let cascade = self.parse_keyword(Keyword::CASCADE); + let restrict = self.parse_keyword(Keyword::RESTRICT); + let purge = self.parse_keyword(Keyword::PURGE); if cascade && restrict { return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP", loc); } @@ -4881,7 +4881,7 @@ impl<'a> Parser<'a> { ) -> Result { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let name = self.parse_identifier(false)?; - let storage_specifier = if self.parse_keyword(Keyword::FROM).is_some() { + let storage_specifier = if self.parse_keyword(Keyword::FROM) { self.parse_identifier(false).ok() } else { None @@ -4923,15 +4923,15 @@ impl<'a> Parser<'a> { let name = self.parse_identifier(false)?; - let binary = Some(self.parse_keyword(Keyword::BINARY).is_some()); - let sensitive = if self.parse_keyword(Keyword::INSENSITIVE).is_some() { + let binary = Some(self.parse_keyword(Keyword::BINARY)); + let sensitive = if self.parse_keyword(Keyword::INSENSITIVE) { Some(true) - } else if self.parse_keyword(Keyword::ASENSITIVE).is_some() { + } else if self.parse_keyword(Keyword::ASENSITIVE) { Some(false) } else { None }; - let scroll = if self.parse_keyword(Keyword::SCROLL).is_some() { + let scroll = if self.parse_keyword(Keyword::SCROLL) { Some(true) } else if self.parse_keywords(&[Keyword::NO, Keyword::SCROLL]) { Some(false) @@ -4990,7 +4990,7 @@ impl<'a> Parser<'a> { }; let expr = if data_type.is_some() { - if self.parse_keyword(Keyword::DEFAULT).is_some() { + if self.parse_keyword(Keyword::DEFAULT) { Some(self.parse_expr()?) } else { None @@ -5046,7 +5046,7 @@ impl<'a> Parser<'a> { loop { let name = self.parse_identifier(false)?; let (declare_type, for_query, assigned_expr, data_type) = - if self.parse_keyword(Keyword::CURSOR).is_some() { + if self.parse_keyword(Keyword::CURSOR) { self.expect_keyword(Keyword::FOR)?; match self.peek_token().token { Token::Word(w) if w.keyword == Keyword::SELECT => ( @@ -5062,7 +5062,7 @@ impl<'a> Parser<'a> { None, ), } - } else if self.parse_keyword(Keyword::RESULTSET).is_some() { + } else if self.parse_keyword(Keyword::RESULTSET) { let assigned_expr = if self.peek_token().token != Token::SemiColon { self.parse_snowflake_variable_declaration_expression()? } else { @@ -5071,7 +5071,7 @@ impl<'a> Parser<'a> { }; (Some(DeclareType::ResultSet), None, assigned_expr, None) - } else if self.parse_keyword(Keyword::EXCEPTION).is_some() { + } else if self.parse_keyword(Keyword::EXCEPTION) { let assigned_expr = if self.peek_token().token == Token::LParen { Some(DeclareAssignment::Expr(Box::new(self.parse_expr()?))) } else { @@ -5243,24 +5243,24 @@ impl<'a> Parser<'a> { // FETCH [ direction { FROM | IN } ] cursor INTO target; pub fn parse_fetch_statement(&mut self) -> Result { - let direction = if self.parse_keyword(Keyword::NEXT).is_some() { + let direction = if self.parse_keyword(Keyword::NEXT) { FetchDirection::Next - } else if self.parse_keyword(Keyword::PRIOR).is_some() { + } else if self.parse_keyword(Keyword::PRIOR) { FetchDirection::Prior - } else if self.parse_keyword(Keyword::FIRST).is_some() { + } else if self.parse_keyword(Keyword::FIRST) { FetchDirection::First - } else if self.parse_keyword(Keyword::LAST).is_some() { + } else if self.parse_keyword(Keyword::LAST) { FetchDirection::Last - } else if self.parse_keyword(Keyword::ABSOLUTE).is_some() { + } else if self.parse_keyword(Keyword::ABSOLUTE) { FetchDirection::Absolute { limit: self.parse_number_value()?, } - } else if self.parse_keyword(Keyword::RELATIVE).is_some() { + } else if self.parse_keyword(Keyword::RELATIVE) { FetchDirection::Relative { limit: self.parse_number_value()?, } - } else if self.parse_keyword(Keyword::FORWARD).is_some() { - if self.parse_keyword(Keyword::ALL).is_some() { + } else if self.parse_keyword(Keyword::FORWARD) { + if self.parse_keyword(Keyword::ALL) { FetchDirection::ForwardAll } else { FetchDirection::Forward { @@ -5268,8 +5268,8 @@ impl<'a> Parser<'a> { limit: Some(self.parse_number_value()?), } } - } else if self.parse_keyword(Keyword::BACKWARD).is_some() { - if self.parse_keyword(Keyword::ALL).is_some() { + } else if self.parse_keyword(Keyword::BACKWARD) { + if self.parse_keyword(Keyword::ALL) { FetchDirection::BackwardAll } else { FetchDirection::Backward { @@ -5277,7 +5277,7 @@ impl<'a> Parser<'a> { limit: Some(self.parse_number_value()?), } } - } else if self.parse_keyword(Keyword::ALL).is_some() { + } else if self.parse_keyword(Keyword::ALL) { FetchDirection::All } else { FetchDirection::Count { @@ -5289,7 +5289,7 @@ impl<'a> Parser<'a> { let name = self.parse_identifier(false)?; - let into = if self.parse_keyword(Keyword::INTO).is_some() { + let into = if self.parse_keyword(Keyword::INTO) { Some(self.parse_object_name(false)?) } else { None @@ -5303,15 +5303,13 @@ impl<'a> Parser<'a> { } pub fn parse_discard(&mut self) -> Result { - let object_type = if self.parse_keyword(Keyword::ALL).is_some() { + let object_type = if self.parse_keyword(Keyword::ALL) { DiscardObject::ALL - } else if self.parse_keyword(Keyword::PLANS).is_some() { + } else if self.parse_keyword(Keyword::PLANS) { DiscardObject::PLANS - } else if self.parse_keyword(Keyword::SEQUENCES).is_some() { + } else if self.parse_keyword(Keyword::SEQUENCES) { DiscardObject::SEQUENCES - } else if self.parse_keyword(Keyword::TEMP).is_some() - || self.parse_keyword(Keyword::TEMPORARY).is_some() - { + } else if self.parse_keyword(Keyword::TEMP) || self.parse_keyword(Keyword::TEMPORARY) { DiscardObject::TEMP } else { return self.expected( @@ -5323,9 +5321,9 @@ impl<'a> Parser<'a> { } pub fn parse_create_index(&mut self, unique: bool) -> Result { - let concurrently = self.parse_keyword(Keyword::CONCURRENTLY).is_some(); + let concurrently = self.parse_keyword(Keyword::CONCURRENTLY); let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let index_name = if if_not_exists || self.parse_keyword(Keyword::ON).is_none() { + let index_name = if if_not_exists || !self.parse_keyword(Keyword::ON) { let index_name = self.parse_object_name(false)?; self.expect_keyword(Keyword::ON)?; Some(index_name) @@ -5333,7 +5331,7 @@ impl<'a> Parser<'a> { None }; let table_name = self.parse_object_name(false)?; - let using = if self.parse_keyword(Keyword::USING).is_some() { + let using = if self.parse_keyword(Keyword::USING) { Some(self.parse_identifier(false)?) } else { None @@ -5342,7 +5340,7 @@ impl<'a> Parser<'a> { let columns = self.parse_comma_separated(Parser::parse_order_by_expr)?; self.expect_token(&Token::RParen)?; - let include = if self.parse_keyword(Keyword::INCLUDE).is_some() { + let include = if self.parse_keyword(Keyword::INCLUDE) { self.expect_token(&Token::LParen)?; let columns = self.parse_comma_separated(|p| p.parse_identifier(false))?; self.expect_token(&Token::RParen)?; @@ -5351,8 +5349,8 @@ impl<'a> Parser<'a> { vec![] }; - let nulls_distinct = if self.parse_keyword(Keyword::NULLS).is_some() { - let not = self.parse_keyword(Keyword::NOT).is_some(); + let nulls_distinct = if self.parse_keyword(Keyword::NULLS) { + let not = self.parse_keyword(Keyword::NOT); self.expect_keyword(Keyword::DISTINCT)?; Some(!not) } else { @@ -5360,7 +5358,7 @@ impl<'a> Parser<'a> { }; let with = if self.dialect.supports_create_index_with_clause() - && self.parse_keyword(Keyword::WITH).is_some() + && self.parse_keyword(Keyword::WITH) { self.expect_token(&Token::LParen)?; let with_params = self.parse_comma_separated(Parser::parse_expr)?; @@ -5370,7 +5368,7 @@ impl<'a> Parser<'a> { Vec::new() }; - let predicate = if self.parse_keyword(Keyword::WHERE).is_some() { + let predicate = if self.parse_keyword(Keyword::WHERE) { Some(self.parse_expr()?) } else { None @@ -5395,20 +5393,20 @@ impl<'a> Parser<'a> { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let name = self.parse_identifier(false)?; - let (schema, version, cascade) = if self.parse_keyword(Keyword::WITH).is_some() { - let schema = if self.parse_keyword(Keyword::SCHEMA).is_some() { + let (schema, version, cascade) = if self.parse_keyword(Keyword::WITH) { + let schema = if self.parse_keyword(Keyword::SCHEMA) { Some(self.parse_identifier(false)?) } else { None }; - let version = if self.parse_keyword(Keyword::VERSION).is_some() { + let version = if self.parse_keyword(Keyword::VERSION) { Some(self.parse_identifier(false)?) } else { None }; - let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); + let cascade = self.parse_keyword(Keyword::CASCADE); (schema, version, cascade) } else { @@ -5450,7 +5448,7 @@ impl<'a> Parser<'a> { } Some(Keyword::STORED) => { self.expect_keyword(Keyword::AS)?; - if self.parse_keyword(Keyword::INPUTFORMAT).is_some() { + if self.parse_keyword(Keyword::INPUTFORMAT) { let input_format = self.parse_expr()?; self.expect_keyword(Keyword::OUTPUTFORMAT)?; let output_format = self.parse_expr()?; @@ -5602,15 +5600,13 @@ impl<'a> Parser<'a> { // Clickhouse has `ON CLUSTER 'cluster'` syntax for DDLs let on_cluster = self.parse_optional_on_cluster()?; - let like = if self.parse_keyword(Keyword::LIKE).is_some() - || self.parse_keyword(Keyword::ILIKE).is_some() - { + let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) { self.parse_object_name(allow_unquoted_hyphen).ok() } else { None }; - let clone = if self.parse_keyword(Keyword::CLONE).is_some() { + let clone = if self.parse_keyword(Keyword::CLONE) { self.parse_object_name(allow_unquoted_hyphen).ok() } else { None @@ -5619,7 +5615,7 @@ impl<'a> Parser<'a> { // parse optional column list (schema) let (columns, constraints) = self.parse_columns()?; let mut comment = if dialect_of!(self is HiveDialect) - && self.parse_keyword(Keyword::COMMENT).is_some() + && self.parse_keyword(Keyword::COMMENT) { let next_token = self.next_token(); match next_token.token { @@ -5640,7 +5636,7 @@ impl<'a> Parser<'a> { let with_options = self.parse_options(Keyword::WITH)?; let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?; - let engine = if self.parse_keyword(Keyword::ENGINE).is_some() { + let engine = if self.parse_keyword(Keyword::ENGINE) { self.expect_token(&Token::Eq)?; let next_token = self.next_token(); match next_token.token { @@ -5659,7 +5655,7 @@ impl<'a> Parser<'a> { None }; - let auto_increment_offset = if self.parse_keyword(Keyword::AUTO_INCREMENT).is_some() { + let auto_increment_offset = if self.parse_keyword(Keyword::AUTO_INCREMENT) { let _ = self.consume_token(&Token::Eq); let next_token = self.next_token(); match next_token.token { @@ -5737,10 +5733,10 @@ impl<'a> Parser<'a> { None }; - let strict = self.parse_keyword(Keyword::STRICT).is_some(); + let strict = self.parse_keyword(Keyword::STRICT); // Excludes Hive dialect here since it has been handled after table column definitions. - if !dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT).is_some() { + if !dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::COMMENT) { let _ = self.consume_token(&Token::Eq); let next_token = self.next_token(); comment = match next_token.token { @@ -5750,7 +5746,7 @@ impl<'a> Parser<'a> { }; // Parse optional `AS ( query )` - let query = if self.parse_keyword(Keyword::AS).is_some() { + let query = if self.parse_keyword(Keyword::AS) { Some(self.parse_boxed_query()?) } else { None @@ -5894,14 +5890,14 @@ impl<'a> Parser<'a> { } else { self.parse_data_type()? }; - let mut collation = if self.parse_keyword(Keyword::COLLATE).is_some() { + let mut collation = if self.parse_keyword(Keyword::COLLATE) { Some(self.parse_object_name(false)?) } else { None }; let mut options = vec![]; loop { - if self.parse_keyword(Keyword::CONSTRAINT).is_some() { + if self.parse_keyword(Keyword::CONSTRAINT) { let name = Some(self.parse_identifier(false)?); if let Some(option) = self.parse_optional_column_option()? { options.push(ColumnOptionDef { name, option }); @@ -5914,7 +5910,7 @@ impl<'a> Parser<'a> { } else if let Some(option) = self.parse_optional_column_option()? { options.push(ColumnOptionDef { name: None, option }); } else if dialect_of!(self is MySqlDialect | GenericDialect) - && self.parse_keyword(Keyword::COLLATE).is_some() + && self.parse_keyword(Keyword::COLLATE) { collation = Some(self.parse_object_name(false)?); } else { @@ -5965,20 +5961,20 @@ impl<'a> Parser<'a> { Token::SingleQuotedString(value, ..) => Ok(Some(ColumnOption::Comment(value))), _ => self.expected("string", next_token), } - } else if self.parse_keyword(Keyword::NULL).is_some() { + } else if self.parse_keyword(Keyword::NULL) { Ok(Some(ColumnOption::Null)) - } else if self.parse_keyword(Keyword::DEFAULT).is_some() { + } else if self.parse_keyword(Keyword::DEFAULT) { Ok(Some(ColumnOption::Default(self.parse_expr()?))) } else if dialect_of!(self is ClickHouseDialect| GenericDialect) - && self.parse_keyword(Keyword::MATERIALIZED).is_some() + && self.parse_keyword(Keyword::MATERIALIZED) { Ok(Some(ColumnOption::Materialized(self.parse_expr()?))) } else if dialect_of!(self is ClickHouseDialect| GenericDialect) - && self.parse_keyword(Keyword::ALIAS).is_some() + && self.parse_keyword(Keyword::ALIAS) { Ok(Some(ColumnOption::Alias(self.parse_expr()?))) } else if dialect_of!(self is ClickHouseDialect| GenericDialect) - && self.parse_keyword(Keyword::EPHEMERAL).is_some() + && self.parse_keyword(Keyword::EPHEMERAL) { // The expression is optional for the EPHEMERAL syntax, so we need to check // if the column definition has remaining tokens before parsing the expression. @@ -5993,13 +5989,13 @@ impl<'a> Parser<'a> { is_primary: true, characteristics, })) - } else if self.parse_keyword(Keyword::UNIQUE).is_some() { + } else if self.parse_keyword(Keyword::UNIQUE) { let characteristics = self.parse_constraint_characteristics()?; Ok(Some(ColumnOption::Unique { is_primary: false, characteristics, })) - } else if self.parse_keyword(Keyword::REFERENCES).is_some() { + } else if self.parse_keyword(Keyword::REFERENCES) { let foreign_table = self.parse_object_name(false)?; // PostgreSQL allows omitting the column list and // uses the primary key column of the foreign table by default @@ -6026,19 +6022,19 @@ impl<'a> Parser<'a> { on_update, characteristics, })) - } else if self.parse_keyword(Keyword::CHECK).is_some() { + } else if self.parse_keyword(Keyword::CHECK) { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; Ok(Some(ColumnOption::Check(expr))) - } else if self.parse_keyword(Keyword::AUTO_INCREMENT).is_some() + } else if self.parse_keyword(Keyword::AUTO_INCREMENT) && dialect_of!(self is MySqlDialect | GenericDialect) { // Support AUTO_INCREMENT for MySQL Ok(Some(ColumnOption::DialectSpecific(vec![ Token::make_keyword("AUTO_INCREMENT"), ]))) - } else if self.parse_keyword(Keyword::AUTOINCREMENT).is_some() + } else if self.parse_keyword(Keyword::AUTOINCREMENT) && dialect_of!(self is SQLiteDialect | GenericDialect) { // Support AUTOINCREMENT for SQLite @@ -6050,16 +6046,16 @@ impl<'a> Parser<'a> { { let expr = self.parse_expr()?; Ok(Some(ColumnOption::OnUpdate(expr))) - } else if self.parse_keyword(Keyword::GENERATED).is_some() { + } else if self.parse_keyword(Keyword::GENERATED) { self.parse_optional_column_option_generated() } else if dialect_of!(self is BigQueryDialect | GenericDialect) - && self.parse_keyword(Keyword::OPTIONS).is_some() + && self.parse_keyword(Keyword::OPTIONS) { self.prev_token(); Ok(Some(ColumnOption::Options( self.parse_options(Keyword::OPTIONS)?, ))) - } else if self.parse_keyword(Keyword::AS).is_some() + } else if self.parse_keyword(Keyword::AS) && dialect_of!(self is MySqlDialect | SQLiteDialect | DuckDbDialect | GenericDialect) { self.parse_optional_column_option_as() @@ -6190,9 +6186,9 @@ impl<'a> Parser<'a> { } pub fn parse_referential_action(&mut self) -> Result { - if self.parse_keyword(Keyword::RESTRICT).is_some() { + if self.parse_keyword(Keyword::RESTRICT) { Ok(ReferentialAction::Restrict) - } else if self.parse_keyword(Keyword::CASCADE).is_some() { + } else if self.parse_keyword(Keyword::CASCADE) { Ok(ReferentialAction::Cascade) } else if self.parse_keywords(&[Keyword::SET, Keyword::NULL]) { Ok(ReferentialAction::SetNull) @@ -6217,17 +6213,17 @@ impl<'a> Parser<'a> { if cc.deferrable.is_none() && self.parse_keywords(&[Keyword::NOT, Keyword::DEFERRABLE]) { cc.deferrable = Some(false); - } else if cc.deferrable.is_none() && self.parse_keyword(Keyword::DEFERRABLE).is_some() { + } else if cc.deferrable.is_none() && self.parse_keyword(Keyword::DEFERRABLE) { cc.deferrable = Some(true); - } else if cc.initially.is_none() && self.parse_keyword(Keyword::INITIALLY).is_some() { - if self.parse_keyword(Keyword::DEFERRED).is_some() { + } else if cc.initially.is_none() && self.parse_keyword(Keyword::INITIALLY) { + if self.parse_keyword(Keyword::DEFERRED) { cc.initially = Some(DeferrableInitial::Deferred); - } else if self.parse_keyword(Keyword::IMMEDIATE).is_some() { + } else if self.parse_keyword(Keyword::IMMEDIATE) { cc.initially = Some(DeferrableInitial::Immediate); } else { self.expected("one of DEFERRED or IMMEDIATE", self.peek_token())?; } - } else if cc.enforced.is_none() && self.parse_keyword(Keyword::ENFORCED).is_some() { + } else if cc.enforced.is_none() && self.parse_keyword(Keyword::ENFORCED) { cc.enforced = Some(true); } else if cc.enforced.is_none() && self.parse_keywords(&[Keyword::NOT, Keyword::ENFORCED]) @@ -6248,7 +6244,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_table_constraint( &mut self, ) -> Result, ParserError> { - let name = if self.parse_keyword(Keyword::CONSTRAINT).is_some() { + let name = if self.parse_keyword(Keyword::CONSTRAINT) { Some(self.parse_identifier(false)?) } else { None @@ -6415,7 +6411,7 @@ impl<'a> Parser<'a> { } pub fn parse_options(&mut self, keyword: Keyword) -> Result, ParserError> { - if self.parse_keyword(keyword).is_some() { + if self.parse_keyword(keyword) { self.expect_token(&Token::LParen)?; let options = self.parse_comma_separated(Parser::parse_sql_option)?; self.expect_token(&Token::RParen)?; @@ -6440,9 +6436,9 @@ impl<'a> Parser<'a> { } pub fn parse_index_type(&mut self) -> Result { - if self.parse_keyword(Keyword::BTREE).is_some() { + if self.parse_keyword(Keyword::BTREE) { Ok(IndexType::BTree) - } else if self.parse_keyword(Keyword::HASH).is_some() { + } else if self.parse_keyword(Keyword::HASH) { Ok(IndexType::Hash) } else { self.expected("index type {BTREE | HASH}", self.peek_token()) @@ -6453,7 +6449,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_using_then_index_type( &mut self, ) -> Result, ParserError> { - if self.parse_keyword(Keyword::USING).is_some() { + if self.parse_keyword(Keyword::USING) { Ok(Some(self.parse_index_type()?)) } else { Ok(None) @@ -6468,9 +6464,9 @@ impl<'a> Parser<'a> { #[must_use] pub fn parse_index_type_display(&mut self) -> KeyOrIndexDisplay { - if self.parse_keyword(Keyword::KEY).is_some() { + if self.parse_keyword(Keyword::KEY) { KeyOrIndexDisplay::Key - } else if self.parse_keyword(Keyword::INDEX).is_some() { + } else if self.parse_keyword(Keyword::INDEX) { KeyOrIndexDisplay::Index } else { KeyOrIndexDisplay::None @@ -6480,7 +6476,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_index_option(&mut self) -> Result, ParserError> { if let Some(index_type) = self.parse_optional_using_then_index_type()? { Ok(Some(IndexOption::Using(index_type))) - } else if self.parse_keyword(Keyword::COMMENT).is_some() { + } else if self.parse_keyword(Keyword::COMMENT) { let s = self.parse_literal_string()?; Ok(Some(IndexOption::Comment(s))) } else { @@ -6564,9 +6560,9 @@ impl<'a> Parser<'a> { let column_name = self.parse_identifier(false)?; self.expect_keyword(Keyword::RANGE)?; - let range_direction = if self.parse_keyword(Keyword::LEFT).is_some() { + let range_direction = if self.parse_keyword(Keyword::LEFT) { Some(PartitionRangeDirection::Left) - } else if self.parse_keyword(Keyword::RIGHT).is_some() { + } else if self.parse_keyword(Keyword::RIGHT) { Some(PartitionRangeDirection::Right) } else { None @@ -6619,11 +6615,11 @@ impl<'a> Parser<'a> { } pub fn parse_alter_table_operation(&mut self) -> Result { - let operation = if self.parse_keyword(Keyword::ADD).is_some() { + let operation = if self.parse_keyword(Keyword::ADD) { if let Some(constraint) = self.parse_optional_table_constraint()? { AlterTableOperation::AddConstraint(constraint) } else if dialect_of!(self is ClickHouseDialect|GenericDialect) - && self.parse_keyword(Keyword::PROJECTION).is_some() + && self.parse_keyword(Keyword::PROJECTION) { return self.parse_alter_table_add_projection(); } else { @@ -6631,7 +6627,7 @@ impl<'a> Parser<'a> { self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let mut new_partitions = vec![]; loop { - if self.parse_keyword(Keyword::PARTITION).is_some() { + if self.parse_keyword(Keyword::PARTITION) { new_partitions.push(self.parse_partition()?); } else { break; @@ -6643,7 +6639,7 @@ impl<'a> Parser<'a> { new_partitions, } } else { - let column_keyword = self.parse_keyword(Keyword::COLUMN).is_some(); + let column_keyword = self.parse_keyword(Keyword::COLUMN); let if_not_exists = if dialect_of!(self is PostgreSqlDialect | BigQueryDialect | DuckDbDialect | GenericDialect) { @@ -6665,19 +6661,17 @@ impl<'a> Parser<'a> { } } } - } else if self.parse_keyword(Keyword::RENAME).is_some() { - if dialect_of!(self is PostgreSqlDialect) - && self.parse_keyword(Keyword::CONSTRAINT).is_some() - { + } else if self.parse_keyword(Keyword::RENAME) { + if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::CONSTRAINT) { let old_name = self.parse_identifier(false)?; self.expect_keyword(Keyword::TO)?; let new_name = self.parse_identifier(false)?; AlterTableOperation::RenameConstraint { old_name, new_name } - } else if self.parse_keyword(Keyword::TO).is_some() { + } else if self.parse_keyword(Keyword::TO) { let table_name = self.parse_object_name(false)?; AlterTableOperation::RenameTable { table_name } } else { - let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] let old_column_name = self.parse_identifier(false)?; self.expect_keyword(Keyword::TO)?; let new_column_name = self.parse_identifier(false)?; @@ -6686,13 +6680,13 @@ impl<'a> Parser<'a> { new_column_name, } } - } else if self.parse_keyword(Keyword::DISABLE).is_some() { + } else if self.parse_keyword(Keyword::DISABLE) { if self.parse_keywords(&[Keyword::ROW, Keyword::LEVEL, Keyword::SECURITY]) { AlterTableOperation::DisableRowLevelSecurity {} - } else if self.parse_keyword(Keyword::RULE).is_some() { + } else if self.parse_keyword(Keyword::RULE) { let name = self.parse_identifier(false)?; AlterTableOperation::DisableRule { name } - } else if self.parse_keyword(Keyword::TRIGGER).is_some() { + } else if self.parse_keyword(Keyword::TRIGGER) { let name = self.parse_identifier(false)?; AlterTableOperation::DisableTrigger { name } } else { @@ -6701,7 +6695,7 @@ impl<'a> Parser<'a> { self.peek_token(), ); } - } else if self.parse_keyword(Keyword::ENABLE).is_some() { + } else if self.parse_keyword(Keyword::ENABLE) { if self.parse_keywords(&[Keyword::ALWAYS, Keyword::RULE]) { let name = self.parse_identifier(false)?; AlterTableOperation::EnableAlwaysRule { name } @@ -6716,10 +6710,10 @@ impl<'a> Parser<'a> { } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::TRIGGER]) { let name = self.parse_identifier(false)?; AlterTableOperation::EnableReplicaTrigger { name } - } else if self.parse_keyword(Keyword::RULE).is_some() { + } else if self.parse_keyword(Keyword::RULE) { let name = self.parse_identifier(false)?; AlterTableOperation::EnableRule { name } - } else if self.parse_keyword(Keyword::TRIGGER).is_some() { + } else if self.parse_keyword(Keyword::TRIGGER) { let name = self.parse_identifier(false)?; AlterTableOperation::EnableTrigger { name } } else { @@ -6758,7 +6752,7 @@ impl<'a> Parser<'a> { name, partition, } - } else if self.parse_keyword(Keyword::DROP).is_some() { + } else if self.parse_keyword(Keyword::DROP) { if self.parse_keywords(&[Keyword::IF, Keyword::EXISTS, Keyword::PARTITION]) { self.expect_token(&Token::LParen)?; let partitions = self.parse_comma_separated(Parser::parse_expr)?; @@ -6767,7 +6761,7 @@ impl<'a> Parser<'a> { partitions, if_exists: true, } - } else if self.parse_keyword(Keyword::PARTITION).is_some() { + } else if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; let partitions = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; @@ -6775,10 +6769,10 @@ impl<'a> Parser<'a> { partitions, if_exists: false, } - } else if self.parse_keyword(Keyword::CONSTRAINT).is_some() { + } else if self.parse_keyword(Keyword::CONSTRAINT) { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let name = self.parse_identifier(false)?; - let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); + let cascade = self.parse_keyword(Keyword::CASCADE); AlterTableOperation::DropConstraint { if_exists, name, @@ -6788,24 +6782,24 @@ impl<'a> Parser<'a> { && dialect_of!(self is MySqlDialect | GenericDialect) { AlterTableOperation::DropPrimaryKey - } else if self.parse_keyword(Keyword::PROJECTION).is_some() + } else if self.parse_keyword(Keyword::PROJECTION) && dialect_of!(self is ClickHouseDialect|GenericDialect) { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let name = self.parse_identifier(false)?; AlterTableOperation::DropProjection { if_exists, name } } else { - let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let column_name = self.parse_identifier(false)?; - let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); + let cascade = self.parse_keyword(Keyword::CASCADE); AlterTableOperation::DropColumn { column_name, if_exists, cascade, } } - } else if self.parse_keyword(Keyword::PARTITION).is_some() { + } else if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; let before = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; @@ -6818,8 +6812,8 @@ impl<'a> Parser<'a> { old_partitions: before, new_partitions: renames, } - } else if self.parse_keyword(Keyword::CHANGE).is_some() { - let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] + } else if self.parse_keyword(Keyword::CHANGE) { + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] let old_name = self.parse_identifier(false)?; let new_name = self.parse_identifier(false)?; let data_type = self.parse_data_type()?; @@ -6837,8 +6831,8 @@ impl<'a> Parser<'a> { options, column_position, } - } else if self.parse_keyword(Keyword::MODIFY).is_some() { - let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] + } else if self.parse_keyword(Keyword::MODIFY) { + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] let col_name = self.parse_identifier(false)?; let data_type = self.parse_data_type()?; let mut options = vec![]; @@ -6854,8 +6848,8 @@ impl<'a> Parser<'a> { options, column_position, } - } else if self.parse_keyword(Keyword::ALTER).is_some() { - let _ = self.parse_keyword(Keyword::COLUMN).is_some(); // [ COLUMN ] + } else if self.parse_keyword(Keyword::ALTER) { + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] let column_name = self.parse_identifier(false)?; let is_postgresql = dialect_of!(self is PostgreSqlDialect); @@ -6874,17 +6868,17 @@ impl<'a> Parser<'a> { } else if self.parse_keywords(&[Keyword::DROP, Keyword::DEFAULT]) { AlterColumnOperation::DropDefault {} } else if self.parse_keywords(&[Keyword::SET, Keyword::DATA, Keyword::TYPE]) - || (is_postgresql && self.parse_keyword(Keyword::TYPE).is_some()) + || (is_postgresql && self.parse_keyword(Keyword::TYPE)) { let data_type = self.parse_data_type()?; - let using = if is_postgresql && self.parse_keyword(Keyword::USING).is_some() { + let using = if is_postgresql && self.parse_keyword(Keyword::USING) { Some(self.parse_expr()?) } else { None }; AlterColumnOperation::SetDataType { data_type, using } } else if self.parse_keywords(&[Keyword::ADD, Keyword::GENERATED]) { - let generated_as = if self.parse_keyword(Keyword::ALWAYS).is_some() { + let generated_as = if self.parse_keyword(Keyword::ALWAYS) { Some(GeneratedAs::Always) } else if self.parse_keywords(&[Keyword::BY, Keyword::DEFAULT]) { Some(GeneratedAs::ByDefault) @@ -6916,7 +6910,7 @@ impl<'a> Parser<'a> { return self.expected(message, self.peek_token()); }; AlterTableOperation::AlterColumn { column_name, op } - } else if self.parse_keyword(Keyword::SWAP).is_some() { + } else if self.parse_keyword(Keyword::SWAP) { self.expect_keyword(Keyword::WITH)?; let table_name = self.parse_object_name(false)?; AlterTableOperation::SwapWith { table_name } @@ -6940,22 +6934,22 @@ impl<'a> Parser<'a> { AlterTableOperation::OwnerTo { new_owner } } else if dialect_of!(self is ClickHouseDialect|GenericDialect) - && self.parse_keyword(Keyword::ATTACH).is_some() + && self.parse_keyword(Keyword::ATTACH) { AlterTableOperation::AttachPartition { partition: self.parse_part_or_partition()?, } } else if dialect_of!(self is ClickHouseDialect|GenericDialect) - && self.parse_keyword(Keyword::DETACH).is_some() + && self.parse_keyword(Keyword::DETACH) { AlterTableOperation::DetachPartition { partition: self.parse_part_or_partition()?, } } else if dialect_of!(self is ClickHouseDialect|GenericDialect) - && self.parse_keyword(Keyword::FREEZE).is_some() + && self.parse_keyword(Keyword::FREEZE) { let partition = self.parse_part_or_partition()?; - let with_name = if self.parse_keyword(Keyword::WITH).is_some() { + let with_name = if self.parse_keyword(Keyword::WITH) { self.expect_keyword(Keyword::NAME)?; Some(self.parse_identifier(false)?) } else { @@ -6966,10 +6960,10 @@ impl<'a> Parser<'a> { with_name, } } else if dialect_of!(self is ClickHouseDialect|GenericDialect) - && self.parse_keyword(Keyword::UNFREEZE).is_some() + && self.parse_keyword(Keyword::UNFREEZE) { let partition = self.parse_part_or_partition()?; - let with_name = if self.parse_keyword(Keyword::WITH).is_some() { + let with_name = if self.parse_keyword(Keyword::WITH) { self.expect_keyword(Keyword::NAME)?; Some(self.parse_identifier(false)?) } else { @@ -7017,13 +7011,13 @@ impl<'a> Parser<'a> { Keyword::VIEW => self.parse_alter_view(), Keyword::TABLE => { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let only = self.parse_keyword(Keyword::ONLY).is_some(); // [ ONLY ] + let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ] let table_name = self.parse_object_name(false)?; let on_cluster = self.parse_optional_on_cluster()?; let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?; let mut location = None; - if self.parse_keyword(Keyword::LOCATION).is_some() { + if self.parse_keyword(Keyword::LOCATION) { location = Some(HiveSetLocation { has_set: false, location: self.parse_identifier(false)?, @@ -7046,8 +7040,8 @@ impl<'a> Parser<'a> { } Keyword::INDEX => { let index_name = self.parse_object_name(false)?; - let operation = if self.parse_keyword(Keyword::RENAME).is_some() { - if self.parse_keyword(Keyword::TO).is_some() { + let operation = if self.parse_keyword(Keyword::RENAME) { + if self.parse_keyword(Keyword::TO) { let index_name = self.parse_object_name(false)?; AlterIndexOperation::RenameIndex { index_name } } else { @@ -7138,11 +7132,11 @@ impl<'a> Parser<'a> { )); } } - let target = if self.parse_keyword(Keyword::STDIN).is_some() { + let target = if self.parse_keyword(Keyword::STDIN) { CopyTarget::Stdin - } else if self.parse_keyword(Keyword::STDOUT).is_some() { + } else if self.parse_keyword(Keyword::STDOUT) { CopyTarget::Stdout - } else if self.parse_keyword(Keyword::PROGRAM).is_some() { + } else if self.parse_keyword(Keyword::PROGRAM) { CopyTarget::Program { command: self.parse_literal_string()?, } @@ -7151,7 +7145,7 @@ impl<'a> Parser<'a> { filename: self.parse_literal_string()?, } }; - let _ = self.parse_keyword(Keyword::WITH).is_some(); // [ WITH ] + let _ = self.parse_keyword(Keyword::WITH); // [ WITH ] let mut options = vec![]; if self.consume_token(&Token::LParen) { options = self.parse_comma_separated(Parser::parse_copy_option)?; @@ -7178,7 +7172,7 @@ impl<'a> Parser<'a> { } pub fn parse_close(&mut self) -> Result { - let cursor = if self.parse_keyword(Keyword::ALL).is_some() { + let cursor = if self.parse_keyword(Keyword::ALL) { CloseCursor::All } else { let name = self.parse_identifier(false)?; @@ -7240,11 +7234,11 @@ impl<'a> Parser<'a> { ]) { Some(Keyword::BINARY) => CopyLegacyOption::Binary, Some(Keyword::DELIMITER) => { - let _ = self.parse_keyword(Keyword::AS).is_some(); // [ AS ] + let _ = self.parse_keyword(Keyword::AS); // [ AS ] CopyLegacyOption::Delimiter(self.parse_literal_char()?) } Some(Keyword::NULL) => { - let _ = self.parse_keyword(Keyword::AS).is_some(); // [ AS ] + let _ = self.parse_keyword(Keyword::AS); // [ AS ] CopyLegacyOption::Null(self.parse_literal_string()?) } Some(Keyword::CSV) => CopyLegacyOption::Csv({ @@ -7270,11 +7264,11 @@ impl<'a> Parser<'a> { ]) { Some(Keyword::HEADER) => CopyLegacyCsvOption::Header, Some(Keyword::QUOTE) => { - let _ = self.parse_keyword(Keyword::AS).is_some(); // [ AS ] + let _ = self.parse_keyword(Keyword::AS); // [ AS ] CopyLegacyCsvOption::Quote(self.parse_literal_char()?) } Some(Keyword::ESCAPE) => { - let _ = self.parse_keyword(Keyword::AS).is_some(); // [ AS ] + let _ = self.parse_keyword(Keyword::AS); // [ AS ] CopyLegacyCsvOption::Escape(self.parse_literal_char()?) } Some(Keyword::FORCE) if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) => { @@ -7555,7 +7549,7 @@ impl<'a> Parser<'a> { Keyword::FLOAT64 => Ok(DataType::Float64), Keyword::FLOAT8 => Ok(DataType::Float8), Keyword::DOUBLE => { - if self.parse_keyword(Keyword::PRECISION).is_some() { + if self.parse_keyword(Keyword::PRECISION) { Ok(DataType::DoublePrecision) } else { Ok(DataType::Double) @@ -7563,7 +7557,7 @@ impl<'a> Parser<'a> { } Keyword::TINYINT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedTinyInt(optional_precision?)) } else { Ok(DataType::TinyInt(optional_precision?)) @@ -7571,7 +7565,7 @@ impl<'a> Parser<'a> { } Keyword::INT2 => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedInt2(optional_precision?)) } else { Ok(DataType::Int2(optional_precision?)) @@ -7579,7 +7573,7 @@ impl<'a> Parser<'a> { } Keyword::SMALLINT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedSmallInt(optional_precision?)) } else { Ok(DataType::SmallInt(optional_precision?)) @@ -7587,7 +7581,7 @@ impl<'a> Parser<'a> { } Keyword::MEDIUMINT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedMediumInt(optional_precision?)) } else { Ok(DataType::MediumInt(optional_precision?)) @@ -7595,7 +7589,7 @@ impl<'a> Parser<'a> { } Keyword::INT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedInt(optional_precision?)) } else { Ok(DataType::Int(optional_precision?)) @@ -7603,7 +7597,7 @@ impl<'a> Parser<'a> { } Keyword::INT4 => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedInt4(optional_precision?)) } else { Ok(DataType::Int4(optional_precision?)) @@ -7611,7 +7605,7 @@ impl<'a> Parser<'a> { } Keyword::INT8 => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedInt8(optional_precision?)) } else { Ok(DataType::Int8(optional_precision?)) @@ -7624,7 +7618,7 @@ impl<'a> Parser<'a> { Keyword::INT256 => Ok(DataType::Int256), Keyword::INTEGER => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedInteger(optional_precision?)) } else { Ok(DataType::Integer(optional_precision?)) @@ -7632,7 +7626,7 @@ impl<'a> Parser<'a> { } Keyword::BIGINT => { let optional_precision = self.parse_optional_precision(); - if self.parse_keyword(Keyword::UNSIGNED).is_some() { + if self.parse_keyword(Keyword::UNSIGNED) { Ok(DataType::UnsignedBigInt(optional_precision?)) } else { Ok(DataType::BigInt(optional_precision?)) @@ -7649,7 +7643,7 @@ impl<'a> Parser<'a> { Ok(DataType::Nvarchar(self.parse_optional_character_length()?)) } Keyword::CHARACTER => { - if self.parse_keyword(Keyword::VARYING).is_some() { + if self.parse_keyword(Keyword::VARYING) { Ok(DataType::CharacterVarying( self.parse_optional_character_length()?, )) @@ -7662,7 +7656,7 @@ impl<'a> Parser<'a> { } } Keyword::CHAR => { - if self.parse_keyword(Keyword::VARYING).is_some() { + if self.parse_keyword(Keyword::VARYING) { Ok(DataType::CharVarying( self.parse_optional_character_length()?, )) @@ -7688,10 +7682,10 @@ impl<'a> Parser<'a> { } Keyword::TIMESTAMP => { let precision = self.parse_optional_precision()?; - let tz = if self.parse_keyword(Keyword::WITH).is_some() { + let tz = if self.parse_keyword(Keyword::WITH) { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithTimeZone - } else if self.parse_keyword(Keyword::WITHOUT).is_some() { + } else if self.parse_keyword(Keyword::WITHOUT) { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithoutTimeZone } else { @@ -7705,10 +7699,10 @@ impl<'a> Parser<'a> { )), Keyword::TIME => { let precision = self.parse_optional_precision()?; - let tz = if self.parse_keyword(Keyword::WITH).is_some() { + let tz = if self.parse_keyword(Keyword::WITH) { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithTimeZone - } else if self.parse_keyword(Keyword::WITHOUT).is_some() { + } else if self.parse_keyword(Keyword::WITHOUT) { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithoutTimeZone } else { @@ -7876,7 +7870,7 @@ impl<'a> Parser<'a> { &mut self, reserved_kwds: &[Keyword], ) -> Result, ParserError> { - let after_as = self.parse_keyword(Keyword::AS).is_some(); + let after_as = self.parse_keyword(Keyword::AS); let next_token = self.next_token(); match next_token.token { // Accept any identifier after `AS` (though many dialects have restrictions on @@ -7931,7 +7925,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_group_by(&mut self) -> Result, ParserError> { if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) { - let expressions = if self.parse_keyword(Keyword::ALL).is_some() { + let expressions = if self.parse_keyword(Keyword::ALL) { None } else { Some(self.parse_comma_separated(Parser::parse_group_by_expr)?) @@ -7940,7 +7934,7 @@ impl<'a> Parser<'a> { let mut modifiers = vec![]; if dialect_of!(self is ClickHouseDialect | GenericDialect) { loop { - if self.parse_keyword(Keyword::WITH).is_none() { + if !self.parse_keyword(Keyword::WITH) { break; } let keyword = self.expect_one_of_keywords(&[ @@ -8220,7 +8214,7 @@ impl<'a> Parser<'a> { fn parse_view_column(&mut self) -> Result { let name = self.parse_identifier(false)?; let options = if dialect_of!(self is BigQueryDialect | GenericDialect) - && self.parse_keyword(Keyword::OPTIONS).is_some() + && self.parse_keyword(Keyword::OPTIONS) { self.prev_token(); Some(self.parse_options(Keyword::OPTIONS)?) @@ -8311,13 +8305,13 @@ impl<'a> Parser<'a> { } pub fn parse_character_length(&mut self) -> Result { - if self.parse_keyword(Keyword::MAX).is_some() { + if self.parse_keyword(Keyword::MAX) { return Ok(CharacterLength::Max); } let length = self.parse_literal_uint()?; - let unit = if self.parse_keyword(Keyword::CHARACTERS).is_some() { + let unit = if self.parse_keyword(Keyword::CHARACTERS) { Some(CharLengthUnits::Characters) - } else if self.parse_keyword(Keyword::OCTETS).is_some() { + } else if self.parse_keyword(Keyword::OCTETS) { Some(CharLengthUnits::Octets) } else { None @@ -8402,7 +8396,7 @@ impl<'a> Parser<'a> { } pub fn parse_delete(&mut self) -> Result { - let (tables, with_from_keyword) = if self.parse_keyword(Keyword::FROM).is_none() { + let (tables, with_from_keyword) = if !self.parse_keyword(Keyword::FROM) { // `FROM` keyword is optional in BigQuery SQL. // https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#delete_statement if dialect_of!(self is BigQueryDialect | GenericDialect) { @@ -8417,17 +8411,17 @@ impl<'a> Parser<'a> { }; let from = self.parse_comma_separated(Parser::parse_table_and_joins)?; - let using = if self.parse_keyword(Keyword::USING).is_some() { + let using = if self.parse_keyword(Keyword::USING) { Some(self.parse_comma_separated(Parser::parse_table_and_joins)?) } else { None }; - let selection = if self.parse_keyword(Keyword::WHERE).is_some() { + let selection = if self.parse_keyword(Keyword::WHERE) { Some(self.parse_expr()?) } else { None }; - let returning = if self.parse_keyword(Keyword::RETURNING).is_some() { + let returning = if self.parse_keyword(Keyword::RETURNING) { Some(self.parse_comma_separated(Parser::parse_select_item)?) } else { None @@ -8437,7 +8431,7 @@ impl<'a> Parser<'a> { } else { vec![] }; - let limit = if self.parse_keyword(Keyword::LIMIT).is_some() { + let limit = if self.parse_keyword(Keyword::LIMIT) { self.parse_limit()? } else { None @@ -8488,10 +8482,10 @@ impl<'a> Parser<'a> { &mut self, describe_alias: DescribeAlias, ) -> Result { - let analyze = self.parse_keyword(Keyword::ANALYZE).is_some(); - let verbose = self.parse_keyword(Keyword::VERBOSE).is_some(); + let analyze = self.parse_keyword(Keyword::ANALYZE); + let verbose = self.parse_keyword(Keyword::VERBOSE); let mut format = None; - if self.parse_keyword(Keyword::FORMAT).is_some() { + if self.parse_keyword(Keyword::FORMAT) { format = Some(self.parse_analyze_format()?); } @@ -8516,7 +8510,7 @@ impl<'a> Parser<'a> { let has_table_keyword = if self.dialect.describe_requires_table_keyword() { // only allow to use TABLE keyword for DESC|DESCRIBE statement - self.parse_keyword(Keyword::TABLE).is_some() + self.parse_keyword(Keyword::TABLE) } else { false }; @@ -8547,16 +8541,16 @@ impl<'a> Parser<'a> { /// expect the initial keyword to be already consumed pub fn parse_query(&mut self) -> Result { let _guard = self.recursion_counter.try_decrease()?; - let with = if let Some(with_token) = self.parse_keyword(Keyword::WITH) { + let with = if let Some(with_token) = self.parse_keyword_token(Keyword::WITH) { Some(With { with_token, - recursive: self.parse_keyword(Keyword::RECURSIVE).is_some(), + recursive: self.parse_keyword(Keyword::RECURSIVE), cte_tables: self.parse_comma_separated(Parser::parse_cte)?, }) } else { None }; - if self.parse_keyword(Keyword::INSERT).is_some() { + if self.parse_keyword(Keyword::INSERT) { Ok(Query { with, body: self.parse_insert_setexpr_boxed()?, @@ -8570,7 +8564,7 @@ impl<'a> Parser<'a> { settings: None, format_clause: None, }) - } else if self.parse_keyword(Keyword::UPDATE).is_some() { + } else if self.parse_keyword(Keyword::UPDATE) { Ok(Query { with, body: self.parse_update_setexpr_boxed()?, @@ -8593,11 +8587,11 @@ impl<'a> Parser<'a> { let mut offset = None; for _x in 0..2 { - if limit.is_none() && self.parse_keyword(Keyword::LIMIT).is_some() { + if limit.is_none() && self.parse_keyword(Keyword::LIMIT) { limit = self.parse_limit()? } - if offset.is_none() && self.parse_keyword(Keyword::OFFSET).is_some() { + if offset.is_none() && self.parse_keyword(Keyword::OFFSET) { offset = Some(self.parse_offset()?) } @@ -8617,7 +8611,7 @@ impl<'a> Parser<'a> { } let limit_by = if dialect_of!(self is ClickHouseDialect | GenericDialect) - && self.parse_keyword(Keyword::BY).is_some() + && self.parse_keyword(Keyword::BY) { self.parse_comma_separated(Parser::parse_expr)? } else { @@ -8626,7 +8620,7 @@ impl<'a> Parser<'a> { let settings = self.parse_settings()?; - let fetch = if self.parse_keyword(Keyword::FETCH).is_some() { + let fetch = if self.parse_keyword(Keyword::FETCH) { Some(self.parse_fetch()?) } else { None @@ -8634,7 +8628,7 @@ impl<'a> Parser<'a> { let mut for_clause = None; let mut locks = Vec::new(); - while self.parse_keyword(Keyword::FOR).is_some() { + while self.parse_keyword(Keyword::FOR) { if let Some(parsed_for_clause) = self.parse_for_clause()? { for_clause = Some(parsed_for_clause); break; @@ -8643,9 +8637,9 @@ impl<'a> Parser<'a> { } } let format_clause = if dialect_of!(self is ClickHouseDialect | GenericDialect) - && self.parse_keyword(Keyword::FORMAT).is_some() + && self.parse_keyword(Keyword::FORMAT) { - if self.parse_keyword(Keyword::NULL).is_some() { + if self.parse_keyword(Keyword::NULL) { Some(FormatClause::Null) } else { let ident = self.parse_identifier(false)?; @@ -8673,7 +8667,7 @@ impl<'a> Parser<'a> { fn parse_settings(&mut self) -> Result>, ParserError> { let settings = if dialect_of!(self is ClickHouseDialect|GenericDialect) - && self.parse_keyword(Keyword::SETTINGS).is_some() + && self.parse_keyword(Keyword::SETTINGS) { let key_values = self.parse_comma_separated(|p| { let key = p.parse_identifier(false)?; @@ -8690,11 +8684,11 @@ impl<'a> Parser<'a> { /// Parse a mssql `FOR [XML | JSON | BROWSE]` clause pub fn parse_for_clause(&mut self) -> Result, ParserError> { - if self.parse_keyword(Keyword::XML).is_some() { + if self.parse_keyword(Keyword::XML) { Ok(Some(self.parse_for_xml()?)) - } else if self.parse_keyword(Keyword::JSON).is_some() { + } else if self.parse_keyword(Keyword::JSON) { Ok(Some(self.parse_for_json()?)) - } else if self.parse_keyword(Keyword::BROWSE).is_some() { + } else if self.parse_keyword(Keyword::BROWSE) { Ok(Some(ForClause::Browse)) } else { Ok(None) @@ -8703,7 +8697,7 @@ impl<'a> Parser<'a> { /// Parse a mssql `FOR XML` clause pub fn parse_for_xml(&mut self) -> Result { - let for_xml = if self.parse_keyword(Keyword::RAW).is_some() { + let for_xml = if self.parse_keyword(Keyword::RAW) { let mut element_name = None; if self.peek_token().token == Token::LParen { self.expect_token(&Token::LParen)?; @@ -8711,11 +8705,11 @@ impl<'a> Parser<'a> { self.expect_token(&Token::RParen)?; } ForXml::Raw(element_name) - } else if self.parse_keyword(Keyword::AUTO).is_some() { + } else if self.parse_keyword(Keyword::AUTO) { ForXml::Auto - } else if self.parse_keyword(Keyword::EXPLICIT).is_some() { + } else if self.parse_keyword(Keyword::EXPLICIT) { ForXml::Explicit - } else if self.parse_keyword(Keyword::PATH).is_some() { + } else if self.parse_keyword(Keyword::PATH) { let mut element_name = None; if self.peek_token().token == Token::LParen { self.expect_token(&Token::LParen)?; @@ -8734,16 +8728,16 @@ impl<'a> Parser<'a> { let mut r#type = false; while self.peek_token().token == Token::Comma { self.next_token(); - if self.parse_keyword(Keyword::ELEMENTS).is_some() { + if self.parse_keyword(Keyword::ELEMENTS) { elements = true; - } else if self.parse_keyword(Keyword::BINARY).is_some() { + } else if self.parse_keyword(Keyword::BINARY) { self.expect_keyword(Keyword::BASE64)?; binary_base64 = true; - } else if self.parse_keyword(Keyword::ROOT).is_some() { + } else if self.parse_keyword(Keyword::ROOT) { self.expect_token(&Token::LParen)?; root = Some(self.parse_literal_string()?); self.expect_token(&Token::RParen)?; - } else if self.parse_keyword(Keyword::TYPE).is_some() { + } else if self.parse_keyword(Keyword::TYPE) { r#type = true; } } @@ -8758,9 +8752,9 @@ impl<'a> Parser<'a> { /// Parse a mssql `FOR JSON` clause pub fn parse_for_json(&mut self) -> Result { - let for_json = if self.parse_keyword(Keyword::AUTO).is_some() { + let for_json = if self.parse_keyword(Keyword::AUTO) { ForJson::Auto - } else if self.parse_keyword(Keyword::PATH).is_some() { + } else if self.parse_keyword(Keyword::PATH) { ForJson::Path } else { return Err(ParserError::ParserError( @@ -8772,13 +8766,13 @@ impl<'a> Parser<'a> { let mut without_array_wrapper = false; while self.peek_token().token == Token::Comma { self.next_token(); - if self.parse_keyword(Keyword::ROOT).is_some() { + if self.parse_keyword(Keyword::ROOT) { self.expect_token(&Token::LParen)?; root = Some(self.parse_literal_string()?); self.expect_token(&Token::RParen)?; - } else if self.parse_keyword(Keyword::INCLUDE_NULL_VALUES).is_some() { + } else if self.parse_keyword(Keyword::INCLUDE_NULL_VALUES) { include_null_values = true; - } else if self.parse_keyword(Keyword::WITHOUT_ARRAY_WRAPPER).is_some() { + } else if self.parse_keyword(Keyword::WITHOUT_ARRAY_WRAPPER) { without_array_wrapper = true; } } @@ -8794,10 +8788,10 @@ impl<'a> Parser<'a> { pub fn parse_cte(&mut self) -> Result { let name = self.parse_identifier(false)?; - let mut cte = if self.parse_keyword(Keyword::AS).is_some() { + let mut cte = if self.parse_keyword(Keyword::AS) { let mut is_materialized = None; if dialect_of!(self is PostgreSqlDialect) { - if self.parse_keyword(Keyword::MATERIALIZED).is_some() { + if self.parse_keyword(Keyword::MATERIALIZED) { is_materialized = Some(CteAsMaterialized::Materialized); } else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) { is_materialized = Some(CteAsMaterialized::NotMaterialized); @@ -8822,7 +8816,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::AS)?; let mut is_materialized = None; if dialect_of!(self is PostgreSqlDialect) { - if self.parse_keyword(Keyword::MATERIALIZED).is_some() { + if self.parse_keyword(Keyword::MATERIALIZED) { is_materialized = Some(CteAsMaterialized::Materialized); } else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) { is_materialized = Some(CteAsMaterialized::NotMaterialized); @@ -8840,7 +8834,7 @@ impl<'a> Parser<'a> { closing_paren_token, } }; - if self.parse_keyword(Keyword::FROM).is_some() { + if self.parse_keyword(Keyword::FROM) { cte.from = Some(self.parse_identifier(false)?); } Ok(cte) @@ -8876,10 +8870,10 @@ impl<'a> Parser<'a> { let subquery = self.parse_boxed_query()?; self.expect_token(&Token::RParen)?; SetExpr::Query(subquery) - } else if self.parse_keyword(Keyword::VALUES).is_some() { + } else if self.parse_keyword(Keyword::VALUES) { let is_mysql = dialect_of!(self is MySqlDialect); SetExpr::Values(self.parse_values(is_mysql)?) - } else if self.parse_keyword(Keyword::TABLE).is_some() { + } else if self.parse_keyword(Keyword::TABLE) { SetExpr::Table(Box::new(self.parse_as_table()?)) } else { return self.expected( @@ -8942,13 +8936,13 @@ impl<'a> Parser<'a> { SetQuantifier::DistinctByName } else if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) { SetQuantifier::ByName - } else if self.parse_keyword(Keyword::ALL).is_some() { + } else if self.parse_keyword(Keyword::ALL) { if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) { SetQuantifier::AllByName } else { SetQuantifier::All } - } else if self.parse_keyword(Keyword::DISTINCT).is_some() { + } else if self.parse_keyword(Keyword::DISTINCT) { SetQuantifier::Distinct } else { SetQuantifier::None @@ -8963,10 +8957,10 @@ impl<'a> Parser<'a> { pub fn parse_select(&mut self) -> Result { let select_token = self.expect_keyword(Keyword::SELECT)?; let value_table_mode = - if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::AS).is_some() { - if self.parse_keyword(Keyword::VALUE).is_some() { + if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::AS) { + if self.parse_keyword(Keyword::VALUE) { Some(ValueTableMode::AsValue) - } else if self.parse_keyword(Keyword::STRUCT).is_some() { + } else if self.parse_keyword(Keyword::STRUCT) { Some(ValueTableMode::AsStruct) } else { self.expected("VALUE or STRUCT", self.peek_token())? @@ -8977,7 +8971,7 @@ impl<'a> Parser<'a> { let distinct = self.parse_all_or_distinct()?; - let top = if self.parse_keyword(Keyword::TOP).is_some() { + let top = if self.parse_keyword(Keyword::TOP) { Some(self.parse_top()?) } else { None @@ -8985,12 +8979,12 @@ impl<'a> Parser<'a> { let projection = self.parse_projection()?; - let into = if self.parse_keyword(Keyword::INTO).is_some() { + let into = if self.parse_keyword(Keyword::INTO) { let temporary = self .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY]) .is_some(); - let unlogged = self.parse_keyword(Keyword::UNLOGGED).is_some(); - let table = self.parse_keyword(Keyword::TABLE).is_some(); + let unlogged = self.parse_keyword(Keyword::UNLOGGED); + let table = self.parse_keyword(Keyword::TABLE); let name = self.parse_object_name(false)?; Some(SelectInto { temporary, @@ -9007,7 +9001,7 @@ impl<'a> Parser<'a> { // otherwise they may be parsed as an alias as part of the `projection` // or `from`. - let from = if self.parse_keyword(Keyword::FROM).is_some() { + let from = if self.parse_keyword(Keyword::FROM) { self.parse_comma_separated(Parser::parse_table_and_joins)? } else { vec![] @@ -9016,7 +9010,7 @@ impl<'a> Parser<'a> { let mut lateral_views = vec![]; loop { if self.parse_keywords(&[Keyword::LATERAL, Keyword::VIEW]) { - let outer = self.parse_keyword(Keyword::OUTER).is_some(); + let outer = self.parse_keyword(Keyword::OUTER); let lateral_view = self.parse_expr()?; let lateral_view_name = self.parse_object_name(false)?; let lateral_col_alias = self @@ -9045,14 +9039,14 @@ impl<'a> Parser<'a> { } let prewhere = if dialect_of!(self is ClickHouseDialect|GenericDialect) - && self.parse_keyword(Keyword::PREWHERE).is_some() + && self.parse_keyword(Keyword::PREWHERE) { Some(self.parse_expr()?) } else { None }; - let selection = if self.parse_keyword(Keyword::WHERE).is_some() { + let selection = if self.parse_keyword(Keyword::WHERE) { Some(self.parse_expr()?) } else { None @@ -9080,35 +9074,35 @@ impl<'a> Parser<'a> { vec![] }; - let having = if self.parse_keyword(Keyword::HAVING).is_some() { + let having = if self.parse_keyword(Keyword::HAVING) { Some(self.parse_expr()?) } else { None }; // Accept QUALIFY and WINDOW in any order and flag accordingly. - let (named_windows, qualify, window_before_qualify) = - if self.parse_keyword(Keyword::WINDOW).is_some() { - let named_windows = self.parse_comma_separated(Parser::parse_named_window)?; - if self.parse_keyword(Keyword::QUALIFY).is_some() { - (named_windows, Some(self.parse_expr()?), true) - } else { - (named_windows, None, true) - } - } else if self.parse_keyword(Keyword::QUALIFY).is_some() { - let qualify = Some(self.parse_expr()?); - if self.parse_keyword(Keyword::WINDOW).is_some() { - ( - self.parse_comma_separated(Parser::parse_named_window)?, - qualify, - false, - ) - } else { - (Default::default(), qualify, false) - } + let (named_windows, qualify, window_before_qualify) = if self.parse_keyword(Keyword::WINDOW) + { + let named_windows = self.parse_comma_separated(Parser::parse_named_window)?; + if self.parse_keyword(Keyword::QUALIFY) { + (named_windows, Some(self.parse_expr()?), true) } else { - Default::default() - }; + (named_windows, None, true) + } + } else if self.parse_keyword(Keyword::QUALIFY) { + let qualify = Some(self.parse_expr()?); + if self.parse_keyword(Keyword::WINDOW) { + ( + self.parse_comma_separated(Parser::parse_named_window)?, + qualify, + false, + ) + } else { + (Default::default(), qualify, false) + } + } else { + Default::default() + }; let connect_by = if self.dialect.supports_connect_by() && self @@ -9231,14 +9225,14 @@ impl<'a> Parser<'a> { self.parse_one_of_keywords(&[Keyword::SESSION, Keyword::LOCAL, Keyword::HIVEVAR]); if let Some(Keyword::HIVEVAR) = modifier { self.expect_token(&Token::Colon)?; - } else if self.parse_keyword(Keyword::ROLE).is_some() { + } else if self.parse_keyword(Keyword::ROLE) { let context_modifier = match modifier { Some(Keyword::LOCAL) => ContextModifier::Local, Some(Keyword::SESSION) => ContextModifier::Session, _ => ContextModifier::None, }; - let role_name = if self.parse_keyword(Keyword::NONE).is_some() { + let role_name = if self.parse_keyword(Keyword::NONE) { None } else { Some(self.parse_identifier(false)?) @@ -9271,7 +9265,7 @@ impl<'a> Parser<'a> { if matches!(&variables, OneOrManyWithParens::One(variable) if variable.to_string().eq_ignore_ascii_case("NAMES") && dialect_of!(self is MySqlDialect | GenericDialect)) { - if self.parse_keyword(Keyword::DEFAULT).is_some() { + if self.parse_keyword(Keyword::DEFAULT) { return Ok(Statement::SetNamesDefault {}); } @@ -9290,7 +9284,7 @@ impl<'a> Parser<'a> { let parenthesized_assignment = matches!(&variables, OneOrManyWithParens::Many(_)); - if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO).is_some() { + if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { if parenthesized_assignment { self.expect_token(&Token::LParen)?; } @@ -9343,7 +9337,7 @@ impl<'a> Parser<'a> { session: true, }) } else if variable.to_string() == "TRANSACTION" && modifier.is_none() { - if self.parse_keyword(Keyword::SNAPSHOT).is_some() { + if self.parse_keyword(Keyword::SNAPSHOT) { let snapshot_id = self.parse_value()?; return Ok(Statement::SetTransaction { modes: vec![], @@ -9362,18 +9356,18 @@ impl<'a> Parser<'a> { } pub fn parse_show(&mut self) -> Result { - let extended = self.parse_keyword(Keyword::EXTENDED).is_some(); - let full = self.parse_keyword(Keyword::FULL).is_some(); - let session = self.parse_keyword(Keyword::SESSION).is_some(); - let global = self.parse_keyword(Keyword::GLOBAL).is_some(); + let extended = self.parse_keyword(Keyword::EXTENDED); + let full = self.parse_keyword(Keyword::FULL); + let session = self.parse_keyword(Keyword::SESSION); + let global = self.parse_keyword(Keyword::GLOBAL); if self .parse_one_of_keywords(&[Keyword::COLUMNS, Keyword::FIELDS]) .is_some() { Ok(self.parse_show_columns(extended, full)?) - } else if self.parse_keyword(Keyword::TABLES).is_some() { + } else if self.parse_keyword(Keyword::TABLES) { Ok(self.parse_show_tables(extended, full)?) - } else if self.parse_keyword(Keyword::FUNCTIONS).is_some() { + } else if self.parse_keyword(Keyword::FUNCTIONS) { Ok(self.parse_show_functions()?) } else if extended || full { Err(ParserError::ParserError( @@ -9381,9 +9375,9 @@ impl<'a> Parser<'a> { )) } else if self.parse_one_of_keywords(&[Keyword::CREATE]).is_some() { Ok(self.parse_show_create()?) - } else if self.parse_keyword(Keyword::COLLATION).is_some() { + } else if self.parse_keyword(Keyword::COLLATION) { Ok(self.parse_show_collation()?) - } else if self.parse_keyword(Keyword::VARIABLES).is_some() + } else if self.parse_keyword(Keyword::VARIABLES) && dialect_of!(self is MySqlDialect | GenericDialect) { Ok(Statement::ShowVariables { @@ -9391,7 +9385,7 @@ impl<'a> Parser<'a> { session, global, }) - } else if self.parse_keyword(Keyword::STATUS).is_some() + } else if self.parse_keyword(Keyword::STATUS) && dialect_of!(self is MySqlDialect | GenericDialect) { Ok(Statement::ShowStatus { @@ -9487,15 +9481,15 @@ impl<'a> Parser<'a> { pub fn parse_show_statement_filter( &mut self, ) -> Result, ParserError> { - if self.parse_keyword(Keyword::LIKE).is_some() { + if self.parse_keyword(Keyword::LIKE) { Ok(Some(ShowStatementFilter::Like( self.parse_literal_string()?, ))) - } else if self.parse_keyword(Keyword::ILIKE).is_some() { + } else if self.parse_keyword(Keyword::ILIKE) { Ok(Some(ShowStatementFilter::ILike( self.parse_literal_string()?, ))) - } else if self.parse_keyword(Keyword::WHERE).is_some() { + } else if self.parse_keyword(Keyword::WHERE) { Ok(Some(ShowStatementFilter::Where(self.parse_expr()?))) } else { Ok(None) @@ -9506,7 +9500,7 @@ impl<'a> Parser<'a> { // Determine which keywords are recognized by the current dialect let parsed_keyword = if dialect_of!(self is HiveDialect) { // HiveDialect accepts USE DEFAULT; statement without any db specified - if self.parse_keyword(Keyword::DEFAULT).is_some() { + if self.parse_keyword(Keyword::DEFAULT) { return Ok(Statement::Use(Use::Default)); } None // HiveDialect doesn't expect any other specific keyword after `USE` @@ -9537,11 +9531,11 @@ impl<'a> Parser<'a> { // a table alias. let mut joins = vec![]; loop { - let global = self.parse_keyword(Keyword::GLOBAL).is_some(); - let join = if self.parse_keyword(Keyword::CROSS).is_some() { - let join_operator = if self.parse_keyword(Keyword::JOIN).is_some() { + let global = self.parse_keyword(Keyword::GLOBAL); + let join = if self.parse_keyword(Keyword::CROSS) { + let join_operator = if self.parse_keyword(Keyword::JOIN) { JoinOperator::CrossJoin - } else if self.parse_keyword(Keyword::APPLY).is_some() { + } else if self.parse_keyword(Keyword::APPLY) { // MSSQL extension, similar to CROSS JOIN LATERAL JoinOperator::CrossApply } else { @@ -9552,7 +9546,7 @@ impl<'a> Parser<'a> { global, join_operator, } - } else if self.parse_keyword(Keyword::OUTER).is_some() { + } else if self.parse_keyword(Keyword::OUTER) { // MSSQL extension, similar to LEFT JOIN LATERAL .. ON 1=1 self.expect_keyword(Keyword::APPLY)?; Join { @@ -9560,7 +9554,7 @@ impl<'a> Parser<'a> { global, join_operator: JoinOperator::OuterApply, } - } else if self.parse_keyword(Keyword::ASOF).is_some() { + } else if self.parse_keyword(Keyword::ASOF) { self.expect_keyword(Keyword::JOIN)?; let relation = self.parse_table_factor()?; self.expect_keyword(Keyword::MATCH_CONDITION)?; @@ -9574,7 +9568,7 @@ impl<'a> Parser<'a> { }, } } else { - let natural = self.parse_keyword(Keyword::NATURAL).is_some(); + let natural = self.parse_keyword(Keyword::NATURAL); let peek_keyword = if let Token::Word(w) = self.peek_token().token { w.keyword } else { @@ -9583,7 +9577,7 @@ impl<'a> Parser<'a> { let join_operator_type = match peek_keyword { Keyword::INNER | Keyword::JOIN => { - let _ = self.parse_keyword(Keyword::INNER).is_some(); // [ INNER ] + let _ = self.parse_keyword(Keyword::INNER); // [ INNER ] self.expect_keyword(Keyword::JOIN)?; JoinOperator::Inner } @@ -9637,7 +9631,7 @@ impl<'a> Parser<'a> { } Keyword::FULL => { let _ = self.next_token(); // consume FULL - let _ = self.parse_keyword(Keyword::OUTER).is_some(); // [ OUTER ] + let _ = self.parse_keyword(Keyword::OUTER); // [ OUTER ] self.expect_keyword(Keyword::JOIN)?; JoinOperator::FullOuter } @@ -9664,7 +9658,7 @@ impl<'a> Parser<'a> { /// A table name or a parenthesized subquery, followed by optional `[AS] alias` pub fn parse_table_factor(&mut self) -> Result { - if self.parse_keyword(Keyword::LATERAL).is_some() { + if self.parse_keyword(Keyword::LATERAL) { // LATERAL must always be followed by a subquery or table function. if self.consume_token(&Token::LParen) { self.parse_derived_table_factor(Lateral) @@ -9680,7 +9674,7 @@ impl<'a> Parser<'a> { alias, }) } - } else if self.parse_keyword(Keyword::TABLE).is_some() { + } else if self.parse_keyword(Keyword::TABLE) { // parse table function (SELECT * FROM TABLE () [ AS ]) self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; @@ -9834,7 +9828,7 @@ impl<'a> Parser<'a> { alias, }) } else if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | GenericDialect) - && self.parse_keyword(Keyword::UNNEST).is_some() + && self.parse_keyword(Keyword::UNNEST) { self.expect_token(&Token::LParen)?; let array_exprs = self.parse_comma_separated(Parser::parse_expr)?; @@ -9889,7 +9883,7 @@ impl<'a> Parser<'a> { let name = self.parse_object_name(true)?; let partitions: Vec = if dialect_of!(self is MySqlDialect | GenericDialect) - && self.parse_keyword(Keyword::PARTITION).is_some() + && self.parse_keyword(Keyword::PARTITION) { self.parse_parenthesized_identifiers()? } else { @@ -9912,7 +9906,7 @@ impl<'a> Parser<'a> { // MSSQL-specific table hints: let mut with_hints = vec![]; - if self.parse_keyword(Keyword::WITH).is_some() { + if self.parse_keyword(Keyword::WITH) { if self.consume_token(&Token::LParen) { with_hints = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; @@ -9941,7 +9935,7 @@ impl<'a> Parser<'a> { } if self.dialect.supports_match_recognize() - && self.parse_keyword(Keyword::MATCH_RECOGNIZE).is_some() + && self.parse_keyword(Keyword::MATCH_RECOGNIZE) { table = self.parse_match_recognize(table)?; } @@ -9965,10 +9959,10 @@ impl<'a> Parser<'a> { vec![] }; - let measures = if self.parse_keyword(Keyword::MEASURES).is_some() { + let measures = if self.parse_keyword(Keyword::MEASURES) { self.parse_comma_separated(|p| { let expr = p.parse_expr()?; - let _ = p.parse_keyword(Keyword::AS).is_some(); + let _ = p.parse_keyword(Keyword::AS); let alias = p.parse_identifier(false)?; Ok(Measure { expr, alias }) })? @@ -10197,13 +10191,13 @@ impl<'a> Parser<'a> { pub fn parse_json_table_column_def(&mut self) -> Result { let name = self.parse_identifier(false)?; let r#type = self.parse_data_type()?; - let exists = self.parse_keyword(Keyword::EXISTS).is_some(); + let exists = self.parse_keyword(Keyword::EXISTS); self.expect_keyword(Keyword::PATH)?; let path = self.parse_value()?; let mut on_empty = None; let mut on_error = None; while let Some(error_handling) = self.parse_json_table_column_error_handling()? { - if self.parse_keyword(Keyword::EMPTY).is_some() { + if self.parse_keyword(Keyword::EMPTY) { on_empty = Some(error_handling); } else { self.expect_keyword(Keyword::ERROR)?; @@ -10223,11 +10217,11 @@ impl<'a> Parser<'a> { fn parse_json_table_column_error_handling( &mut self, ) -> Result, ParserError> { - let res = if self.parse_keyword(Keyword::NULL).is_some() { + let res = if self.parse_keyword(Keyword::NULL) { JsonTableColumnErrorHandling::Null - } else if self.parse_keyword(Keyword::ERROR).is_some() { + } else if self.parse_keyword(Keyword::ERROR) { JsonTableColumnErrorHandling::Error - } else if self.parse_keyword(Keyword::DEFAULT).is_some() { + } else if self.parse_keyword(Keyword::DEFAULT) { JsonTableColumnErrorHandling::Default(self.parse_value()?) } else { return Ok(None); @@ -10259,7 +10253,7 @@ impl<'a> Parser<'a> { _ => self.expected("a function identifier", self.peek_token()), }?; let expr = self.parse_function(ObjectName(vec![Ident::new(function_name)]))?; - let alias = if self.parse_keyword(Keyword::AS).is_some() { + let alias = if self.parse_keyword(Keyword::AS) { Some(self.parse_identifier(false)?) } else { None @@ -10270,7 +10264,7 @@ impl<'a> Parser<'a> { fn parse_expr_with_alias(&mut self) -> Result { let expr = self.parse_expr()?; - let alias = if self.parse_keyword(Keyword::AS).is_some() { + let alias = if self.parse_keyword(Keyword::AS) { Some(self.parse_identifier(false)?) } else { None @@ -10290,7 +10284,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::IN)?; self.expect_token(&Token::LParen)?; - let value_source = if self.parse_keyword(Keyword::ANY).is_some() { + let value_source = if self.parse_keyword(Keyword::ANY) { let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { self.parse_comma_separated(Parser::parse_order_by_expr)? } else { @@ -10354,10 +10348,10 @@ impl<'a> Parser<'a> { pub fn parse_join_constraint(&mut self, natural: bool) -> Result { if natural { Ok(JoinConstraint::Natural) - } else if self.parse_keyword(Keyword::ON).is_some() { + } else if self.parse_keyword(Keyword::ON) { let constraint = self.parse_expr()?; Ok(JoinConstraint::On(constraint)) - } else if self.parse_keyword(Keyword::USING).is_some() { + } else if self.parse_keyword(Keyword::USING) { let columns = self.parse_parenthesized_column_list(Mandatory, false)?; Ok(JoinConstraint::Using(columns)) } else { @@ -10392,9 +10386,9 @@ impl<'a> Parser<'a> { pub fn parse_grant_revoke_privileges_objects( &mut self, ) -> Result<(Privileges, GrantObjects), ParserError> { - let privileges = if self.parse_keyword(Keyword::ALL).is_some() { + let privileges = if self.parse_keyword(Keyword::ALL) { Privileges::All { - with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES).is_some(), + with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES), } } else { let (actions, err): (Vec<_>, Vec<_>) = self @@ -10509,8 +10503,8 @@ impl<'a> Parser<'a> { .then(|| self.parse_identifier(false).unwrap()); let loc = self.peek_token().span.start; - let cascade = self.parse_keyword(Keyword::CASCADE).is_some(); - let restrict = self.parse_keyword(Keyword::RESTRICT).is_some(); + let cascade = self.parse_keyword(Keyword::CASCADE); + let restrict = self.parse_keyword(Keyword::RESTRICT); if cascade && restrict { return parser_err!("Cannot specify both CASCADE and RESTRICT in REVOKE", loc); } @@ -10562,7 +10556,7 @@ impl<'a> Parser<'a> { Some(SqliteOnConflict::Fail) } else if self.parse_keywords(&[Keyword::OR, Keyword::IGNORE]) { Some(SqliteOnConflict::Ignore) - } else if self.parse_keyword(Keyword::REPLACE).is_some() { + } else if self.parse_keyword(Keyword::REPLACE) { Some(SqliteOnConflict::Replace) } else { None @@ -10570,18 +10564,18 @@ impl<'a> Parser<'a> { let priority = if !dialect_of!(self is MySqlDialect | GenericDialect) { None - } else if self.parse_keyword(Keyword::LOW_PRIORITY).is_some() { + } else if self.parse_keyword(Keyword::LOW_PRIORITY) { Some(MysqlInsertPriority::LowPriority) - } else if self.parse_keyword(Keyword::DELAYED).is_some() { + } else if self.parse_keyword(Keyword::DELAYED) { Some(MysqlInsertPriority::Delayed) - } else if self.parse_keyword(Keyword::HIGH_PRIORITY).is_some() { + } else if self.parse_keyword(Keyword::HIGH_PRIORITY) { Some(MysqlInsertPriority::HighPriority) } else { None }; let ignore = dialect_of!(self is MySqlDialect | GenericDialect) - && self.parse_keyword(Keyword::IGNORE).is_some(); + && self.parse_keyword(Keyword::IGNORE); let replace_into = false; @@ -10589,9 +10583,9 @@ impl<'a> Parser<'a> { let into = action == Some(Keyword::INTO); let overwrite = action == Some(Keyword::OVERWRITE); - let local = self.parse_keyword(Keyword::LOCAL).is_some(); + let local = self.parse_keyword(Keyword::LOCAL); - if self.parse_keyword(Keyword::DIRECTORY).is_some() { + if self.parse_keyword(Keyword::DIRECTORY) { let path = self.parse_literal_string()?; let file_format = if self.parse_keywords(&[Keyword::STORED, Keyword::AS]) { Some(self.parse_file_format()?) @@ -10608,16 +10602,15 @@ impl<'a> Parser<'a> { }) } else { // Hive lets you put table here regardless - let table = self.parse_keyword(Keyword::TABLE).is_some(); + let table = self.parse_keyword(Keyword::TABLE); let table_name = self.parse_object_name(false)?; - let table_alias = if dialect_of!(self is PostgreSqlDialect) - && self.parse_keyword(Keyword::AS).is_some() - { - Some(self.parse_identifier(false)?) - } else { - None - }; + let table_alias = + if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) { + Some(self.parse_identifier(false)?) + } else { + None + }; let is_mysql = dialect_of!(self is MySqlDialect); @@ -10641,7 +10634,7 @@ impl<'a> Parser<'a> { }; let insert_alias = if dialect_of!(self is MySqlDialect | GenericDialect) - && self.parse_keyword(Keyword::AS).is_some() + && self.parse_keyword(Keyword::AS) { let row_alias = self.parse_object_name(false)?; let col_aliases = Some(self.parse_parenthesized_column_list(Optional, false)?); @@ -10653,8 +10646,8 @@ impl<'a> Parser<'a> { None }; - let on = if self.parse_keyword(Keyword::ON).is_some() { - if self.parse_keyword(Keyword::CONFLICT).is_some() { + let on = if self.parse_keyword(Keyword::ON) { + if self.parse_keyword(Keyword::CONFLICT) { let conflict_target = if self.parse_keywords(&[Keyword::ON, Keyword::CONSTRAINT]) { Some(ConflictTarget::OnConstraint(self.parse_object_name(false)?)) @@ -10667,13 +10660,13 @@ impl<'a> Parser<'a> { }; self.expect_keyword(Keyword::DO)?; - let action = if self.parse_keyword(Keyword::NOTHING).is_some() { + let action = if self.parse_keyword(Keyword::NOTHING) { OnConflictAction::DoNothing } else { self.expect_keyword(Keyword::UPDATE)?; self.expect_keyword(Keyword::SET)?; let assignments = self.parse_comma_separated(Parser::parse_assignment)?; - let selection = if self.parse_keyword(Keyword::WHERE).is_some() { + let selection = if self.parse_keyword(Keyword::WHERE) { Some(self.parse_expr()?) } else { None @@ -10700,7 +10693,7 @@ impl<'a> Parser<'a> { None }; - let returning = if self.parse_keyword(Keyword::RETURNING).is_some() { + let returning = if self.parse_keyword(Keyword::RETURNING) { Some(self.parse_comma_separated(Parser::parse_select_item)?) } else { None @@ -10728,7 +10721,7 @@ impl<'a> Parser<'a> { } pub fn parse_insert_partition(&mut self) -> Result>, ParserError> { - if self.parse_keyword(Keyword::PARTITION).is_some() { + if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; let partition_cols = Some(self.parse_comma_separated(Parser::parse_expr)?); self.expect_token(&Token::RParen)?; @@ -10749,19 +10742,19 @@ impl<'a> Parser<'a> { let table = self.parse_table_and_joins()?; self.expect_keyword(Keyword::SET)?; let assignments = self.parse_comma_separated(Parser::parse_assignment)?; - let from = if self.parse_keyword(Keyword::FROM).is_some() + let from = if self.parse_keyword(Keyword::FROM) && dialect_of!(self is GenericDialect | PostgreSqlDialect | DuckDbDialect | BigQueryDialect | SnowflakeDialect | RedshiftSqlDialect | MsSqlDialect | SQLiteDialect ) { Some(self.parse_table_and_joins()?) } else { None }; - let selection = if self.parse_keyword(Keyword::WHERE).is_some() { + let selection = if self.parse_keyword(Keyword::WHERE) { Some(self.parse_expr()?) } else { None }; - let returning = if self.parse_keyword(Keyword::RETURNING).is_some() { + let returning = if self.parse_keyword(Keyword::RETURNING) { Some(self.parse_comma_separated(Parser::parse_select_item)?) } else { None @@ -10903,12 +10896,12 @@ impl<'a> Parser<'a> { )); } - if self.parse_keyword(Keyword::LIMIT).is_some() { + if self.parse_keyword(Keyword::LIMIT) { clauses.push(FunctionArgumentClause::Limit(self.parse_expr()?)); } if dialect_of!(self is GenericDialect | BigQueryDialect) - && self.parse_keyword(Keyword::HAVING).is_some() + && self.parse_keyword(Keyword::HAVING) { let kind = match self.expect_one_of_keywords(&[Keyword::MIN, Keyword::MAX])? { Keyword::MIN => HavingBoundKind::Min, @@ -10922,7 +10915,7 @@ impl<'a> Parser<'a> { } if dialect_of!(self is GenericDialect | MySqlDialect) - && self.parse_keyword(Keyword::SEPARATOR).is_some() + && self.parse_keyword(Keyword::SEPARATOR) { clauses.push(FunctionArgumentClause::Separator(self.parse_value()?)); } @@ -10942,8 +10935,8 @@ impl<'a> Parser<'a> { fn parse_duplicate_treatment(&mut self) -> Result, ParserError> { let loc = self.peek_token().span.start; match ( - self.parse_keyword(Keyword::ALL).is_some(), - self.parse_keyword(Keyword::DISTINCT).is_some(), + self.parse_keyword(Keyword::ALL), + self.parse_keyword(Keyword::DISTINCT), ) { (true, false) => Ok(Some(DuplicateTreatment::All)), (false, true) => Ok(Some(DuplicateTreatment::Distinct)), @@ -11029,7 +11022,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_select_item_ilike( &mut self, ) -> Result, ParserError> { - let opt_ilike = if self.parse_keyword(Keyword::ILIKE).is_some() { + let opt_ilike = if self.parse_keyword(Keyword::ILIKE) { let next_token = self.next_token(); let pattern = match next_token.token { Token::SingleQuotedString(s) => s, @@ -11048,7 +11041,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_select_item_exclude( &mut self, ) -> Result, ParserError> { - let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE).is_some() { + let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE) { if self.consume_token(&Token::LParen) { let columns = self.parse_comma_separated(|parser| parser.parse_identifier(false))?; @@ -11071,7 +11064,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_select_item_except( &mut self, ) -> Result, ParserError> { - let opt_except = if self.parse_keyword(Keyword::EXCEPT).is_some() { + let opt_except = if self.parse_keyword(Keyword::EXCEPT) { if self.peek_token().token == Token::LParen { let idents = self.parse_parenthesized_column_list(Mandatory, false)?; match &idents[..] { @@ -11105,7 +11098,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_select_item_rename( &mut self, ) -> Result, ParserError> { - let opt_rename = if self.parse_keyword(Keyword::RENAME).is_some() { + let opt_rename = if self.parse_keyword(Keyword::RENAME) { if self.consume_token(&Token::LParen) { let idents = self.parse_comma_separated(|parser| parser.parse_identifier_with_alias())?; @@ -11126,7 +11119,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_select_item_replace( &mut self, ) -> Result, ParserError> { - let opt_replace = if self.parse_keyword(Keyword::REPLACE).is_some() { + let opt_replace = if self.parse_keyword(Keyword::REPLACE) { if self.consume_token(&Token::LParen) { let items = self.parse_comma_separated(|parser| { Ok(Box::new(parser.parse_replace_elements()?)) @@ -11145,7 +11138,7 @@ impl<'a> Parser<'a> { } pub fn parse_replace_elements(&mut self) -> Result { let expr = self.parse_expr()?; - let as_keyword = self.parse_keyword(Keyword::AS).is_some(); + let as_keyword = self.parse_keyword(Keyword::AS); let ident = self.parse_identifier(false)?; Ok(ReplaceSelectElement { expr, @@ -11157,9 +11150,9 @@ impl<'a> Parser<'a> { /// Parse ASC or DESC, returns an Option with true if ASC, false of DESC or `None` if none of /// them. pub fn parse_asc_desc(&mut self) -> Option { - if self.parse_keyword(Keyword::ASC).is_some() { + if self.parse_keyword(Keyword::ASC) { Some(true) - } else if self.parse_keyword(Keyword::DESC).is_some() { + } else if self.parse_keyword(Keyword::DESC) { Some(false) } else { None @@ -11199,19 +11192,19 @@ impl<'a> Parser<'a> { // Parse a WITH FILL clause (ClickHouse dialect) // that follow the WITH FILL keywords in a ORDER BY clause pub fn parse_with_fill(&mut self) -> Result { - let from = if self.parse_keyword(Keyword::FROM).is_some() { + let from = if self.parse_keyword(Keyword::FROM) { Some(self.parse_expr()?) } else { None }; - let to = if self.parse_keyword(Keyword::TO).is_some() { + let to = if self.parse_keyword(Keyword::TO) { Some(self.parse_expr()?) } else { None }; - let step = if self.parse_keyword(Keyword::STEP).is_some() { + let step = if self.parse_keyword(Keyword::STEP) { Some(self.parse_expr()?) } else { None @@ -11223,7 +11216,7 @@ impl<'a> Parser<'a> { // Parse a set of comma seperated INTERPOLATE expressions (ClickHouse dialect) // that follow the INTERPOLATE keyword in an ORDER BY clause with the WITH FILL modifier pub fn parse_interpolations(&mut self) -> Result, ParserError> { - if self.parse_keyword(Keyword::INTERPOLATE).is_none() { + if !self.parse_keyword(Keyword::INTERPOLATE) { return Ok(None); } @@ -11244,7 +11237,7 @@ impl<'a> Parser<'a> { // Parse a INTERPOLATE expression (ClickHouse dialect) pub fn parse_interpolation(&mut self) -> Result { let column = self.parse_identifier(false)?; - let expr = if self.parse_keyword(Keyword::AS).is_some() { + let expr = if self.parse_keyword(Keyword::AS) { Some(self.parse_expr()?) } else { None @@ -11268,7 +11261,7 @@ impl<'a> Parser<'a> { Some(TopQuantity::Constant(quantity)) }; - let percent = self.parse_keyword(Keyword::PERCENT).is_some(); + let percent = self.parse_keyword(Keyword::PERCENT); let with_ties = self.parse_keywords(&[Keyword::WITH, Keyword::TIES]); @@ -11281,7 +11274,7 @@ impl<'a> Parser<'a> { /// Parse a LIMIT clause pub fn parse_limit(&mut self) -> Result, ParserError> { - if self.parse_keyword(Keyword::ALL).is_some() { + if self.parse_keyword(Keyword::ALL) { Ok(None) } else { Ok(Some(self.parse_expr()?)) @@ -11291,9 +11284,9 @@ impl<'a> Parser<'a> { /// Parse an OFFSET clause pub fn parse_offset(&mut self) -> Result { let value = self.parse_expr()?; - let rows = if self.parse_keyword(Keyword::ROW).is_some() { + let rows = if self.parse_keyword(Keyword::ROW) { OffsetRows::Row - } else if self.parse_keyword(Keyword::ROWS).is_some() { + } else if self.parse_keyword(Keyword::ROWS) { OffsetRows::Rows } else { OffsetRows::None @@ -11311,11 +11304,11 @@ impl<'a> Parser<'a> { (None, false) } else { let quantity = Expr::Value(self.parse_value()?); - let percent = self.parse_keyword(Keyword::PERCENT).is_some(); + let percent = self.parse_keyword(Keyword::PERCENT); self.expect_one_of_keywords(&[Keyword::ROW, Keyword::ROWS])?; (Some(quantity), percent) }; - let with_ties = if self.parse_keyword(Keyword::ONLY).is_some() { + let with_ties = if self.parse_keyword(Keyword::ONLY) { false } else if self.parse_keywords(&[Keyword::WITH, Keyword::TIES]) { true @@ -11336,12 +11329,12 @@ impl<'a> Parser<'a> { Keyword::SHARE => LockType::Share, _ => unreachable!(), }; - let of = if self.parse_keyword(Keyword::OF).is_some() { + let of = if self.parse_keyword(Keyword::OF) { Some(self.parse_object_name(false)?) } else { None }; - let nonblock = if self.parse_keyword(Keyword::NOWAIT).is_some() { + let nonblock = if self.parse_keyword(Keyword::NOWAIT) { Some(NonBlock::Nowait) } else if self.parse_keywords(&[Keyword::SKIP, Keyword::LOCKED]) { Some(NonBlock::SkipLocked) @@ -11359,7 +11352,7 @@ impl<'a> Parser<'a> { let mut explicit_row = false; let rows = self.parse_comma_separated(|parser| { - if parser.parse_keyword(Keyword::ROW).is_some() { + if parser.parse_keyword(Keyword::ROW) { explicit_row = true; } @@ -11388,11 +11381,11 @@ impl<'a> Parser<'a> { pub fn parse_begin(&mut self) -> Result { let modifier = if !self.dialect.supports_start_transaction_modifier() { None - } else if self.parse_keyword(Keyword::DEFERRED).is_some() { + } else if self.parse_keyword(Keyword::DEFERRED) { Some(TransactionModifier::Deferred) - } else if self.parse_keyword(Keyword::IMMEDIATE).is_some() { + } else if self.parse_keyword(Keyword::IMMEDIATE) { Some(TransactionModifier::Immediate) - } else if self.parse_keyword(Keyword::EXCLUSIVE).is_some() { + } else if self.parse_keyword(Keyword::EXCLUSIVE) { Some(TransactionModifier::Exclusive) } else { None @@ -11422,7 +11415,7 @@ impl<'a> Parser<'a> { TransactionIsolationLevel::ReadCommitted } else if self.parse_keywords(&[Keyword::REPEATABLE, Keyword::READ]) { TransactionIsolationLevel::RepeatableRead - } else if self.parse_keyword(Keyword::SERIALIZABLE).is_some() { + } else if self.parse_keyword(Keyword::SERIALIZABLE) { TransactionIsolationLevel::Serializable } else { self.expected("isolation level", self.peek_token())? @@ -11462,8 +11455,8 @@ impl<'a> Parser<'a> { pub fn parse_commit_rollback_chain(&mut self) -> Result { let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]); - if self.parse_keyword(Keyword::AND).is_some() { - let chain = self.parse_keyword(Keyword::NO).is_none(); + if self.parse_keyword(Keyword::AND) { + let chain = !self.parse_keyword(Keyword::NO); self.expect_keyword(Keyword::CHAIN)?; Ok(chain) } else { @@ -11472,8 +11465,8 @@ impl<'a> Parser<'a> { } pub fn parse_rollback_savepoint(&mut self) -> Result, ParserError> { - if self.parse_keyword(Keyword::TO).is_some() { - let _ = self.parse_keyword(Keyword::SAVEPOINT).is_some(); + if self.parse_keyword(Keyword::TO) { + let _ = self.parse_keyword(Keyword::SAVEPOINT); let savepoint = self.parse_identifier(false)?; Ok(Some(savepoint)) @@ -11483,7 +11476,7 @@ impl<'a> Parser<'a> { } pub fn parse_deallocate(&mut self) -> Result { - let prepare = self.parse_keyword(Keyword::PREPARE).is_some(); + let prepare = self.parse_keyword(Keyword::PREPARE); let name = self.parse_identifier(false)?; Ok(Statement::Deallocate { name, prepare }) } @@ -11498,7 +11491,7 @@ impl<'a> Parser<'a> { } let mut using = vec![]; - if self.parse_keyword(Keyword::USING).is_some() { + if self.parse_keyword(Keyword::USING) { using.push(self.parse_expr()?); while self.consume_token(&Token::Comma) { @@ -11557,7 +11550,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::WHEN)?; let mut clause_kind = MergeClauseKind::Matched; - if self.parse_keyword(Keyword::NOT).is_some() { + if self.parse_keyword(Keyword::NOT) { clause_kind = MergeClauseKind::NotMatched; } self.expect_keyword(Keyword::MATCHED)?; @@ -11572,7 +11565,7 @@ impl<'a> Parser<'a> { clause_kind = MergeClauseKind::NotMatchedByTarget; } - let predicate = if self.parse_keyword(Keyword::AND).is_some() { + let predicate = if self.parse_keyword(Keyword::AND) { Some(self.parse_expr()?) } else { None @@ -11623,7 +11616,7 @@ impl<'a> Parser<'a> { let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; let kind = if dialect_of!(self is BigQueryDialect | GenericDialect) - && self.parse_keyword(Keyword::ROW).is_some() + && self.parse_keyword(Keyword::ROW) { MergeInsertKind::Row } else { @@ -11649,7 +11642,7 @@ impl<'a> Parser<'a> { } pub fn parse_merge(&mut self) -> Result { - let into = self.parse_keyword(Keyword::INTO).is_some(); + let into = self.parse_keyword(Keyword::INTO); let table = self.parse_table_factor()?; @@ -11729,8 +11722,8 @@ impl<'a> Parser<'a> { let name = self.parse_object_name(false)?; let on_cluster = self.parse_optional_on_cluster()?; - let partition = if self.parse_keyword(Keyword::PARTITION).is_some() { - if self.parse_keyword(Keyword::ID).is_some() { + let partition = if self.parse_keyword(Keyword::PARTITION) { + if self.parse_keyword(Keyword::ID) { Some(Partition::Identifier(self.parse_identifier(false)?)) } else { Some(Partition::Expr(self.parse_expr()?)) @@ -11739,9 +11732,9 @@ impl<'a> Parser<'a> { None }; - let include_final = self.parse_keyword(Keyword::FINAL).is_some(); - let deduplicate = if self.parse_keyword(Keyword::DEDUPLICATE).is_some() { - if self.parse_keyword(Keyword::BY).is_some() { + let include_final = self.parse_keyword(Keyword::FINAL); + let deduplicate = if self.parse_keyword(Keyword::DEDUPLICATE) { + if self.parse_keyword(Keyword::BY) { Some(Deduplicate::ByExpression(self.parse_expr()?)) } else { Some(Deduplicate::All) @@ -11806,7 +11799,7 @@ impl<'a> Parser<'a> { } } //[ MINVALUE minvalue | NO MINVALUE ] - if self.parse_keyword(Keyword::MINVALUE).is_some() { + if self.parse_keyword(Keyword::MINVALUE) { sequence_options.push(SequenceOptions::MinValue(Some(self.parse_number()?))); } else if self.parse_keywords(&[Keyword::NO, Keyword::MINVALUE]) { sequence_options.push(SequenceOptions::MinValue(None)); @@ -11922,7 +11915,7 @@ impl<'a> Parser<'a> { loop { let attr_name = self.parse_identifier(false)?; let attr_data_type = self.parse_data_type()?; - let attr_collation = if self.parse_keyword(Keyword::COLLATE).is_some() { + let attr_collation = if self.parse_keyword(Keyword::COLLATE) { Some(self.parse_object_name(false)?) } else { None @@ -11956,9 +11949,9 @@ impl<'a> Parser<'a> { fn parse_column_position(&mut self) -> Result, ParserError> { if dialect_of!(self is MySqlDialect | GenericDialect) { - if self.parse_keyword(Keyword::FIRST).is_some() { + if self.parse_keyword(Keyword::FIRST) { Ok(Some(MySQLColumnPosition::First)) - } else if self.parse_keyword(Keyword::AFTER).is_some() { + } else if self.parse_keyword(Keyword::AFTER) { let ident = self.parse_identifier(false)?; Ok(Some(MySQLColumnPosition::After(ident))) } else { diff --git a/tests/sqlparser_custom_dialect.rs b/tests/sqlparser_custom_dialect.rs index 471dc47ea..5b29047a4 100644 --- a/tests/sqlparser_custom_dialect.rs +++ b/tests/sqlparser_custom_dialect.rs @@ -106,7 +106,7 @@ fn custom_statement_parser() -> Result<(), ParserError> { } fn parse_statement(&self, parser: &mut Parser) -> Option> { - if parser.parse_keyword(Keyword::SELECT).is_some() { + if parser.parse_keyword(Keyword::SELECT) { for _ in 0..3 { let _ = parser.next_token(); } From 0804e9959637ab59e5ae9fbf722999816a23e8f9 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 8 Oct 2024 12:58:06 +0200 Subject: [PATCH 14/32] fix: diverging hash and partialeq implementations --- src/ast/mod.rs | 9 ++++++++- src/tokenizer.rs | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 123af6522..cb7d3e003 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -128,7 +128,7 @@ where } /// An identifier, decomposed into its value or character data and the quote style. -#[derive(Debug, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct Ident { @@ -147,6 +147,13 @@ impl PartialEq for Ident { } } +impl core::hash::Hash for Ident { + fn hash(&self, state: &mut H) { + self.value.hash(state); + self.quote_style.hash(state); + } +} + impl Eq for Ident {} impl Ident { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index a7568176e..9d8fb84d9 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -505,7 +505,7 @@ impl Span { } /// A [Token] with [Location] attached to it -#[derive(Debug, Eq, Hash, Clone)] +#[derive(Debug, Eq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TokenWithLocation { pub token: Token, @@ -526,6 +526,12 @@ impl TokenWithLocation { } } +impl core::hash::Hash for TokenWithLocation { + fn hash(&self, state: &mut H) { + self.token.hash(state); + } +} + impl PartialEq for TokenWithLocation { fn eq(&self, other: &TokenWithLocation) -> bool { self.token == other.token From eb9ff9af36b74167fb2508522e1b9972a0fda651 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Wed, 9 Oct 2024 09:53:00 +0200 Subject: [PATCH 15/32] Update src/ast/spans.rs Co-authored-by: Ifeanyi Ubah --- src/ast/spans.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index b0df0ac15..c3af0d7d4 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -22,7 +22,7 @@ use super::{ /// /// Source spans are not guaranteed to be entirely accurate. They may /// be missing keywords or other tokens. Some nodes may not have a computable -/// span at all, in which case they return `Span::empty()`. +/// span at all, in which case they return [`Span::empty()`]. /// /// Some impl blocks may contain doc comments with information /// on which nodes are missing spans. From a93cebc18749e320016dc4659a81ae672c8726f2 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Wed, 9 Oct 2024 10:00:49 +0200 Subject: [PATCH 16/32] improve docs & un-pub union_spans --- src/ast/mod.rs | 2 +- src/ast/spans.rs | 3 ++- src/tokenizer.rs | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index cb7d3e003..a8337e8b0 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -82,7 +82,7 @@ pub mod helpers; mod operator; mod query; mod spans; -pub use spans::{union_spans, Spanned}; +pub use spans::Spanned; mod trigger; mod value; diff --git a/src/ast/spans.rs b/src/ast/spans.rs index c3af0d7d4..d7fe397fa 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1642,7 +1642,8 @@ impl Spanned for TableWithJoins { } } -pub fn union_spans>(iter: I) -> Span { +/// Given an iterator of spans, return the [Span::union] of all spans. +fn union_spans>(iter: I) -> Span { iter.reduce(|acc, item| acc.union(&item)) .unwrap_or(Span::empty()) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 9d8fb84d9..ec6456ded 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -470,12 +470,16 @@ impl std::fmt::Debug for Span { } impl Span { + // An empty span (0, 0) -> (0, 0) + // We need a const instance for pattern matching const EMPTY: Span = Self::empty(); pub fn new(start: Location, end: Location) -> Span { Span { start, end } } + /// Returns an empty span (0, 0) -> (0, 0) + /// Empty spans represent no knowledge of source location pub const fn empty() -> Span { Span { start: Location { line: 0, column: 0 }, @@ -483,6 +487,8 @@ impl Span { } } + /// Returns the smallest Span that contains both `self` and `other` + /// If either span is [Span::empty], the other span is returned pub fn union(&self, other: &Span) -> Span { // If either span is empty, return the other // this prevents propagating (0, 0) through the tree @@ -496,6 +502,8 @@ impl Span { } } + /// Same as [Span::union] for `Option` + /// If `other` is `None`, `self` is returned pub fn union_opt(&self, other: &Option) -> Span { match other { Some(other) => self.union(other), From 734264a4efb1c83147938cc83f5ff0d27c9c8809 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Wed, 9 Oct 2024 10:02:37 +0200 Subject: [PATCH 17/32] move union_spans to top of file --- src/ast/spans.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index d7fe397fa..03aa0166b 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -18,6 +18,12 @@ use super::{ WildcardAdditionalOptions, With, WithFill, }; +/// Given an iterator of spans, return the [Span::union] of all spans. +fn union_spans>(iter: I) -> Span { + iter.reduce(|acc, item| acc.union(&item)) + .unwrap_or(Span::empty()) +} + /// A trait for AST nodes that have a source span for use in diagnostics. /// /// Source spans are not guaranteed to be entirely accurate. They may @@ -1642,12 +1648,6 @@ impl Spanned for TableWithJoins { } } -/// Given an iterator of spans, return the [Span::union] of all spans. -fn union_spans>(iter: I) -> Span { - iter.reduce(|acc, item| acc.union(&item)) - .unwrap_or(Span::empty()) -} - impl Spanned for Select { fn span(&self) -> Span { union_spans( From 441ceb19a489b494b30c83b948ce9b35e4b72164 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Wed, 9 Oct 2024 10:12:01 +0200 Subject: [PATCH 18/32] replace old tests --- src/ast/spans.rs | 101 ++++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 03aa0166b..b95b2ba5f 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1680,65 +1680,94 @@ pub mod tests { } #[test] - fn test_query_span() { - let query = crate::parser::Parser::new(&GenericDialect) - .try_with_sql( - "SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id", - ) - .unwrap() - .parse_query() - .unwrap(); + fn test_join() { + let dialect = &GenericDialect; + let mut test = SpanTest::new( + dialect, + "SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id", + ); + + let query = test.0.parse_select().unwrap(); + let select_span = query.span(); + + assert_eq!( + test.get_source(select_span), + "SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id" + ); + + let join_span = query.from[0].joins[0].span(); + // 'LEFT JOIN' missing assert_eq!( - query.span(), - Span::new((1, 1).into(), (1, 109 - 28 + 1).into()) + test.get_source(join_span), + "companies ON users.company_id = companies.id" ); } #[test] pub fn test_union() { - let query = crate::parser::Parser::new(&GenericDialect) - .try_with_sql( - "SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source", - ) - .unwrap() - .parse_query() - .unwrap(); + let dialect = &GenericDialect; + let mut test = SpanTest::new( + dialect, + "SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source", + ); - query.span(); + let query = test.0.parse_query().unwrap(); + let select_span = query.span(); + + assert_eq!( + test.get_source(select_span), + "SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source" + ); } #[test] pub fn test_subquery() { - let query = crate::parser::Parser::new(&GenericDialect) - .try_with_sql("SELECT a FROM (SELECT a FROM postgres.public.source) AS b") - .unwrap() - .parse_query() - .unwrap(); + let dialect = &GenericDialect; + let mut test = SpanTest::new( + dialect, + "SELECT a FROM (SELECT a FROM postgres.public.source) AS b", + ); + + let query = test.0.parse_select().unwrap(); + let select_span = query.span(); + + assert_eq!( + test.get_source(select_span), + "SELECT a FROM (SELECT a FROM postgres.public.source) AS b" + ); + + let subquery_span = query.from[0].span(); - query.span(); + // left paren missing + assert_eq!( + test.get_source(subquery_span), + "SELECT a FROM postgres.public.source) AS b" + ); } #[test] pub fn test_cte() { - let query = crate::parser::Parser::new(&GenericDialect) - .try_with_sql("WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner") - .unwrap() - .parse_query() - .unwrap(); + let dialect = &GenericDialect; + let mut test = SpanTest::new(dialect, "WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner"); + + let query = test.0.parse_query().unwrap(); + + let select_span = query.span(); - query.span(); + assert_eq!(test.get_source(select_span), "WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner"); } #[test] pub fn test_snowflake_lateral_flatten() { - let query = crate::parser::Parser::new(&SnowflakeDialect) - .try_with_sql("SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED") - .unwrap() - .parse_query() - .unwrap(); + let dialect = &SnowflakeDialect; + let mut test = SpanTest::new(dialect, "SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED"); + + let query = test.0.parse_select().unwrap(); + + let select_span = query.span(); - query.span(); + assert_eq!(test.get_source(select_span), "SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED"); } #[test] From e6a434054865253a78c3f3953cc24826a0e0dd63 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Fri, 11 Oct 2024 11:05:13 +0200 Subject: [PATCH 19/32] pr feedback --- src/ast/mod.rs | 18 +++++++++++++++--- src/ast/spans.rs | 9 +++++---- src/tokenizer.rs | 14 ++++++++++---- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a8337e8b0..6b1210166 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -143,14 +143,26 @@ pub struct Ident { impl PartialEq for Ident { fn eq(&self, other: &Self) -> bool { - self.value == other.value && self.quote_style == other.quote_style + let Ident { + value, + quote_style, + span: _, + } = self; + + value == &other.value && quote_style == &other.quote_style } } impl core::hash::Hash for Ident { fn hash(&self, state: &mut H) { - self.value.hash(state); - self.quote_style.hash(state); + let Ident { + value, + quote_style, + span: _, + } = self; + + value.hash(state); + quote_style.hash(state); } } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index b95b2ba5f..98f94ee73 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1200,9 +1200,8 @@ impl Spanned for Subscript { upper_bound.as_ref().map(|i| i.span()), stride.as_ref().map(|i| i.span()), ] - .iter() - .flatten() - .cloned(), + .into_iter() + .flatten(), ), } } @@ -1529,7 +1528,7 @@ impl Spanned for OrderByExpr { fn span(&self) -> Span { self.expr .span() - .union_opt(&self.with_fill.as_ref().map(|f| f.span()).clone()) + .union_opt(&self.with_fill.as_ref().map(|f| f.span())) } } @@ -1673,6 +1672,8 @@ pub mod tests { Self(Parser::new(dialect).try_with_sql(sql).unwrap(), sql) } + // get the subsection of the source string that corresponds to the span + // only works on single-line strings fn get_source(&self, span: Span) -> &'a str { // lines in spans are 1-indexed &self.1[(span.start.column as usize - 1)..(span.end.column - 1) as usize] diff --git a/src/tokenizer.rs b/src/tokenizer.rs index ec6456ded..cd6521278 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -456,6 +456,7 @@ impl From<(u64, u64)> for Location { } } +/// A span of source code locations (start, end) #[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Span { @@ -536,25 +537,30 @@ impl TokenWithLocation { impl core::hash::Hash for TokenWithLocation { fn hash(&self, state: &mut H) { - self.token.hash(state); + let TokenWithLocation { token, span: _ } = self; + + token.hash(state); } } impl PartialEq for TokenWithLocation { fn eq(&self, other: &TokenWithLocation) -> bool { - self.token == other.token + let TokenWithLocation { token, span: _ } = self; + + token == &other.token } } impl PartialOrd for TokenWithLocation { fn partial_cmp(&self, other: &TokenWithLocation) -> Option { - self.token.partial_cmp(&other.token) + Some(self.cmp(other)) } } impl Ord for TokenWithLocation { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.token.cmp(&other.token) + let TokenWithLocation { token, span: _ } = self; + token.cmp(&other.token) } } From 16a3f2a8eb8fb0bf10ada26ca1f6c77c9d697f88 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Fri, 11 Oct 2024 11:15:35 +0200 Subject: [PATCH 20/32] add small comment --- src/ast/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 6b1210166..bbc4540fc 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -146,6 +146,7 @@ impl PartialEq for Ident { let Ident { value, quote_style, + // backwards compat span: _, } = self; @@ -158,6 +159,7 @@ impl core::hash::Hash for Ident { let Ident { value, quote_style, + // backwards compat span: _, } = self; From 98b051dc11488101dd6ec3e88c9e1234ceb46402 Mon Sep 17 00:00:00 2001 From: Nyrox Date: Wed, 16 Oct 2024 12:15:57 +0200 Subject: [PATCH 21/32] refactor: rewrite all span implementations to pattern match exhaustively on self --- src/ast/spans.rs | 583 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 460 insertions(+), 123 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 98f94ee73..6b01fbaac 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -5,16 +5,17 @@ use crate::tokenizer::Span; use super::{ AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Array, Assignment, AssignmentTarget, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, - ConflictTarget, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, + ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, - ExprWithAlias, FromTable, Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, - FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, IlikeSelectItem, Insert, - Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, - MatchRecognizePattern, Measure, ObjectName, OnConflict, OnConflictAction, OnInsert, OrderBy, + ExprWithAlias, Fetch, FromTable, Function, FunctionArg, FunctionArgExpr, + FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, + IlikeSelectItem, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, + JsonPath, JsonPathElem, LateralView, MatchRecognizePattern, Measure, NamedWindowDefinition, + ObjectName, Offset, OnConflict, OnConflictAction, OnInsert, OrderBy, OrderByExpr, Partition, PivotValueSource, ProjectionSelect, Query, ReferentialAction, - RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectItem, SetExpr, - SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableConstraint, TableFactor, - TableOptionsClustered, TableWithJoins, Use, Value, Values, ViewColumnDef, + RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, + SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableConstraint, + TableFactor, TableOptionsClustered, TableWithJoins, Use, Value, Values, ViewColumnDef, WildcardAdditionalOptions, With, WithFill, }; @@ -40,28 +41,85 @@ pub trait Spanned { impl Spanned for Query { fn span(&self) -> Span { - self.body - .span() - .union_opt(&self.with.as_ref().map(|i| i.span())) + let Query { + with, + body, + order_by, + limit, + limit_by, + offset, + fetch, + locks: _, // todo + for_clause: _, // todo, mysql specific + settings: _, // todo, clickhouse specific + format_clause: _, // todo, clickhouse specific + } = self; + + union_spans( + with.iter() + .map(|i| i.span()) + .chain(core::iter::once(body.span())) + .chain(order_by.as_ref().map(|i| i.span())) + .chain(limit.as_ref().map(|i| i.span())) + .chain(limit_by.iter().map(|i| i.span())) + .chain(offset.as_ref().map(|i| i.span())) + .chain(fetch.as_ref().map(|i| i.span())), + ) + } +} + +impl Spanned for Offset { + fn span(&self) -> Span { + let Offset { + value, + rows: _, // enum + } = self; + + value.span() + } +} + +impl Spanned for Fetch { + fn span(&self) -> Span { + let Fetch { + with_ties: _, // bool + percent: _, // bool + quantity, + } = self; + + quantity.as_ref().map_or(Span::empty(), |i| i.span()) } } impl Spanned for With { fn span(&self) -> Span { + let With { + with_token, + recursive: _, // bool + cte_tables, + } = self; + union_spans( - core::iter::once(self.with_token.span) - .chain(self.cte_tables.iter().map(|item| item.span())), + core::iter::once(with_token.span).chain(cte_tables.iter().map(|item| item.span())), ) } } impl Spanned for Cte { fn span(&self) -> Span { + let Cte { + alias, + query, + from, + materialized: _, // enum + closing_paren_token, + } = self; + union_spans( - core::iter::once(self.alias.span()) - .chain(core::iter::once(self.query.span())) - .chain(self.from.iter().map(|item| item.span)) - .chain(core::iter::once(self.closing_paren_token.span)), + core::iter::once(alias.span()) + .chain(core::iter::once(query.span())) + .chain(from.iter().map(|item| item.span)) + .chain(core::iter::once(closing_paren_token.span)), ) } } @@ -90,9 +148,13 @@ impl Spanned for SetExpr { impl Spanned for Values { fn span(&self) -> Span { + let Values { + explicit_row: _, // bool, + rows, + } = self; + union_spans( - self.rows - .iter() + rows.iter() .map(|row| union_spans(row.iter().map(|expr| expr.span()))), ) } @@ -387,34 +449,87 @@ impl Spanned for Use { impl Spanned for CreateTable { fn span(&self) -> Span { + let CreateTable { + or_replace: _, // bool + temporary: _, // bool + external: _, // bool + global: _, // bool + if_not_exists: _, // bool + transient: _, // bool + volatile: _, // bool + name, + columns, + constraints, + hive_distribution: _, // hive specific + hive_formats: _, // hive specific + table_properties, + with_options, + file_format: _, // enum + location: _, // string, no span + query, + without_rowid: _, // bool + like, + clone, + engine: _, // todo + comment: _, // todo, no span + auto_increment_offset: _, // u32, no span + default_charset: _, // string, no span + collation: _, // string, no span + on_commit: _, // enum + on_cluster: _, // todo, clickhouse specific + primary_key: _, // todo, clickhouse specific + order_by: _, // todo, clickhouse specific + partition_by: _, // todo, BigQuery specific + cluster_by: _, // todo, BigQuery specific + clustered_by: _, // todo, Hive specific + options: _, // todo, BigQuery specific + strict: _, // bool + copy_grants: _, // bool + enable_schema_evolution: _, // bool + change_tracking: _, // bool + data_retention_time_in_days: _, // u64, no span + max_data_extension_time_in_days: _, // u64, no span + default_ddl_collation: _, // string, no span + with_aggregation_policy: _, // todo, Snowflake specific + with_row_access_policy: _, // todo, Snowflake specific + with_tags: _, // todo, Snowflake specific + } = self; + union_spans( - core::iter::once(self.name.span()) - .chain(self.columns.iter().map(|i| i.span())) - .chain(self.constraints.iter().map(|i| i.span())) - .chain(self.table_properties.iter().map(|i| i.span())) - .chain(self.with_options.iter().map(|i| i.span())) - .chain(self.query.iter().map(|i| i.span())) - .chain(self.like.iter().map(|i| i.span())) - .chain(self.clone.iter().map(|i| i.span())), + core::iter::once(name.span()) + .chain(columns.iter().map(|i| i.span())) + .chain(constraints.iter().map(|i| i.span())) + .chain(table_properties.iter().map(|i| i.span())) + .chain(with_options.iter().map(|i| i.span())) + .chain(query.iter().map(|i| i.span())) + .chain(like.iter().map(|i| i.span())) + .chain(clone.iter().map(|i| i.span())), ) } } impl Spanned for ColumnDef { fn span(&self) -> Span { + let ColumnDef { + name, + data_type: _, // enum + collation, + options, + } = self; + union_spans( - core::iter::once(self.name.span) - .chain(self.collation.iter().map(|i| i.span())) - .chain(self.options.iter().map(|i| i.span())), + core::iter::once(name.span) + .chain(collation.iter().map(|i| i.span())) + .chain(options.iter().map(|i| i.span())), ) } } impl Spanned for ColumnOptionDef { fn span(&self) -> Span { - self.option - .span() - .union_opt(&self.name.as_ref().map(|i| i.span)) + let ColumnOptionDef { name, option } = self; + + option.span().union_opt(&name.as_ref().map(|i| i.span)) } } @@ -498,16 +613,29 @@ impl Spanned for TableConstraint { impl Spanned for CreateIndex { fn span(&self) -> Span { + let CreateIndex { + name, + table_name, + using, + columns, + unique: _, // bool + concurrently: _, // bool + if_not_exists: _, // bool + include, + nulls_distinct: _, // bool + with, + predicate, + } = self; + union_spans( - self.name - .iter() + name.iter() .map(|i| i.span()) - .chain(core::iter::once(self.table_name.span())) - .chain(self.using.iter().map(|i| i.span)) - .chain(self.columns.iter().map(|i| i.span())) - .chain(self.include.iter().map(|i| i.span)) - .chain(self.with.iter().map(|i| i.span())) - .chain(self.predicate.iter().map(|i| i.span())), + .chain(core::iter::once(table_name.span())) + .chain(using.iter().map(|i| i.span)) + .chain(columns.iter().map(|i| i.span())) + .chain(include.iter().map(|i| i.span)) + .chain(with.iter().map(|i| i.span())) + .chain(predicate.iter().map(|i| i.span())), ) } } @@ -565,6 +693,12 @@ impl Spanned for ReferentialAction { /// # missing span impl Spanned for ConstraintCharacteristics { fn span(&self) -> Span { + let ConstraintCharacteristics { + deferrable: _, // bool + initially: _, // enum + enforced: _, // bool + } = self; + Span::empty() } } @@ -608,24 +742,30 @@ impl Spanned for CopySource { impl Spanned for Delete { fn span(&self) -> Span { + let Delete { + tables, + from, + using, + selection, + returning, + order_by, + limit, + } = self; + union_spans( - self.tables + tables .iter() .map(|i| i.span()) - .chain(core::iter::once(self.from.span())) + .chain(core::iter::once(from.span())) .chain( - self.using + using .iter() .map(|u| union_spans(u.iter().map(|i| i.span()))), ) - .chain(self.selection.iter().map(|i| i.span())) - .chain( - self.returning - .iter() - .flat_map(|i| i.iter().map(|k| k.span())), - ) - .chain(self.order_by.iter().map(|i| i.span())) - .chain(self.limit.iter().map(|i| i.span())), + .chain(selection.iter().map(|i| i.span())) + .chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span()))) + .chain(order_by.iter().map(|i| i.span())) + .chain(limit.iter().map(|i| i.span())), ) } } @@ -641,9 +781,15 @@ impl Spanned for FromTable { impl Spanned for ViewColumnDef { fn span(&self) -> Span { + let ViewColumnDef { + name, + data_type: _, // todo, DataType + options, + } = self; + union_spans( - core::iter::once(self.name.span) - .chain(self.options.iter().flat_map(|i| i.iter().map(|k| k.span()))), + core::iter::once(name.span) + .chain(options.iter().flat_map(|i| i.iter().map(|k| k.span()))), ) } } @@ -683,7 +829,12 @@ impl Spanned for TableOptionsClustered { impl Spanned for ClusteredIndex { fn span(&self) -> Span { - self.name.span + let ClusteredIndex { + name, + asc: _, // bool + } = self; + + name.span } } @@ -831,23 +982,31 @@ impl Spanned for Partition { impl Spanned for ProjectionSelect { fn span(&self) -> Span { + let ProjectionSelect { + projection, + order_by, + group_by, + } = self; + union_spans( - self.projection + projection .iter() .map(|i| i.span()) - .chain(self.order_by.iter().map(|i| i.span())) - .chain(self.group_by.iter().map(|i| i.span())), + .chain(order_by.iter().map(|i| i.span())) + .chain(group_by.iter().map(|i| i.span())), ) } } impl Spanned for OrderBy { fn span(&self) -> Span { + let OrderBy { exprs, interpolate } = self; + union_spans( - self.exprs + exprs .iter() .map(|i| i.span()) - .chain(self.interpolate.iter().map(|i| i.span())), + .chain(interpolate.iter().map(|i| i.span())), ) } } @@ -869,15 +1028,17 @@ impl Spanned for GroupByExpr { impl Spanned for Interpolate { fn span(&self) -> Span { - union_spans(self.exprs.iter().flat_map(|i| i.iter().map(|e| e.span()))) + let Interpolate { exprs } = self; + + union_spans(exprs.iter().flat_map(|i| i.iter().map(|e| e.span()))) } } impl Spanned for InterpolateExpr { fn span(&self) -> Span { - self.column - .span - .union_opt(&self.expr.as_ref().map(|e| e.span())) + let InterpolateExpr { column, expr } = self; + + column.span.union_opt(&expr.as_ref().map(|e| e.span())) } } @@ -895,23 +1056,34 @@ impl Spanned for AlterIndexOperation { /// - [Insert::insert_alias] impl Spanned for Insert { fn span(&self) -> Span { + let Insert { + or: _, // enum, sqlite specific + ignore: _, // bool + into: _, // bool + table_name, + table_alias, + columns, + overwrite: _, // bool + source, + partitioned, + after_columns, + table: _, // bool + on, + returning, + replace_into: _, // bool + priority: _, // todo, mysql specific + insert_alias: _, // todo, mysql specific + } = self; + union_spans( - core::iter::once(self.table_name.span()) - .chain(self.table_alias.as_ref().map(|i| i.span)) - .chain(self.columns.iter().map(|i| i.span)) - .chain(self.source.as_ref().map(|q| q.span())) - .chain( - self.partitioned - .iter() - .flat_map(|i| i.iter().map(|k| k.span())), - ) - .chain(self.after_columns.iter().map(|i| i.span)) - .chain(self.on.as_ref().map(|i| i.span())) - .chain( - self.returning - .iter() - .flat_map(|i| i.iter().map(|k| k.span())), - ), + core::iter::once(table_name.span()) + .chain(table_alias.as_ref().map(|i| i.span)) + .chain(columns.iter().map(|i| i.span)) + .chain(source.as_ref().map(|q| q.span())) + .chain(partitioned.iter().flat_map(|i| i.iter().map(|k| k.span()))) + .chain(after_columns.iter().map(|i| i.span)) + .chain(on.as_ref().map(|i| i.span())) + .chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span()))), ) } } @@ -927,9 +1099,14 @@ impl Spanned for OnInsert { impl Spanned for OnConflict { fn span(&self) -> Span { - self.action + let OnConflict { + conflict_target, + action, + } = self; + + action .span() - .union_opt(&self.conflict_target.as_ref().map(|i| i.span())) + .union_opt(&conflict_target.as_ref().map(|i| i.span())) } } @@ -957,18 +1134,25 @@ impl Spanned for OnConflictAction { impl Spanned for DoUpdate { fn span(&self) -> Span { + let DoUpdate { + assignments, + selection, + } = self; + union_spans( - self.assignments + assignments .iter() .map(|i| i.span()) - .chain(self.selection.iter().map(|i| i.span())), + .chain(selection.iter().map(|i| i.span())), ) } } impl Spanned for Assignment { fn span(&self) -> Span { - self.target.span().union(&self.value.span()) + let Assignment { target, value } = self; + + target.span().union(&value.span()) } } @@ -1209,24 +1393,43 @@ impl Spanned for Subscript { impl Spanned for ObjectName { fn span(&self) -> Span { - union_spans(self.0.iter().map(|i| i.span)) + let ObjectName(segments) = self; + + union_spans(segments.iter().map(|i| i.span)) } } impl Spanned for Array { fn span(&self) -> Span { - union_spans(self.elem.iter().map(|i| i.span())) + let Array { + elem, + named: _, // bool + } = self; + + union_spans(elem.iter().map(|i| i.span())) } } impl Spanned for Function { fn span(&self) -> Span { + let Function { + name, + parameters, + args, + filter, + null_treatment: _, // enum + over: _, // todo + within_group, + } = self; + union_spans( - self.name - .0 + name.0 .iter() .map(|i| i.span) - .chain(iter::once(self.args.span())), + .chain(iter::once(args.span())) + .chain(iter::once(parameters.span())) + .chain(filter.iter().map(|i| i.span())) + .chain(within_group.iter().map(|i| i.span())), ) } } @@ -1246,12 +1449,17 @@ impl Spanned for FunctionArguments { impl Spanned for FunctionArgumentList { fn span(&self) -> Span { + let FunctionArgumentList { + duplicate_treatment: _, // enum + args, + clauses, + } = self; + union_spans( // # todo: duplicate-treatment span - self.args - .iter() + args.iter() .map(|i| i.span()) - .chain(self.clauses.iter().map(|i| i.span())), + .chain(clauses.iter().map(|i| i.span())), ) } } @@ -1274,7 +1482,9 @@ impl Spanned for FunctionArgumentClause { /// see Spanned impl for JsonPathElem for more information impl Spanned for JsonPath { fn span(&self) -> Span { - union_spans(self.path.iter().map(|i| i.span())) + let JsonPath { path } = self; + + union_spans(path.iter().map(|i| i.span())) } } @@ -1310,13 +1520,22 @@ impl Spanned for SelectItem { impl Spanned for WildcardAdditionalOptions { fn span(&self) -> Span { + let WildcardAdditionalOptions { + wildcard_token, + opt_ilike, + opt_exclude, + opt_except, + opt_replace, + opt_rename, + } = self; + union_spans( - core::iter::once(self.wildcard_token.span) - .chain(self.opt_ilike.as_ref().map(|i| i.span())) - .chain(self.opt_exclude.as_ref().map(|i| i.span())) - .chain(self.opt_rename.as_ref().map(|i| i.span())) - .chain(self.opt_replace.as_ref().map(|i| i.span())) - .chain(self.opt_except.as_ref().map(|i| i.span())), + core::iter::once(wildcard_token.span) + .chain(opt_ilike.as_ref().map(|i| i.span())) + .chain(opt_exclude.as_ref().map(|i| i.span())) + .chain(opt_rename.as_ref().map(|i| i.span())) + .chain(opt_replace.as_ref().map(|i| i.span())) + .chain(opt_except.as_ref().map(|i| i.span())), ) } } @@ -1350,22 +1569,34 @@ impl Spanned for RenameSelectItem { impl Spanned for ExceptSelectItem { fn span(&self) -> Span { + let ExceptSelectItem { + first_element, + additional_elements, + } = self; + union_spans( - iter::once(self.first_element.span) - .chain(self.additional_elements.iter().map(|i| i.span)), + iter::once(first_element.span).chain(additional_elements.iter().map(|i| i.span)), ) } } impl Spanned for ReplaceSelectItem { fn span(&self) -> Span { - union_spans(self.items.iter().map(|i| i.span())) + let ReplaceSelectItem { items } = self; + + union_spans(items.iter().map(|i| i.span())) } } impl Spanned for ReplaceSelectElement { fn span(&self) -> Span { - self.expr.span().union(&self.column_name.span) + let ReplaceSelectElement { + expr, + column_name, + as_keyword: _, // bool + } = self; + + expr.span().union(&column_name.span) } } @@ -1499,9 +1730,9 @@ impl Spanned for PivotValueSource { impl Spanned for ExprWithAlias { fn span(&self) -> Span { - self.expr - .span() - .union_opt(&self.alias.as_ref().map(|i| i.span)) + let ExprWithAlias { expr, alias } = self; + + expr.span().union_opt(&alias.as_ref().map(|i| i.span)) } } @@ -1514,32 +1745,42 @@ impl Spanned for MatchRecognizePattern { impl Spanned for SymbolDefinition { fn span(&self) -> Span { - self.symbol.span.union(&self.definition.span()) + let SymbolDefinition { symbol, definition } = self; + + symbol.span.union(&definition.span()) } } impl Spanned for Measure { fn span(&self) -> Span { - self.expr.span().union(&self.alias.span) + let Measure { expr, alias } = self; + + expr.span().union(&alias.span) } } impl Spanned for OrderByExpr { fn span(&self) -> Span { - self.expr - .span() - .union_opt(&self.with_fill.as_ref().map(|f| f.span())) + let OrderByExpr { + expr, + asc: _, // bool + nulls_first: _, // bool + with_fill, + } = self; + + expr.span().union_opt(&with_fill.as_ref().map(|f| f.span())) } } impl Spanned for WithFill { fn span(&self) -> Span { + let WithFill { from, to, step } = self; + union_spans( - self.from - .iter() + from.iter() .map(|f| f.span()) - .chain(self.to.iter().map(|t| t.span())) - .chain(self.step.iter().map(|s| s.span())), + .chain(to.iter().map(|t| t.span())) + .chain(step.iter().map(|s| s.span())), ) } } @@ -1575,7 +1816,9 @@ impl Spanned for FunctionArgExpr { impl Spanned for TableAlias { fn span(&self) -> Span { - union_spans(iter::once(self.name.span).chain(self.columns.iter().map(|i| i.span))) + let TableAlias { name, columns } = self; + + union_spans(iter::once(name.span).chain(columns.iter().map(|i| i.span))) } } @@ -1591,7 +1834,13 @@ impl Spanned for Value { impl Spanned for Join { fn span(&self) -> Span { - self.relation.span().union(&self.join_operator.span()) + let Join { + relation, + global: _, // bool + join_operator, + } = self; + + relation.span().union(&join_operator.span()) } } @@ -1641,22 +1890,110 @@ impl Spanned for JoinConstraint { impl Spanned for TableWithJoins { fn span(&self) -> Span { + let TableWithJoins { relation, joins } = self; + + union_spans(core::iter::once(relation.span()).chain(joins.iter().map(|item| item.span()))) + } +} + +impl Spanned for Select { + fn span(&self) -> Span { + let Select { + select_token, + distinct: _, // todo + top: _, // todo, mysql specific + projection, + into, + from, + lateral_views, + prewhere, + selection, + group_by, + cluster_by, + distribute_by, + sort_by, + having, + named_window, + qualify, + window_before_qualify: _, // bool + value_table_mode: _, // todo, BigQuery specific + connect_by, + } = self; + union_spans( - core::iter::once(self.relation.span()).chain(self.joins.iter().map(|item| item.span())), + core::iter::once(select_token.span) + .chain(projection.iter().map(|item| item.span())) + .chain(into.iter().map(|item| item.span())) + .chain(from.iter().map(|item| item.span())) + .chain(lateral_views.iter().map(|item| item.span())) + .chain(prewhere.iter().map(|item| item.span())) + .chain(selection.iter().map(|item| item.span())) + .chain(core::iter::once(group_by.span())) + .chain(cluster_by.iter().map(|item| item.span())) + .chain(distribute_by.iter().map(|item| item.span())) + .chain(sort_by.iter().map(|item| item.span())) + .chain(having.iter().map(|item| item.span())) + .chain(named_window.iter().map(|item| item.span())) + .chain(qualify.iter().map(|item| item.span())) + .chain(connect_by.iter().map(|item| item.span())), ) } } -impl Spanned for Select { +impl Spanned for ConnectBy { + fn span(&self) -> Span { + let ConnectBy { + condition, + relationships, + } = self; + + union_spans( + core::iter::once(condition.span()).chain(relationships.iter().map(|item| item.span())), + ) + } +} + +impl Spanned for NamedWindowDefinition { fn span(&self) -> Span { + let NamedWindowDefinition( + ident, + _, // todo: NamedWindowExpr + ) = self; + + ident.span + } +} + +impl Spanned for LateralView { + fn span(&self) -> Span { + let LateralView { + lateral_view, + lateral_view_name, + lateral_col_alias, + outer: _, // bool + } = self; + union_spans( - core::iter::once(self.select_token.span) - .chain(self.projection.iter().map(|item| item.span())) - .chain(self.from.iter().map(|item| item.span())), + core::iter::once(lateral_view.span()) + .chain(core::iter::once(lateral_view_name.span())) + .chain(lateral_col_alias.iter().map(|i| i.span)), ) } } +impl Spanned for SelectInto { + fn span(&self) -> Span { + let SelectInto { + temporary: _, // bool + unlogged: _, // bool + table: _, // bool + name, + } = self; + + name.span() + } +} + #[cfg(test)] pub mod tests { use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect}; From d76d1e0ef49d5b4b3ba1910dea6a0d83bf36372d Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Fri, 18 Oct 2024 11:24:55 +0200 Subject: [PATCH 22/32] for_clause is mssql, not mysql --- src/ast/spans.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 6b01fbaac..13f1ba3ff 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -50,7 +50,7 @@ impl Spanned for Query { offset, fetch, locks: _, // todo - for_clause: _, // todo, mysql specific + for_clause: _, // todo, mssql specific settings: _, // todo, clickhouse specific format_clause: _, // todo, clickhouse specific } = self; From 71c27ea001741bcc729cf3f743364e7c9704b638 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Fri, 25 Oct 2024 13:17:08 +0200 Subject: [PATCH 23/32] cargo fmt --- src/ast/spans.rs | 10 +++++----- src/parser/mod.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 13f1ba3ff..3ded54ab2 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -11,11 +11,11 @@ use super::{ FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, IlikeSelectItem, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, MatchRecognizePattern, Measure, NamedWindowDefinition, - ObjectName, Offset, OnConflict, OnConflictAction, OnInsert, OrderBy, - OrderByExpr, Partition, PivotValueSource, ProjectionSelect, Query, ReferentialAction, - RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, - SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableConstraint, - TableFactor, TableOptionsClustered, TableWithJoins, Use, Value, Values, ViewColumnDef, + ObjectName, Offset, OnConflict, OnConflictAction, OnInsert, OrderBy, OrderByExpr, Partition, + PivotValueSource, ProjectionSelect, Query, ReferentialAction, RenameSelectItem, + ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, + Statement, Subscript, SymbolDefinition, TableAlias, TableConstraint, TableFactor, + TableOptionsClustered, TableWithJoins, Use, Value, Values, ViewColumnDef, WildcardAdditionalOptions, With, WithFill, }; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c9aa7d8d7..433f9957b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9040,10 +9040,10 @@ impl<'a> Parser<'a> { } } self.expect_token(&Token::LParen)?; - + let query = self.parse_query()?; let closing_paren_token = self.expect_token(&Token::RParen)?; - + let alias = TableAlias { name, columns: vec![], From a31c6a6ccaed885d823de95177c847f095ade247 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 12 Nov 2024 11:04:46 +0100 Subject: [PATCH 24/32] lint & no-std --- src/ast/mod.rs | 7 +++++-- src/parser/mod.rs | 5 +---- src/tokenizer.rs | 19 +++++++++++-------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 5d765eb38..2784a8ea8 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -24,8 +24,11 @@ use alloc::{ vec::Vec, }; -use core::fmt::{self, Display}; use core::ops::Deref; +use core::{ + fmt::{self, Display}, + hash, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -163,7 +166,7 @@ impl PartialEq for Ident { } impl core::hash::Hash for Ident { - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { let Ident { value, quote_style, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index aae4bde63..a7f2b5f66 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3270,10 +3270,7 @@ impl<'a> Parser<'a> { #[must_use] pub fn peek_keyword(&mut self, expected: Keyword) -> bool { - match self.peek_token().token { - Token::Word(w) if expected == w.keyword => true, - _ => false, - } + matches!(self.peek_token().token, Token::Word(w) if expected == w.keyword) } /// If the current token is the `expected` keyword followed by diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 7fe48d824..64ff992e6 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -29,10 +29,10 @@ use alloc::{ vec, vec::Vec, }; -use core::iter::Peekable; use core::num::NonZeroU8; use core::str::Chars; use core::{cmp, fmt}; +use core::{hash, iter::Peekable}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -424,6 +424,7 @@ impl fmt::Display for Whitespace { /// Location in input string #[derive(Eq, PartialEq, Hash, Clone, Copy, Ord, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct Location { /// Line number, starting from 1 pub line: u64, @@ -431,7 +432,7 @@ pub struct Location { pub column: u64, } -impl std::fmt::Display for Location { +impl fmt::Display for Location { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.line == 0 { return Ok(()); @@ -440,7 +441,7 @@ impl std::fmt::Display for Location { } } -impl std::fmt::Debug for Location { +impl fmt::Debug for Location { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Location({},{})", self.line, self.column) } @@ -465,12 +466,13 @@ impl From<(u64, u64)> for Location { /// A span of source code locations (start, end) #[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct Span { pub start: Location, pub end: Location, } -impl std::fmt::Debug for Span { +impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Span({:?}..{:?})", self.start, self.end) } @@ -522,6 +524,7 @@ impl Span { /// A [Token] with [Location] attached to it #[derive(Debug, Eq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct TokenWithLocation { pub token: Token, pub span: Span, @@ -542,7 +545,7 @@ impl TokenWithLocation { } impl core::hash::Hash for TokenWithLocation { - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { let TokenWithLocation { token, span: _ } = self; token.hash(state); @@ -558,13 +561,13 @@ impl PartialEq for TokenWithLocation { } impl PartialOrd for TokenWithLocation { - fn partial_cmp(&self, other: &TokenWithLocation) -> Option { + fn partial_cmp(&self, other: &TokenWithLocation) -> Option { Some(self.cmp(other)) } } impl Ord for TokenWithLocation { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> cmp::Ordering { let TokenWithLocation { token, span: _ } = self; token.cmp(&other.token) } @@ -2799,7 +2802,7 @@ mod tests { compare(expected, tokens); } - fn compare(expected: Vec, actual: Vec) { + fn compare(expected: Vec, actual: Vec) { //println!("------------------------------"); //println!("tokens = {:?}", actual); //println!("expected = {:?}", expected); From ce8b35cf00681fc05f82bb06c32993de0eeda54c Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 12 Nov 2024 12:14:19 +0100 Subject: [PATCH 25/32] add IgnoreField helper --- src/ast/helpers/ignore_field.rs | 69 +++++++++++++++++++++++++++++++++ src/ast/helpers/mod.rs | 1 + src/ast/query.rs | 3 +- src/ast/spans.rs | 2 +- src/parser/mod.rs | 3 +- tests/sqlparser_clickhouse.rs | 2 +- tests/sqlparser_common.rs | 18 +++++---- tests/sqlparser_duckdb.rs | 4 +- tests/sqlparser_mssql.rs | 4 +- tests/sqlparser_mysql.rs | 17 ++++---- tests/sqlparser_postgres.rs | 6 +-- 11 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 src/ast/helpers/ignore_field.rs diff --git a/src/ast/helpers/ignore_field.rs b/src/ast/helpers/ignore_field.rs new file mode 100644 index 000000000..36aa9a860 --- /dev/null +++ b/src/ast/helpers/ignore_field.rs @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; +use core::fmt::{self, Debug, Formatter}; +use core::hash::{Hash, Hasher}; + +/// A wrapper type that ignores the field when comparing or hashing. +pub struct IgnoreField(pub T); + +// Conditional Implementations +impl Debug for IgnoreField { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Clone for IgnoreField { + fn clone(&self) -> Self { + IgnoreField(self.0.clone()) + } +} + +// Blanket Implementations +impl PartialEq for IgnoreField { + fn eq(&self, _: &Self) -> bool { + true + } +} + +impl Eq for IgnoreField {} + +impl PartialOrd for IgnoreField { + fn partial_cmp(&self, _: &Self) -> Option { + Some(Ordering::Equal) + } +} + +impl Ord for IgnoreField { + fn cmp(&self, _: &Self) -> Ordering { + Ordering::Equal + } +} + +impl Hash for IgnoreField { + fn hash(&self, _state: &mut H) { + // Do nothing + } +} + +impl From for IgnoreField { + fn from(value: T) -> Self { + IgnoreField(value) + } +} diff --git a/src/ast/helpers/mod.rs b/src/ast/helpers/mod.rs index d6924ab88..c7e382fb5 100644 --- a/src/ast/helpers/mod.rs +++ b/src/ast/helpers/mod.rs @@ -14,5 +14,6 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +pub mod ignore_field; pub mod stmt_create_table; pub mod stmt_data_loading; diff --git a/src/ast/query.rs b/src/ast/query.rs index 23a3cb3b9..40c970ffd 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -18,6 +18,7 @@ #[cfg(not(feature = "std"))] use alloc::{boxed::Box, vec::Vec}; +use helpers::ignore_field::IgnoreField; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -280,7 +281,7 @@ impl fmt::Display for Table { #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct Select { /// SELECT - pub select_token: TokenWithLocation, + pub select_token: IgnoreField, pub distinct: Option, /// MSSQL syntax: `TOP () [ PERCENT ] [ WITH TIES ]` pub top: Option, diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 1bf08fa26..c70f073a8 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1939,7 +1939,7 @@ impl Spanned for Select { } = self; union_spans( - core::iter::once(select_token.span) + core::iter::once(select_token.0.span) .chain(projection.iter().map(|item| item.span())) .chain(into.iter().map(|item| item.span())) .chain(from.iter().map(|item| item.span())) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a7f2b5f66..8c08d6cf9 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -24,6 +24,7 @@ use core::{ fmt::{self, Display}, str::FromStr, }; +use helpers::ignore_field::IgnoreField; use log::debug; @@ -9369,7 +9370,7 @@ impl<'a> Parser<'a> { }; Ok(Select { - select_token, + select_token: IgnoreField(select_token), distinct, top, top_before_distinct, diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index d94f53941..b0e14dcec 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -40,7 +40,7 @@ fn parse_map_access_expr() { assert_eq!( Select { distinct: None, - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), top: None, top_before_distinct: false, projection: vec![UnnamedExpr(MapAccess { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 15705e60c..ea78e143d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -378,7 +378,8 @@ fn parse_update_set_from() { subquery: Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")) + .into(), distinct: None, top: None, top_before_distinct: false, @@ -4660,7 +4661,7 @@ fn test_parse_named_window() { ORDER BY C3"; let actual_select_only = verified_only_select(sql); let expected = Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECTI")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECTI")).into(), distinct: None, top: None, top_before_distinct: false, @@ -5316,7 +5317,7 @@ fn parse_interval_and_or_xor() { let expected_ast = vec![Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, @@ -7406,7 +7407,7 @@ fn lateral_function() { let sql = "SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id)"; let actual_select_only = verified_only_select(sql); let expected = Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], @@ -8253,7 +8254,8 @@ fn parse_merge() { subquery: Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")) + .into(), distinct: None, top: None, top_before_distinct: false, @@ -9892,7 +9894,7 @@ fn parse_unload() { Statement::Unload { query: Box::new(Query { body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, @@ -10071,7 +10073,7 @@ fn parse_map_access_expr() { #[test] fn parse_connect_by() { let expect_query = Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, @@ -10159,7 +10161,7 @@ fn parse_connect_by() { assert_eq!( all_dialects_where(|d| d.supports_connect_by()).verified_only_select(connect_by_3), Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 58e8bfbfc..7c3302e75 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -260,7 +260,7 @@ fn test_select_union_by_name() { op: SetOperator::Union, set_quantifier: *expected_quantifier, left: Box::::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], @@ -297,7 +297,7 @@ fn test_select_union_by_name() { connect_by: None, }))), right: Box::::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 63feb06eb..e0429c39f 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -113,7 +113,7 @@ fn parse_create_procedure() { settings: None, format_clause: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, @@ -525,7 +525,7 @@ fn parse_substring_in_select() { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: Some(Distinct::Distinct), top: None, top_before_distinct: false, diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index c15852eb9..a18f970e8 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -966,7 +966,7 @@ fn parse_escaped_quote_identifiers_with_escape() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, @@ -1019,7 +1019,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, @@ -1066,7 +1066,7 @@ fn parse_escaped_backticks_with_escape() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, @@ -1117,7 +1117,7 @@ fn parse_escaped_backticks_with_no_escape() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, @@ -1765,7 +1765,7 @@ fn parse_select_with_numeric_prefix_column_name() { assert_eq!( q.body, Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, @@ -1822,7 +1822,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() { assert_eq!( q.body, Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, @@ -2334,7 +2334,7 @@ fn parse_substring_in_select() { Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: Some(Distinct::Distinct), top: None, top_before_distinct: false, @@ -2659,8 +2659,7 @@ fn parse_hex_string_introducer() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), - + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index daf28a546..a6d456ad3 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1164,7 +1164,7 @@ fn parse_copy_to() { source: CopySource::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, @@ -2520,7 +2520,7 @@ fn parse_array_subquery_expr() { op: SetOperator::Union, set_quantifier: SetQuantifier::None, left: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, @@ -2542,7 +2542,7 @@ fn parse_array_subquery_expr() { connect_by: None, }))), right: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")), + select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), distinct: None, top: None, top_before_distinct: false, From 2bb72a424529930fca0a31174f045cb424d089c4 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 12 Nov 2024 14:50:19 +0100 Subject: [PATCH 26/32] docs and fixes --- README.md | 17 +++++++++++++++ docs/source_spans.md | 52 ++++++++++++++++++++++++++++++++++++++++++++ src/ast/query.rs | 8 +++---- src/ast/spans.rs | 6 ++--- src/parser/mod.rs | 8 +++---- 5 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 docs/source_spans.md diff --git a/README.md b/README.md index 934d9d06d..972a658a7 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,23 @@ similar semantics are represented with the same AST. We welcome PRs to fix such issues and distinguish different syntaxes in the AST. +## WIP: Extracting source locations from AST nodes + +This crate allows recovering source locations from AST nodes via the [Spanned](https://docs.rs/sqlparser/latest/sqlparser/ast/trait.Spanned.html) trait, which can be used for advanced diagnostics tooling. Node that this feature is a work in progress and many nodes report missing or inaccurate spans. Please see [this document](./docs/source_spans.md#source-span-contributing-guidelines) for information on how to contribute missing improvements. + +```rust +use sqlparser::ast::Spanned; + +// Parse SQL +let ast = Parser::parse_sql(&GenericDialect, "SELECT A FROM B").unwrap(); + +// The source span can be retrieved with start and end locations +assert_eq!(ast[0].span(), Span { + start: Location::of(1, 1), + end: Location::of(1, 16), +}); +``` + ## SQL compliance SQL was first standardized in 1987, and revisions of the standard have been diff --git a/docs/source_spans.md b/docs/source_spans.md new file mode 100644 index 000000000..d8b5497ad --- /dev/null +++ b/docs/source_spans.md @@ -0,0 +1,52 @@ + +## Breaking Changes + +These are the current breaking changes introduced by the source spans feature: + +#### Added fields for spans (must be added to any existing pattern matches) +- `Ident` now stores a `Span` +- `Select`, `With`, `Cte`, `WildcardAdditionalOptions` now store a `TokenWithLocation` + +#### Misc. +- `TokenWithLocation` stores a full `Span`, rathern than just a source location. Users relying on `token.location` should use `token.location.start` instead. +## Source Span Contributing Guidelines + +For contributing source spans improvement in addition to the general [contribution guidelines](../README.md#contributing), please make sure to pay attention to the following: + + +### Source Span Design Considerations + +- `Ident` always have correct source spans +- Downstream breaking change impact is to be as minimal as possible +- To this end, use recursive merging of spans in favor of storing spans on all nodes +- Any metadata added to compute spans must not change semantics (Eq, Ord, Hash, etc.) + +The primary reason for missing and inaccurate source spans at this time is missing spans of keyword tokens and values in many structures, either due to lack of time or because adding them would break downstream significantly. + +When considering adding support for source spans on a type, consider the impact to consumers of that type and whether your change would require a consumer to do non-trivial changes to their code. + +f.e. of a trivial change +```rust +match node { + ast::Query { + field1, + field2, + location: _, // add a new line to ignored location +} +``` + +If adding source spans to a type would require a significant change like wrapping that type or similar, please open an issue to discuss. + +### AST Node Equality and Hashes + +When adding tokens to AST nodes, make sure to wrap them in the [IgnoreField](https://docs.rs/sqlparser/latest/sqlparser/ast/helpers/struct.IgnoreField.html) helper to ensure that semantically equivalent AST nodes always compare as equal and hash to the same value. F.e. `select 5` and `SELECT 5` would compare as different `Select` nodes, if the select token was stored directly. f.e. + +```rust +struct Select { + select_token: IgnoreField, // only used for spans + /// remaining fields + field1, + field2, + ... +} +``` \ No newline at end of file diff --git a/src/ast/query.rs b/src/ast/query.rs index 40c970ffd..df9da2fdf 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -511,7 +511,7 @@ impl fmt::Display for NamedWindowDefinition { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct With { - pub with_token: TokenWithLocation, + pub with_token: IgnoreField, pub recursive: bool, pub cte_tables: Vec, } @@ -564,7 +564,7 @@ pub struct Cte { pub from: Option, pub materialized: Option, // needed for accurate span reporting - pub closing_paren_token: TokenWithLocation, + pub closing_paren_token: IgnoreField, } impl fmt::Display for Cte { @@ -620,7 +620,7 @@ impl fmt::Display for IdentWithAlias { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct WildcardAdditionalOptions { - pub wildcard_token: TokenWithLocation, + pub wildcard_token: IgnoreField, /// `[ILIKE...]`. /// Snowflake syntax: pub opt_ilike: Option, @@ -641,7 +641,7 @@ pub struct WildcardAdditionalOptions { impl Default for WildcardAdditionalOptions { fn default() -> Self { Self { - wildcard_token: TokenWithLocation::wrap(Token::Mul), + wildcard_token: TokenWithLocation::wrap(Token::Mul).into(), opt_ilike: None, opt_exclude: None, opt_except: None, diff --git a/src/ast/spans.rs b/src/ast/spans.rs index c70f073a8..8a7e7916b 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -100,7 +100,7 @@ impl Spanned for With { } = self; union_spans( - core::iter::once(with_token.span).chain(cte_tables.iter().map(|item| item.span())), + core::iter::once(with_token.0.span).chain(cte_tables.iter().map(|item| item.span())), ) } } @@ -119,7 +119,7 @@ impl Spanned for Cte { core::iter::once(alias.span()) .chain(core::iter::once(query.span())) .chain(from.iter().map(|item| item.span)) - .chain(core::iter::once(closing_paren_token.span)), + .chain(core::iter::once(closing_paren_token.0.span)), ) } } @@ -1547,7 +1547,7 @@ impl Spanned for WildcardAdditionalOptions { } = self; union_spans( - core::iter::once(wildcard_token.span) + core::iter::once(wildcard_token.0.span) .chain(opt_ilike.as_ref().map(|i| i.span())) .chain(opt_exclude.as_ref().map(|i| i.span())) .chain(opt_rename.as_ref().map(|i| i.span())) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 8c08d6cf9..121cd92ff 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8799,7 +8799,7 @@ impl<'a> Parser<'a> { let _guard = self.recursion_counter.try_decrease()?; let with = if let Some(with_token) = self.parse_keyword_token(Keyword::WITH) { Some(With { - with_token, + with_token: with_token.into(), recursive: self.parse_keyword(Keyword::RECURSIVE), cte_tables: self.parse_comma_separated(Parser::parse_cte)?, }) @@ -9070,7 +9070,7 @@ impl<'a> Parser<'a> { query, from: None, materialized: is_materialized, - closing_paren_token, + closing_paren_token: closing_paren_token.into(), } } else { let columns = self.parse_parenthesized_column_list(Optional, false)?; @@ -9094,7 +9094,7 @@ impl<'a> Parser<'a> { query, from: None, materialized: is_materialized, - closing_paren_token, + closing_paren_token: closing_paren_token.into(), } }; if self.parse_keyword(Keyword::FROM) { @@ -11365,7 +11365,7 @@ impl<'a> Parser<'a> { }; Ok(WildcardAdditionalOptions { - wildcard_token, + wildcard_token: wildcard_token.into(), opt_ilike, opt_exclude, opt_except, From 4dda1fed416d83256ac08f452b8556cc5a3a952b Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 12 Nov 2024 14:55:34 +0100 Subject: [PATCH 27/32] fix: test failing --- tests/sqlparser_common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index ea78e143d..8bb7f3c65 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -6444,7 +6444,7 @@ fn parse_recursive_cte() { query: Box::new(cte_query), from: None, materialized: None, - closing_paren_token: TokenWithLocation::wrap(Token::RParen), + closing_paren_token: TokenWithLocation::wrap(Token::RParen).into(), }; assert_eq!(with.cte_tables.first().unwrap(), &expected); } From 3fa3766e8e513f2fafe3e2eb54791da8eae1b2f2 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Mon, 18 Nov 2024 09:28:23 +0100 Subject: [PATCH 28/32] pr feedback --- src/ast/helpers/ignore_field.rs | 37 ++++++++++++++++----------------- src/ast/mod.rs | 4 ++-- src/ast/query.rs | 16 +++++++------- src/parser/mod.rs | 4 ++-- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/ast/helpers/ignore_field.rs b/src/ast/helpers/ignore_field.rs index 36aa9a860..4c4f6c59e 100644 --- a/src/ast/helpers/ignore_field.rs +++ b/src/ast/helpers/ignore_field.rs @@ -19,51 +19,50 @@ use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; use core::fmt::{self, Debug, Formatter}; use core::hash::{Hash, Hasher}; -/// A wrapper type that ignores the field when comparing or hashing. -pub struct IgnoreField(pub T); +use crate::tokenizer::TokenWithLocation; + +/// A wrapper type for attaching tokens to AST nodes that should be ignored in comparisons and hashing. +/// This should be used when a token is not relevant for semantics, but is still needed for +/// accurate source location tracking. +#[derive(Clone)] +pub struct AttachedToken(pub TokenWithLocation); // Conditional Implementations -impl Debug for IgnoreField { +impl Debug for AttachedToken { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } -impl Clone for IgnoreField { - fn clone(&self) -> Self { - IgnoreField(self.0.clone()) - } -} - // Blanket Implementations -impl PartialEq for IgnoreField { +impl PartialEq for AttachedToken { fn eq(&self, _: &Self) -> bool { true } } -impl Eq for IgnoreField {} +impl Eq for AttachedToken {} -impl PartialOrd for IgnoreField { - fn partial_cmp(&self, _: &Self) -> Option { - Some(Ordering::Equal) +impl PartialOrd for AttachedToken { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } -impl Ord for IgnoreField { +impl Ord for AttachedToken { fn cmp(&self, _: &Self) -> Ordering { Ordering::Equal } } -impl Hash for IgnoreField { +impl Hash for AttachedToken { fn hash(&self, _state: &mut H) { // Do nothing } } -impl From for IgnoreField { - fn from(value: T) -> Self { - IgnoreField(value) +impl From for AttachedToken { + fn from(value: TokenWithLocation) -> Self { + AttachedToken(value) } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 2784a8ea8..a59bc0dd2 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -157,7 +157,7 @@ impl PartialEq for Ident { let Ident { value, quote_style, - // backwards compat + // exhaustiveness check; we ignore spans in comparisons span: _, } = self; @@ -170,7 +170,7 @@ impl core::hash::Hash for Ident { let Ident { value, quote_style, - // backwards compat + // exhaustiveness check; we ignore spans in hashes span: _, } = self; diff --git a/src/ast/query.rs b/src/ast/query.rs index df9da2fdf..a7ccf95bd 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -18,7 +18,7 @@ #[cfg(not(feature = "std"))] use alloc::{boxed::Box, vec::Vec}; -use helpers::ignore_field::IgnoreField; +use helpers::ignore_field::AttachedToken; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -280,8 +280,8 @@ impl fmt::Display for Table { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct Select { - /// SELECT - pub select_token: IgnoreField, + /// Token for the `SELECT` keyword + pub select_token: AttachedToken, pub distinct: Option, /// MSSQL syntax: `TOP () [ PERCENT ] [ WITH TIES ]` pub top: Option, @@ -511,7 +511,8 @@ impl fmt::Display for NamedWindowDefinition { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct With { - pub with_token: IgnoreField, + // Token for the "WITH" keyword + pub with_token: AttachedToken, pub recursive: bool, pub cte_tables: Vec, } @@ -563,8 +564,8 @@ pub struct Cte { pub query: Box, pub from: Option, pub materialized: Option, - // needed for accurate span reporting - pub closing_paren_token: IgnoreField, + // Token for the closing parenthesis + pub closing_paren_token: AttachedToken, } impl fmt::Display for Cte { @@ -620,7 +621,8 @@ impl fmt::Display for IdentWithAlias { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct WildcardAdditionalOptions { - pub wildcard_token: IgnoreField, + /// The wildcard token `*` + pub wildcard_token: AttachedToken, /// `[ILIKE...]`. /// Snowflake syntax: pub opt_ilike: Option, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 121cd92ff..c3ef640ab 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -24,7 +24,7 @@ use core::{ fmt::{self, Display}, str::FromStr, }; -use helpers::ignore_field::IgnoreField; +use helpers::ignore_field::AttachedToken; use log::debug; @@ -9370,7 +9370,7 @@ impl<'a> Parser<'a> { }; Ok(Select { - select_token: IgnoreField(select_token), + select_token: AttachedToken(select_token), distinct, top, top_before_distinct, From 6bfe13f227ae079147f2d2a9c84a81a0779e0ab6 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Mon, 18 Nov 2024 09:30:03 +0100 Subject: [PATCH 29/32] rename ignore_field.rs -> attached_token.rs --- src/ast/helpers/{ignore_field.rs => attached_token.rs} | 0 src/ast/helpers/mod.rs | 2 +- src/ast/query.rs | 2 +- src/parser/mod.rs | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/ast/helpers/{ignore_field.rs => attached_token.rs} (100%) diff --git a/src/ast/helpers/ignore_field.rs b/src/ast/helpers/attached_token.rs similarity index 100% rename from src/ast/helpers/ignore_field.rs rename to src/ast/helpers/attached_token.rs diff --git a/src/ast/helpers/mod.rs b/src/ast/helpers/mod.rs index c7e382fb5..a96bffc51 100644 --- a/src/ast/helpers/mod.rs +++ b/src/ast/helpers/mod.rs @@ -14,6 +14,6 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -pub mod ignore_field; +pub mod attached_token; pub mod stmt_create_table; pub mod stmt_data_loading; diff --git a/src/ast/query.rs b/src/ast/query.rs index a7ccf95bd..c1a0797a2 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -18,7 +18,7 @@ #[cfg(not(feature = "std"))] use alloc::{boxed::Box, vec::Vec}; -use helpers::ignore_field::AttachedToken; +use helpers::attached_token::AttachedToken; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c3ef640ab..00ac175bd 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -24,7 +24,7 @@ use core::{ fmt::{self, Display}, str::FromStr, }; -use helpers::ignore_field::AttachedToken; +use helpers::attached_token::AttachedToken; use log::debug; From 903f24a7799e490503314b4afddd6421fd341420 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 19 Nov 2024 10:57:24 +0100 Subject: [PATCH 30/32] pr feedback --- README.md | 2 +- docs/source_spans.md | 8 ++++---- src/ast/helpers/attached_token.rs | 8 ++++++++ src/ast/mod.rs | 7 ++++--- src/ast/spans.rs | 4 ++-- src/parser/mod.rs | 13 ++++++------ src/tokenizer.rs | 33 ++----------------------------- 7 files changed, 27 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 972a658a7..49373cf22 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ issues and distinguish different syntaxes in the AST. ## WIP: Extracting source locations from AST nodes -This crate allows recovering source locations from AST nodes via the [Spanned](https://docs.rs/sqlparser/latest/sqlparser/ast/trait.Spanned.html) trait, which can be used for advanced diagnostics tooling. Node that this feature is a work in progress and many nodes report missing or inaccurate spans. Please see [this document](./docs/source_spans.md#source-span-contributing-guidelines) for information on how to contribute missing improvements. +This crate allows recovering source locations from AST nodes via the [Spanned](https://docs.rs/sqlparser/latest/sqlparser/ast/trait.Spanned.html) trait, which can be used for advanced diagnostics tooling. Note that this feature is a work in progress and many nodes report missing or inaccurate spans. Please see [this document](./docs/source_spans.md#source-span-contributing-guidelines) for information on how to contribute missing improvements. ```rust use sqlparser::ast::Spanned; diff --git a/docs/source_spans.md b/docs/source_spans.md index d8b5497ad..136a4ced2 100644 --- a/docs/source_spans.md +++ b/docs/source_spans.md @@ -8,7 +8,7 @@ These are the current breaking changes introduced by the source spans feature: - `Select`, `With`, `Cte`, `WildcardAdditionalOptions` now store a `TokenWithLocation` #### Misc. -- `TokenWithLocation` stores a full `Span`, rathern than just a source location. Users relying on `token.location` should use `token.location.start` instead. +- `TokenWithLocation` stores a full `Span`, rather than just a source location. Users relying on `token.location` should use `token.location.start` instead. ## Source Span Contributing Guidelines For contributing source spans improvement in addition to the general [contribution guidelines](../README.md#contributing), please make sure to pay attention to the following: @@ -25,7 +25,7 @@ The primary reason for missing and inaccurate source spans at this time is missi When considering adding support for source spans on a type, consider the impact to consumers of that type and whether your change would require a consumer to do non-trivial changes to their code. -f.e. of a trivial change +Example of a trivial change ```rust match node { ast::Query { @@ -39,11 +39,11 @@ If adding source spans to a type would require a significant change like wrappin ### AST Node Equality and Hashes -When adding tokens to AST nodes, make sure to wrap them in the [IgnoreField](https://docs.rs/sqlparser/latest/sqlparser/ast/helpers/struct.IgnoreField.html) helper to ensure that semantically equivalent AST nodes always compare as equal and hash to the same value. F.e. `select 5` and `SELECT 5` would compare as different `Select` nodes, if the select token was stored directly. f.e. +When adding tokens to AST nodes, make sure to store them using the [AttachedToken](https://docs.rs/sqlparser/latest/sqlparser/ast/helpers/struct.AttachedToken.html) helper to ensure that semantically equivalent AST nodes always compare as equal and hash to the same value. F.e. `select 5` and `SELECT 5` would compare as different `Select` nodes, if the select token was stored directly. f.e. ```rust struct Select { - select_token: IgnoreField, // only used for spans + select_token: AttachedToken, // only used for spans /// remaining fields field1, field2, diff --git a/src/ast/helpers/attached_token.rs b/src/ast/helpers/attached_token.rs index 4c4f6c59e..4c325f200 100644 --- a/src/ast/helpers/attached_token.rs +++ b/src/ast/helpers/attached_token.rs @@ -21,10 +21,18 @@ use core::hash::{Hash, Hasher}; use crate::tokenizer::TokenWithLocation; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "visitor")] +use sqlparser_derive::{Visit, VisitMut}; + /// A wrapper type for attaching tokens to AST nodes that should be ignored in comparisons and hashing. /// This should be used when a token is not relevant for semantics, but is still needed for /// accurate source location tracking. #[derive(Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct AttachedToken(pub TokenWithLocation); // Conditional Implementations diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a59bc0dd2..47955a1d7 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -23,6 +23,7 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; +use helpers::attached_token::AttachedToken; use core::ops::Deref; use core::{ @@ -36,7 +37,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -use crate::tokenizer::{Span, TokenWithLocation}; +use crate::tokenizer::Span; pub use self::data_type::{ ArrayElemTypeDef, CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, @@ -967,10 +968,10 @@ pub enum Expr { /// `` opt_search_modifier: Option, }, - Wildcard(TokenWithLocation), + Wildcard(AttachedToken), /// Qualified wildcard, e.g. `alias.*` or `schema.table.*`. /// (Same caveats apply to `QualifiedWildcard` as to `Wildcard`.) - QualifiedWildcard(ObjectName, TokenWithLocation), + QualifiedWildcard(ObjectName, AttachedToken), /// Some dialects support an older syntax for outer joins where columns are /// marked with the `(+)` operator in the WHERE clause, for example: /// diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 8a7e7916b..0f56f8b18 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1372,13 +1372,13 @@ impl Spanned for Expr { Expr::Map(_) => Span::empty(), Expr::Subscript { expr, subscript } => expr.span().union(&subscript.span()), Expr::Interval(interval) => interval.value.span(), - Expr::Wildcard(token) => token.span, + Expr::Wildcard(token) => token.0.span, Expr::QualifiedWildcard(object_name, token) => union_spans( object_name .0 .iter() .map(|i| i.span) - .chain(iter::once(token.span)), + .chain(iter::once(token.0.span)), ), Expr::OuterJoin(expr) => expr.span(), Expr::Prior(expr) => expr.span(), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 00ac175bd..a6a328b33 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -886,7 +886,7 @@ impl<'a> Parser<'a> { Token::Mul => { return Ok(Expr::QualifiedWildcard( ObjectName(id_parts), - next_token, + AttachedToken(next_token), )); } _ => { @@ -898,7 +898,7 @@ impl<'a> Parser<'a> { } } Token::Mul => { - return Ok(Expr::Wildcard(next_token)); + return Ok(Expr::Wildcard(AttachedToken(next_token))); } _ => (), }; @@ -1143,7 +1143,7 @@ impl<'a> Parser<'a> { } if let Some(ending_wildcard_token) = ending_wildcard { - Ok(Expr::QualifiedWildcard(ObjectName(id_parts), ending_wildcard_token)) + Ok(Expr::QualifiedWildcard(ObjectName(id_parts), AttachedToken(ending_wildcard_token))) } else if self.consume_token(&Token::LParen) { if dialect_of!(self is SnowflakeDialect | MsSqlDialect) && self.consume_tokens(&[Token::Plus, Token::RParen]) @@ -9203,8 +9203,7 @@ impl<'a> Parser<'a> { } } - /// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`), - /// assuming the initial `SELECT` was not yet consumed + /// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`) pub fn parse_select(&mut self) -> Result { let select_token = self.expect_keyword(Keyword::SELECT)?; let value_table_mode = @@ -11290,10 +11289,10 @@ impl<'a> Parser<'a> { match self.parse_wildcard_expr()? { Expr::QualifiedWildcard(prefix, token) => Ok(SelectItem::QualifiedWildcard( prefix, - self.parse_wildcard_additional_options(token)?, + self.parse_wildcard_additional_options(token.0)?, )), Expr::Wildcard(token) => Ok(SelectItem::Wildcard( - self.parse_wildcard_additional_options(token)?, + self.parse_wildcard_additional_options(token.0)?, )), Expr::Identifier(v) if v.value.to_lowercase() == "from" && v.quote_style.is_none() => { parser_err!( diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 64ff992e6..c4417d262 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -29,10 +29,10 @@ use alloc::{ vec, vec::Vec, }; +use core::iter::Peekable; use core::num::NonZeroU8; use core::str::Chars; use core::{cmp, fmt}; -use core::{hash, iter::Peekable}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -522,7 +522,7 @@ impl Span { } /// A [Token] with [Location] attached to it -#[derive(Debug, Eq, Clone)] +#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct TokenWithLocation { @@ -544,35 +544,6 @@ impl TokenWithLocation { } } -impl core::hash::Hash for TokenWithLocation { - fn hash(&self, state: &mut H) { - let TokenWithLocation { token, span: _ } = self; - - token.hash(state); - } -} - -impl PartialEq for TokenWithLocation { - fn eq(&self, other: &TokenWithLocation) -> bool { - let TokenWithLocation { token, span: _ } = self; - - token == &other.token - } -} - -impl PartialOrd for TokenWithLocation { - fn partial_cmp(&self, other: &TokenWithLocation) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TokenWithLocation { - fn cmp(&self, other: &Self) -> cmp::Ordering { - let TokenWithLocation { token, span: _ } = self; - token.cmp(&other.token) - } -} - impl PartialEq for TokenWithLocation { fn eq(&self, other: &Token) -> bool { &self.token == other From 3989efe030e4cc9fcd62bdc48fc185170b5c8f95 Mon Sep 17 00:00:00 2001 From: Mark-Oliver Junge Date: Tue, 19 Nov 2024 11:04:47 +0100 Subject: [PATCH 31/32] add AttachedToken::empty --- src/ast/helpers/attached_token.rs | 8 +++++++- tests/sqlparser_clickhouse.rs | 3 ++- tests/sqlparser_common.rs | 21 ++++++++++----------- tests/sqlparser_duckdb.rs | 5 +++-- tests/sqlparser_mssql.rs | 5 +++-- tests/sqlparser_mysql.rs | 18 +++++++++--------- tests/sqlparser_postgres.rs | 7 ++++--- 7 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/ast/helpers/attached_token.rs b/src/ast/helpers/attached_token.rs index 4c325f200..48696c336 100644 --- a/src/ast/helpers/attached_token.rs +++ b/src/ast/helpers/attached_token.rs @@ -19,7 +19,7 @@ use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; use core::fmt::{self, Debug, Formatter}; use core::hash::{Hash, Hasher}; -use crate::tokenizer::TokenWithLocation; +use crate::tokenizer::{Token, TokenWithLocation}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -35,6 +35,12 @@ use sqlparser_derive::{Visit, VisitMut}; #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct AttachedToken(pub TokenWithLocation); +impl AttachedToken { + pub fn empty() -> Self { + AttachedToken(TokenWithLocation::wrap(Token::EOF)) + } +} + // Conditional Implementations impl Debug for AttachedToken { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index b0e14dcec..57932bf54 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -21,6 +21,7 @@ #[macro_use] mod test_utils; +use helpers::attached_token::AttachedToken; use sqlparser::tokenizer::{Span, Token, TokenWithLocation}; use test_utils::*; @@ -40,7 +41,7 @@ fn parse_map_access_expr() { assert_eq!( Select { distinct: None, - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), top: None, top_before_distinct: false, projection: vec![UnnamedExpr(MapAccess { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 8bb7f3c65..dbe7d2f67 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -25,6 +25,7 @@ extern crate core; +use helpers::attached_token::AttachedToken; use matches::assert_matches; use sqlparser::ast::SelectItem::UnnamedExpr; use sqlparser::ast::TableFactor::{Pivot, Unpivot}; @@ -378,8 +379,7 @@ fn parse_update_set_from() { subquery: Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")) - .into(), + select_token: AttachedToken::empty().into(), distinct: None, top: None, top_before_distinct: false, @@ -4661,7 +4661,7 @@ fn test_parse_named_window() { ORDER BY C3"; let actual_select_only = verified_only_select(sql); let expected = Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECTI")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -5317,7 +5317,7 @@ fn parse_interval_and_or_xor() { let expected_ast = vec![Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -6444,7 +6444,7 @@ fn parse_recursive_cte() { query: Box::new(cte_query), from: None, materialized: None, - closing_paren_token: TokenWithLocation::wrap(Token::RParen).into(), + closing_paren_token: AttachedToken::empty(), }; assert_eq!(with.cte_tables.first().unwrap(), &expected); } @@ -7407,7 +7407,7 @@ fn lateral_function() { let sql = "SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id)"; let actual_select_only = verified_only_select(sql); let expected = Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], @@ -8254,8 +8254,7 @@ fn parse_merge() { subquery: Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")) - .into(), + select_token: AttachedToken::empty().into(), distinct: None, top: None, top_before_distinct: false, @@ -9894,7 +9893,7 @@ fn parse_unload() { Statement::Unload { query: Box::new(Query { body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -10073,7 +10072,7 @@ fn parse_map_access_expr() { #[test] fn parse_connect_by() { let expect_query = Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -10161,7 +10160,7 @@ fn parse_connect_by() { assert_eq!( all_dialects_where(|d| d.supports_connect_by()).verified_only_select(connect_by_3), Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 7c3302e75..623a216a2 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -18,6 +18,7 @@ #[macro_use] mod test_utils; +use helpers::attached_token::AttachedToken; use sqlparser::tokenizer::{Span, Token, TokenWithLocation}; use test_utils::*; @@ -260,7 +261,7 @@ fn test_select_union_by_name() { op: SetOperator::Union, set_quantifier: *expected_quantifier, left: Box::::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], @@ -297,7 +298,7 @@ fn test_select_union_by_name() { connect_by: None, }))), right: Box::::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())], diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index e0429c39f..1e72dfd0c 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -22,6 +22,7 @@ #[macro_use] mod test_utils; +use helpers::attached_token::AttachedToken; use sqlparser::tokenizer::{Span, Token, TokenWithLocation}; use test_utils::*; @@ -113,7 +114,7 @@ fn parse_create_procedure() { settings: None, format_clause: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -525,7 +526,7 @@ fn parse_substring_in_select() { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: Some(Distinct::Distinct), top: None, top_before_distinct: false, diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index a18f970e8..d5b64501f 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -19,6 +19,7 @@ //! Test SQL syntax specific to MySQL. The parser based on the generic dialect //! is also tested (on the inputs it can handle). +use helpers::attached_token::AttachedToken; use matches::assert_matches; use sqlparser::ast::MysqlInsertPriority::{Delayed, HighPriority, LowPriority}; @@ -966,7 +967,7 @@ fn parse_escaped_quote_identifiers_with_escape() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -1019,8 +1020,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), - + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -1066,7 +1066,7 @@ fn parse_escaped_backticks_with_escape() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, @@ -1117,7 +1117,7 @@ fn parse_escaped_backticks_with_no_escape() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, @@ -1765,7 +1765,7 @@ fn parse_select_with_numeric_prefix_column_name() { assert_eq!( q.body, Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, @@ -1822,7 +1822,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() { assert_eq!( q.body, Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, @@ -2334,7 +2334,7 @@ fn parse_substring_in_select() { Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: Some(Distinct::Distinct), top: None, top_before_distinct: false, @@ -2659,7 +2659,7 @@ fn parse_hex_string_introducer() { Statement::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index a6d456ad3..b33de9e37 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -21,6 +21,7 @@ #[macro_use] mod test_utils; +use helpers::attached_token::AttachedToken; use sqlparser::tokenizer::{Span, Token, TokenWithLocation}; use test_utils::*; @@ -1164,7 +1165,7 @@ fn parse_copy_to() { source: CopySource::Query(Box::new(Query { with: None, body: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -2520,7 +2521,7 @@ fn parse_array_subquery_expr() { op: SetOperator::Union, set_quantifier: SetQuantifier::None, left: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, @@ -2542,7 +2543,7 @@ fn parse_array_subquery_expr() { connect_by: None, }))), right: Box::new(SetExpr::Select(Box::new(Select { - select_token: TokenWithLocation::wrap(Token::make_keyword("SELECT")).into(), + select_token: AttachedToken::empty(), distinct: None, top: None, top_before_distinct: false, From 23c4922ec1a93d332925a8ca4edd257acebf70d6 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Sun, 24 Nov 2024 07:05:12 -0500 Subject: [PATCH 32/32] update test to avoid overflow --- tests/sqlparser_snowflake.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index f99a00f5b..08792380d 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -2762,7 +2762,9 @@ fn parse_view_column_descriptions() { #[test] fn test_parentheses_overflow() { - let max_nesting_level: usize = 30; + // TODO: increase / improve after we fix the recursion limit + // for real (see https://github.com/apache/datafusion-sqlparser-rs/issues/984) + let max_nesting_level: usize = 25; // Verify the recursion check is not too wasteful... (num of parentheses - 2 is acceptable) let slack = 2;