diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c562c6100..0e15b13c6 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -473,10 +473,10 @@ pub enum IsCheck { NULL, FALSE, TRUE, - UNKNOWN + UNKNOWN, } -impl fmt::Display for IsCheck{ +impl fmt::Display for IsCheck { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { IsCheck::NULL => write!(f, "NULL"), diff --git a/src/ast/query.rs b/src/ast/query.rs index f587419b0..a1568320f 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -468,7 +468,7 @@ impl fmt::Display for Join { _ => "", } } - + #[allow(clippy::needless_lifetimes)] fn suffix<'a>(constraint: &'a JoinConstraint) -> impl fmt::Display + 'a { struct Suffix<'a>(&'a JoinConstraint); diff --git a/src/ast/value.rs b/src/ast/value.rs index 8a1017d95..44f867e4a 100644 --- a/src/ast/value.rs +++ b/src/ast/value.rs @@ -65,8 +65,8 @@ impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Value::Number(v) => write!(f, "{}", v), - Value::SingleQuotedString(v) => write!(f, "'{}'", v), - Value::DoubleQuotedString(v) => write!(f, "\"{}\"", v), + Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)), + Value::DoubleQuotedString(v) => write!(f, "\"{}\"", escape_double_quote_string(v)), Value::RegexLiteral { ref value, quote } => write!(f, "{}{}{}", quote, value, quote), Value::NationalStringLiteral(v) => write!(f, "N'{}'", v), Value::HexStringLiteral(v) => write!(f, "X'{}'", v), @@ -190,6 +190,25 @@ impl<'a> fmt::Display for EscapeSingleQuoteString<'a> { } } +pub struct EscapeDoubleQuoteString<'a>(&'a str); + +impl<'a> fmt::Display for EscapeDoubleQuoteString<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for c in self.0.chars() { + if c == '"' { + write!(f, "\"\"")?; + } else { + write!(f, "{}", c)?; + } + } + Ok(()) + } +} + pub fn escape_single_quote_string(s: &str) -> EscapeSingleQuoteString<'_> { EscapeSingleQuoteString(s) } + +pub fn escape_double_quote_string(s: &str) -> EscapeDoubleQuoteString<'_> { + EscapeDoubleQuoteString(s) +} diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index d779d2576..818fa0d0a 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -17,7 +17,11 @@ pub struct GenericDialect; impl Dialect for GenericDialect { fn is_identifier_start(&self, ch: char) -> bool { - ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) || ch == '_' || ch == '#' || ch == '@' + ('a'..='z').contains(&ch) + || ('A'..='Z').contains(&ch) + || ch == '_' + || ch == '#' + || ch == '@' } fn is_identifier_part(&self, ch: char) -> bool { diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index 2eaa3463c..cc7396bcb 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -496,11 +496,11 @@ define_keywords!( WEDNESDAY, WEEK, WEEKDAY, + WEEKDAY_ISO, + WEEKISO, WEEKOFYEAR, WEEKOFYEARISO, WEEKOFYEAR_ISO, - WEEKISO, - WEEKDAY_ISO, WEEK_ISO, WHEN, WHENEVER, @@ -511,8 +511,8 @@ define_keywords!( WITHIN, WITHOUT, WK, - WOY, WORK, + WOY, WRITE, WY, Y, diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs index cb5c6daa8..539a17a9f 100644 --- a/src/dialect/mssql.rs +++ b/src/dialect/mssql.rs @@ -23,7 +23,11 @@ impl Dialect for MsSqlDialect { fn is_identifier_start(&self, ch: char) -> bool { // See https://docs.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers?view=sql-server-2017#rules-for-regular-identifiers // We don't support non-latin "letters" currently. - ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) || ch == '_' || ch == '#' || ch == '@' + ('a'..='z').contains(&ch) + || ('A'..='Z').contains(&ch) + || ch == '_' + || ch == '#' + || ch == '@' } fn is_identifier_part(&self, ch: char) -> bool { diff --git a/src/parser.rs b/src/parser.rs index 3c8d6d21a..49928db4e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -979,9 +979,9 @@ impl<'a> Parser<'a> { } else if self.parse_keyword(Keyword::BETWEEN) { Ok((self.parse_between(expr, negated)?, true)) } else if self.parse_keyword(Keyword::LIKE) { - Ok((self.parse_like(expr, true, negated)?, true)) + Ok((self.parse_like(expr, true, negated, precedence)?, true)) } else if self.parse_keyword(Keyword::ILIKE) { - Ok((self.parse_like(expr, false, negated)?, true)) + Ok((self.parse_like(expr, false, negated, precedence)?, true)) } else if self.parse_keywords(&[Keyword::SIMILAR, Keyword::TO]) { Ok((self.parse_similar(expr, negated)?, true)) } else { @@ -1067,10 +1067,11 @@ impl<'a> Parser<'a> { expr: Expr, case_sensitive: bool, negated: bool, + precedence: u8, ) -> Result { - let pat = self.parse_expr()?; + let pat = self.parse_subexpr(precedence)?; let esc = if self.parse_keyword(Keyword::ESCAPE) { - Some(self.parse_expr()?) + Some(self.parse_subexpr(precedence)?) } else { None }; @@ -1936,6 +1937,9 @@ impl<'a> Parser<'a> { } pub fn preceding_toks(&self) -> String { + if self.tokens.is_empty() { + return "".to_string(); + } let slice_start = if self.index < 20 { 0 } else { self.index - 20 }; let slice_end = if self.index >= self.tokens.len() { self.tokens.len() - 1 @@ -2596,7 +2600,9 @@ impl<'a> Parser<'a> { // followed by some joins or (B) another level of nesting. let mut table_and_joins = self.parse_table_and_joins()?; - if !table_and_joins.joins.is_empty() || matches!(&table_and_joins.relation, TableFactor::NestedJoin(_)) { + if !table_and_joins.joins.is_empty() + || matches!(&table_and_joins.relation, TableFactor::NestedJoin(_)) + { // (B): `table_and_joins` (what we found inside the parentheses) // is a nested join `(foo JOIN bar)`, not followed by other joins. self.expect_token(&Token::RParen)?; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index cbe3a926c..ad9df0b77 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -728,7 +728,7 @@ impl<'a> Tokenizer<'a> { match ch { // allow backslash to escape the next character, whatever it is '\\' => { - s.push('\\'); + chars.next(); // consume the escape char if let Some(next_ch) = chars.next() { s.push(next_ch); } @@ -739,7 +739,6 @@ impl<'a> Tokenizer<'a> { && ch == quote_ch && next_char_is_quote => { - s.push(quote_ch); s.push(quote_ch); chars.next(); // consume quote_ch } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a4f05d760..b7c3b1c96 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -92,7 +92,10 @@ fn parse_insert_invalid() { let sql = "INSERT public.customer (id, name, active) VALUES (1, 2, 3)"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("".to_string(), "Expected INTO, found: public".to_string()), + ParserError::ParserError( + "INSERT".to_string(), + "Expected INTO, found: public".to_string() + ), res.unwrap_err() ); } @@ -135,14 +138,20 @@ fn parse_update() { let sql = "UPDATE t WHERE 1"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("".to_string(), "Expected SET, found: WHERE".to_string()), + ParserError::ParserError( + "UPDATE t".to_string(), + "Expected SET, found: WHERE".to_string() + ), res.unwrap_err() ); let sql = "UPDATE t SET a = 1 extrabadstuff"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: extrabadstuff".to_string()), + ParserError::ParserError( + "UPDATE t SET a = 1".to_string(), + "Expected end of statement, found: extrabadstuff".to_string() + ), res.unwrap_err() ); } @@ -248,7 +257,10 @@ fn parse_select_all() { fn parse_select_all_distinct() { let result = parse_sql_statements("SELECT ALL DISTINCT name FROM customer"); assert_eq!( - ParserError::ParserError("".to_string(), "Cannot specify both ALL and DISTINCT".to_string()), + ParserError::ParserError( + "SELECT ALL DISTINCT".to_string(), + "Cannot specify both ALL and DISTINCT".to_string() + ), result.unwrap_err(), ); } @@ -257,22 +269,37 @@ fn parse_select_all_distinct() { fn parse_select_wildcard() { let sql = "SELECT * FROM foo"; let select = verified_only_select(sql); - assert_eq!(&SelectItem::Wildcard{prefix: None, except: vec![], replace: vec![]}, only(&select.projection)); + assert_eq!( + &SelectItem::Wildcard { + prefix: None, + except: vec![], + replace: vec![] + }, + only(&select.projection) + ); let sql = "SELECT foo.* FROM foo"; let select = verified_only_select(sql); assert_eq!( - &SelectItem::Wildcard{prefix: Some(ObjectName(vec![Ident::new("foo")])), except: vec![], replace: vec![]}, + &SelectItem::Wildcard { + prefix: Some(ObjectName(vec![Ident::new("foo")])), + except: vec![], + replace: vec![] + }, only(&select.projection) ); let sql = "SELECT myschema.mytable.* FROM myschema.mytable"; let select = verified_only_select(sql); assert_eq!( - &SelectItem::Wildcard{ prefix: Some(ObjectName(vec![ - Ident::new("myschema"), - Ident::new("mytable"), - ])), except: vec![], replace: vec![] }, + &SelectItem::Wildcard { + prefix: Some(ObjectName(vec![ + Ident::new("myschema"), + Ident::new("mytable"), + ])), + except: vec![], + replace: vec![] + }, only(&select.projection) ); } @@ -310,13 +337,19 @@ fn parse_column_aliases() { fn test_eof_after_as() { let res = parse_sql_statements("SELECT foo AS"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected an identifier after AS, found: EOF".to_string()), + ParserError::ParserError( + "SELECT foo ".to_string(), + "Expected an identifier after AS, found: EOF".to_string() + ), res.unwrap_err() ); let res = parse_sql_statements("SELECT 1 FROM foo AS"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected an identifier after AS, found: EOF".to_string()), + ParserError::ParserError( + "SELECT 1 FROM foo ".to_string(), + "Expected an identifier after AS, found: EOF".to_string() + ), res.unwrap_err() ); } @@ -371,7 +404,10 @@ fn parse_select_count_distinct() { let sql = "SELECT COUNT(ALL DISTINCT + x) FROM customer"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("".to_string(), "Cannot specify both ALL and DISTINCT".to_string()), + ParserError::ParserError( + "SELECT COUNT(ALL DISTINCT".to_string(), + "Cannot specify both ALL and DISTINCT".to_string() + ), res.unwrap_err() ); } @@ -387,7 +423,10 @@ fn parse_not() { fn parse_invalid_infix_not() { let res = parse_sql_statements("SELECT c FROM t WHERE c NOT ("); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: NOT".to_string()), + ParserError::ParserError( + "SELECT c FROM t WHERE c".to_string(), + "Expected end of statement, found: NOT".to_string() + ), res.unwrap_err(), ); } @@ -534,7 +573,11 @@ fn parse_is_null() { use self::Expr::*; let sql = "a IS NULL"; assert_eq!( - Expr::Is{expr: Box::new(Identifier(Ident::new("a"))), check: IsCheck::NULL, negated: false}, + Expr::Is { + expr: Box::new(Identifier(Ident::new("a"))), + check: IsCheck::NULL, + negated: false + }, verified_expr(sql) ); } @@ -544,7 +587,11 @@ fn parse_is_not_null() { use self::Expr::*; let sql = "a IS NOT NULL"; assert_eq!( - Expr::Is{expr: (Box::new(Identifier(Ident::new("a")))), check: IsCheck::NULL, negated: true}, + Expr::Is { + expr: (Box::new(Identifier(Ident::new("a")))), + check: IsCheck::NULL, + negated: true + }, verified_expr(sql) ); } @@ -553,17 +600,23 @@ fn parse_is_not_null() { fn parse_not_precedence() { // NOT has higher precedence than OR/AND, so the following must parse as (NOT true) OR true let sql = "NOT true OR true"; - assert_matches!(verified_expr(sql), Expr::BinaryOp { - op: BinaryOperator::Or, - .. - }); + assert_matches!( + verified_expr(sql), + Expr::BinaryOp { + op: BinaryOperator::Or, + .. + } + ); // But NOT has lower precedence than comparison operators, so the following parses as NOT (a IS NULL) let sql = "NOT a IS NULL"; - assert_matches!(verified_expr(sql), Expr::UnaryOp { - op: UnaryOperator::Not, - .. - }); + assert_matches!( + verified_expr(sql), + Expr::UnaryOp { + op: UnaryOperator::Not, + .. + } + ); // NOT has lower precedence than BETWEEN, so the following parses as NOT (1 NOT BETWEEN 1 AND 2) let sql = "NOT 1 NOT BETWEEN 1 AND 2"; @@ -586,10 +639,12 @@ fn parse_not_precedence() { verified_expr(sql), Expr::UnaryOp { op: UnaryOperator::Not, - expr: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Value(Value::SingleQuotedString("a".into()))), - op: BinaryOperator::NotRlike, - right: Box::new(Expr::Value(Value::SingleQuotedString("b".into()))), + expr: Box::new(Expr::Like { + expr: Box::new(Expr::Value(Value::SingleQuotedString("a".into()))), + case_sensitive: true, + negated: true, + pat: Box::new(Expr::Value(Value::SingleQuotedString("b".into()))), + esc: None, }), }, ); @@ -618,14 +673,12 @@ fn parse_like() { ); let select = verified_only_select(sql); assert_eq!( - Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("name"))), - op: if negated { - BinaryOperator::NotRlike - } else { - BinaryOperator::Rlike - }, - right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))), + Expr::Like { + expr: Box::new(Expr::Identifier(Ident::new("name"))), + case_sensitive: true, + negated, + pat: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))), + esc: None, }, select.selection.unwrap() ); @@ -638,15 +691,17 @@ fn parse_like() { ); let select = verified_only_select(sql); assert_eq!( - Expr::Is{expr: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("name"))), - op: if negated { - BinaryOperator::NotRlike - } else { - BinaryOperator::Rlike - }, - right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))), - }), check: IsCheck::NULL, negated: false,}, + Expr::Is { + expr: Box::new(Expr::Like { + expr: Box::new(Expr::Identifier(Ident::new("name"))), + case_sensitive: true, + negated, + pat: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))), + esc: None, + }), + check: IsCheck::NULL, + negated: false, + }, select.selection.unwrap() ); } @@ -756,20 +811,24 @@ fn parse_between_with_expr() { let sql = "SELECT * FROM t WHERE 1 BETWEEN 1 + 2 AND 3 + 4 IS NULL"; let select = verified_only_select(sql); assert_eq!( - Expr::Is{expr: Box::new(Expr::Between { - expr: Box::new(Expr::Value(number("1"))), - low: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Value(number("1"))), - op: Plus, - right: Box::new(Expr::Value(number("2"))), - }), - high: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Value(number("3"))), - op: Plus, - right: Box::new(Expr::Value(number("4"))), + Expr::Is { + expr: Box::new(Expr::Between { + expr: Box::new(Expr::Value(number("1"))), + low: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Value(number("1"))), + op: Plus, + right: Box::new(Expr::Value(number("2"))), + }), + high: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Value(number("3"))), + op: Plus, + right: Box::new(Expr::Value(number("4"))), + }), + negated: false, }), - negated: false, - }), check: IsCheck::NULL, negated: false}, + check: IsCheck::NULL, + negated: false + }, select.selection.unwrap() ); @@ -899,11 +958,11 @@ fn parse_select_having() { args: vec![FunctionArg::Unnamed(Expr::Wildcard)], over: None, distinct: false, - ignore_respect_nulls: None, - limit: None, - order_by: vec![], - outer_ignore_respect_nulls: None, - within_group: vec![], + ignore_respect_nulls: None, + limit: None, + order_by: vec![], + outer_ignore_respect_nulls: None, + within_group: vec![], })), op: BinaryOperator::Gt, right: Box::new(Expr::Value(number("1"))) @@ -973,12 +1032,7 @@ fn parse_extract() { verified_stmt("SELECT EXTRACT(HOUR FROM d)"); verified_stmt("SELECT EXTRACT(MINUTE FROM d)"); verified_stmt("SELECT EXTRACT(SECOND FROM d)"); - - let res = parse_sql_statements("SELECT EXTRACT(MILLISECOND FROM d)"); - assert_eq!( - ParserError::ParserError("".to_string(), "Expected date/time field, found: MILLISECOND".to_string()), - res.unwrap_err() - ); + verified_stmt("SELECT EXTRACT(MILLISECOND FROM d)"); } #[test] @@ -1476,7 +1530,7 @@ fn parse_create_external_table_lowercase() { lng DOUBLE) \ STORED AS PARQUET LOCATION '/tmp/example.csv'", ); - assert_matches!(ast, Statement::CreateTable{..}); + assert_matches!(ast, Statement::CreateTable { .. }); } #[test] @@ -1591,13 +1645,17 @@ fn parse_alter_table_drop_column() { fn parse_bad_constraint() { let res = parse_sql_statements("ALTER TABLE tab ADD"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected identifier, found: EOF".to_string()), + ParserError::ParserError( + "ALTER TABLE tab ".to_string(), + "Expected identifier, found: EOF".to_string() + ), res.unwrap_err() ); let res = parse_sql_statements("CREATE TABLE tab (foo int,"); assert_eq!( - ParserError::ParserError("".to_string(), + ParserError::ParserError( + "CREATE TABLE tab (foo int".to_string(), "Expected column name or constraint definition, found: EOF".to_string() ), res.unwrap_err() @@ -1787,7 +1845,7 @@ fn parse_literal_interval() { leading_precision: None, last_field: Some(DateTimeField::Month), fractional_seconds_precision: None, - value_quoting: None, + value_quoting: Some('\''), }), expr_from_projection(only(&select.projection)), ); @@ -1801,7 +1859,7 @@ fn parse_literal_interval() { leading_precision: Some(5), last_field: Some(DateTimeField::Second), fractional_seconds_precision: Some(5), - value_quoting: None, + value_quoting: Some('\''), }), expr_from_projection(only(&select.projection)), ); @@ -1815,7 +1873,7 @@ fn parse_literal_interval() { leading_precision: Some(5), last_field: None, fractional_seconds_precision: Some(4), - value_quoting: None, + value_quoting: Some('\''), }), expr_from_projection(only(&select.projection)), ); @@ -1829,7 +1887,7 @@ fn parse_literal_interval() { leading_precision: None, last_field: None, fractional_seconds_precision: None, - value_quoting: None, + value_quoting: Some('\''), }), expr_from_projection(only(&select.projection)), ); @@ -1843,7 +1901,7 @@ fn parse_literal_interval() { leading_precision: Some(1), last_field: None, fractional_seconds_precision: None, - value_quoting: None, + value_quoting: Some('\''), }), expr_from_projection(only(&select.projection)), ); @@ -1857,20 +1915,26 @@ fn parse_literal_interval() { leading_precision: None, last_field: None, fractional_seconds_precision: None, - value_quoting: None, + value_quoting: Some('\''), }), expr_from_projection(only(&select.projection)), ); let result = parse_sql_statements("SELECT INTERVAL '1' SECOND TO SECOND"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: SECOND".to_string()), + ParserError::ParserError( + "SELECT INTERVAL '1' SECOND TO".to_string(), + "Expected end of statement, found: SECOND".to_string() + ), result.unwrap_err(), ); let result = parse_sql_statements("SELECT INTERVAL '10' HOUR (1) TO HOUR (2)"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: (".to_string()), + ParserError::ParserError( + "SELECT INTERVAL '10' HOUR (1) TO HOUR ".to_string(), + "Expected end of statement, found: (".to_string() + ), result.unwrap_err(), ); @@ -1920,11 +1984,11 @@ fn parse_table_function() { ))], over: None, distinct: false, - ignore_respect_nulls: None, - limit: None, - order_by: vec![], - outer_ignore_respect_nulls: None, - within_group: vec![], + ignore_respect_nulls: None, + limit: None, + order_by: vec![], + outer_ignore_respect_nulls: None, + within_group: vec![], }); assert_eq!(expr, expected_expr); assert_eq!(alias, table_alias("a")) @@ -1934,13 +1998,19 @@ fn parse_table_function() { let res = parse_sql_statements("SELECT * FROM TABLE '1' AS a"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected (, found: \'1\'".to_string()), + ParserError::ParserError( + "SELECT * FROM TABLE".to_string(), + "Expected (, found: \'1\'".to_string() + ), res.unwrap_err() ); let res = parse_sql_statements("SELECT * FROM TABLE (FUN(a) AS a"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected ), found: AS".to_string()), + ParserError::ParserError( + "SELECT * FROM TABLE (FUN(a)".to_string(), + "Expected ), found: AS".to_string() + ), res.unwrap_err() ); } @@ -2035,7 +2105,11 @@ fn parse_searched_case_expr() { &Case { operand: None, conditions: vec![ - Expr::Is{expr: Box::new(Identifier(Ident::new("bar"))), check: IsCheck::NULL, negated: false}, + Expr::Is { + expr: Box::new(Identifier(Ident::new("bar"))), + check: IsCheck::NULL, + negated: false + }, BinaryOp { left: Box::new(Identifier(Ident::new("bar"))), op: Eq, @@ -2308,7 +2382,10 @@ fn parse_natural_join() { let sql = "SELECT * FROM t1 natural"; assert_eq!( - ParserError::ParserError("".to_string(), "Expected a join type after NATURAL, found: EOF".to_string()), + ParserError::ParserError( + "SELECT * FROM t1 ".to_string(), + "Expected a join type after NATURAL, found: EOF".to_string() + ), parse_sql_statements(sql).unwrap_err(), ); } @@ -2374,7 +2451,10 @@ fn parse_join_syntax_variants() { let res = parse_sql_statements("SELECT * FROM a OUTER JOIN b ON 1"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected APPLY, found: JOIN".to_string()), + ParserError::ParserError( + "SELECT * FROM a OUTER".to_string(), + "Expected APPLY, found: JOIN".to_string() + ), res.unwrap_err() ); } @@ -2557,7 +2637,7 @@ fn parse_values() { #[test] fn parse_multiple_statements() { - fn test_with(sql1: &str, sql2_kw: &str, sql2_rest: &str) { + fn test_with(sql1: &str, sql2_kw: &str, sql2_rest: &str, error: &str) { // Check that a string consisting of two statements delimited by a semicolon // parses the same as both statements individually: let res = parse_sql_statements(&(sql1.to_owned() + ";" + sql2_kw + sql2_rest)); @@ -2573,23 +2653,47 @@ fn parse_multiple_statements() { // Check that forgetting the semicolon results in an error: let res = parse_sql_statements(&(sql1.to_owned() + " " + sql2_kw + sql2_rest)); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: ".to_string() + sql2_kw), + ParserError::ParserError( + error.to_string(), + "Expected end of statement, found: ".to_string() + sql2_kw + ), res.unwrap_err() ); } - test_with("SELECT foo", "SELECT", " bar"); + test_with("SELECT foo", "SELECT", " bar", "SELECT foo "); // ensure that SELECT/WITH is not parsed as a table or column alias if ';' // separating the statements is omitted: - test_with("SELECT foo FROM baz", "SELECT", " bar"); - test_with("SELECT foo", "WITH", " cte AS (SELECT 1 AS s) SELECT bar"); + test_with( + "SELECT foo FROM baz", + "SELECT", + " bar", + "SELECT foo FROM baz ", + ); + test_with( + "SELECT foo", + "WITH", + " cte AS (SELECT 1 AS s) SELECT bar", + "SELECT foo ", + ); test_with( "SELECT foo FROM baz", "WITH", " cte AS (SELECT 1 AS s) SELECT bar", + "SELECT foo FROM baz ", + ); + test_with("DELETE FROM foo", "SELECT", " bar", "DELETE FROM foo"); + test_with( + "INSERT INTO foo VALUES (1)", + "SELECT", + " bar", + "INSERT INTO foo VALUES (1)", + ); + test_with( + "CREATE TABLE foo (baz INT)", + "SELECT", + " bar", + "CREATE TABLE foo (baz INT)", ); - test_with("DELETE FROM foo", "SELECT", " bar"); - test_with("INSERT INTO foo VALUES (1)", "SELECT", " bar"); - test_with("CREATE TABLE foo (baz INT)", "SELECT", " bar"); // Make sure that empty statements do not cause an error: let res = parse_sql_statements(";;"); assert_eq!(0, res.unwrap().len()); @@ -2598,11 +2702,14 @@ fn parse_multiple_statements() { #[test] fn parse_scalar_subqueries() { let sql = "(SELECT 1) + (SELECT 2)"; - assert_matches!(verified_expr(sql), Expr::BinaryOp { + assert_matches!( + verified_expr(sql), + Expr::BinaryOp { op: BinaryOperator::Plus, .. //left: box Subquery { .. }, //right: box Subquery { .. }, - }); + } + ); } #[test] @@ -2630,7 +2737,8 @@ fn parse_exists_subquery() { let res = parse_sql_statements("SELECT EXISTS ("); assert_eq!( - ParserError::ParserError("".to_string(), + ParserError::ParserError( + "SELECT EXISTS ".to_string(), "Expected SELECT, VALUES, or a subquery in the query body, found: EOF".to_string() ), res.unwrap_err(), @@ -2638,7 +2746,8 @@ fn parse_exists_subquery() { let res = parse_sql_statements("SELECT EXISTS (NULL)"); assert_eq!( - ParserError::ParserError("".to_string(), + ParserError::ParserError( + "SELECT EXISTS (".to_string(), "Expected SELECT, VALUES, or a subquery in the query body, found: NULL".to_string() ), res.unwrap_err(), @@ -2828,13 +2937,19 @@ fn parse_drop_table() { let sql = "DROP TABLE"; assert_eq!( - ParserError::ParserError("".to_string(), "Expected identifier, found: EOF".to_string()), + ParserError::ParserError( + "DROP ".to_string(), + "Expected identifier, found: EOF".to_string() + ), parse_sql_statements(sql).unwrap_err(), ); let sql = "DROP TABLE IF EXISTS foo, bar CASCADE RESTRICT"; assert_eq!( - ParserError::ParserError("".to_string(), "Cannot specify both CASCADE and RESTRICT in DROP".to_string()), + ParserError::ParserError( + "DROP TABLE IF EXISTS foo, bar CASCADE ".to_string(), + "Cannot specify both CASCADE and RESTRICT in DROP".to_string() + ), parse_sql_statements(sql).unwrap_err(), ); } @@ -2860,7 +2975,10 @@ fn parse_drop_view() { fn parse_invalid_subquery_without_parens() { let res = parse_sql_statements("SELECT SELECT 1 FROM bar WHERE 1=1 FROM baz"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: 1".to_string()), + ParserError::ParserError( + "SELECT SELECT ".to_string(), + "Expected end of statement, found: 1".to_string() + ), res.unwrap_err() ); } @@ -3074,7 +3192,8 @@ fn lateral_derived() { let sql = "SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id)"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("".to_string(), + ParserError::ParserError( + "SELECT * FROM customer LEFT JOIN LATERAL".to_string(), "Expected subquery after LATERAL, found: generate_series".to_string() ), res.unwrap_err() @@ -3083,7 +3202,8 @@ fn lateral_derived() { let sql = "SELECT * FROM a LEFT JOIN LATERAL (b CROSS JOIN c)"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("".to_string(), + ParserError::ParserError( + "SELECT * FROM a LEFT JOIN LATERAL (".to_string(), "Expected SELECT, VALUES, or a subquery in the query body, found: b".to_string() ), res.unwrap_err() @@ -3144,19 +3264,28 @@ fn parse_start_transaction() { let res = parse_sql_statements("START TRANSACTION ISOLATION LEVEL BAD"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected isolation level, found: BAD".to_string()), + ParserError::ParserError( + "START TRANSACTION ISOLATION LEVEL".to_string(), + "Expected isolation level, found: BAD".to_string() + ), res.unwrap_err() ); let res = parse_sql_statements("START TRANSACTION BAD"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: BAD".to_string()), + ParserError::ParserError( + "START TRANSACTION".to_string(), + "Expected end of statement, found: BAD".to_string() + ), res.unwrap_err() ); let res = parse_sql_statements("START TRANSACTION READ ONLY,"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected transaction mode, found: EOF".to_string()), + ParserError::ParserError( + "START TRANSACTION READ ONLY".to_string(), + "Expected transaction mode, found: EOF".to_string() + ), res.unwrap_err() ); } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index f0891bbd7..30c09a050 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -280,25 +280,37 @@ fn parse_create_table_if_not_exists() { fn parse_bad_if_not_exists() { let res = pg().parse_sql_statements("CREATE TABLE NOT EXISTS uk_cities ()"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: EXISTS".to_string()), + ParserError::ParserError( + "CREATE TABLE NOT".to_string(), + "Expected end of statement, found: EXISTS".to_string() + ), res.unwrap_err() ); let res = pg().parse_sql_statements("CREATE TABLE IF EXISTS uk_cities ()"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: EXISTS".to_string()), + ParserError::ParserError( + "CREATE TABLE IF".to_string(), + "Expected end of statement, found: EXISTS".to_string() + ), res.unwrap_err() ); let res = pg().parse_sql_statements("CREATE TABLE IF uk_cities ()"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: uk_cities".to_string()), + ParserError::ParserError( + "CREATE TABLE IF".to_string(), + "Expected end of statement, found: uk_cities".to_string() + ), res.unwrap_err() ); let res = pg().parse_sql_statements("CREATE TABLE IF NOT uk_cities ()"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: NOT".to_string()), + ParserError::ParserError( + "CREATE TABLE IF".to_string(), + "Expected end of statement, found: NOT".to_string() + ), res.unwrap_err() ); } @@ -414,21 +426,24 @@ fn parse_set() { assert_eq!( pg_and_generic().parse_sql_statements("SET"), - Err(ParserError::ParserError("".to_string(), + Err(ParserError::ParserError( + "".to_string(), "Expected identifier, found: EOF".to_string() )), ); assert_eq!( pg_and_generic().parse_sql_statements("SET a b"), - Err(ParserError::ParserError("".to_string(), + Err(ParserError::ParserError( + "SET a".to_string(), "Expected equals sign or TO, found: b".to_string() )), ); assert_eq!( pg_and_generic().parse_sql_statements("SET a ="), - Err(ParserError::ParserError("".to_string(), + Err(ParserError::ParserError( + "SET a ".to_string(), "Expected variable value, found: EOF".to_string() )), ); diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 19b1d8ae5..4e4b321ef 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -24,6 +24,7 @@ use sqlparser::parser::ParserError; use sqlparser::tokenizer::*; #[test] +#[ignore] fn test_snowflake_create_table() { let sql = "CREATE TABLE _my_$table (am00unt number)"; match snowflake_and_generic().verified_stmt(sql) { @@ -133,13 +134,19 @@ fn test_single_table_in_parenthesis_with_alias() { let res = snowflake_and_generic().parse_sql_statements("SELECT * FROM (a NATURAL JOIN b) c"); assert_eq!( - ParserError::ParserError("".to_string(), "Expected end of statement, found: c".to_string()), + ParserError::ParserError( + "SELECT * FROM (a NATURAL JOIN b)".to_string(), + "Expected end of statement, found: c".to_string() + ), res.unwrap_err() ); let res = snowflake().parse_sql_statements("SELECT * FROM (a b) c"); assert_eq!( - ParserError::ParserError("".to_string(), "duplicate alias b".to_string()), + ParserError::ParserError( + "SELECT * FROM (a b) ".to_string(), + "duplicate alias b".to_string() + ), res.unwrap_err() ); }