From 73349c9f967f789c6d4406fc8a0da1d5ac949e6e Mon Sep 17 00:00:00 2001 From: Ittai Dafner Date: Mon, 10 Nov 2025 11:55:11 +0200 Subject: [PATCH] parser: attach a span to most common errors describing the problematic token --- src/ast/spans.rs | 2 +- src/parser/mod.rs | 193 +++++++------ src/tokenizer.rs | 7 +- tests/sqlparser_bigquery.rs | 41 +-- tests/sqlparser_clickhouse.rs | 73 +++-- tests/sqlparser_common.rs | 525 +++++++++++++++++++++++++--------- tests/sqlparser_databricks.rs | 4 +- tests/sqlparser_duckdb.rs | 2 +- tests/sqlparser_hive.rs | 29 +- tests/sqlparser_mssql.rs | 9 +- tests/sqlparser_mysql.rs | 4 +- tests/sqlparser_postgres.rs | 60 ++-- tests/sqlparser_snowflake.rs | 66 +++-- tests/sqlparser_sqlite.rs | 7 +- 14 files changed, 694 insertions(+), 328 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 7d2a00095..ac08367ff 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -2511,7 +2511,7 @@ pub mod tests { span, })) => { assert_eq!(":fooBar", s); - assert_eq!(&Span::new((3, 3).into(), (3, 10).into()), span); + assert_eq!(&Span::new((3, 3), (3, 10)), span); } _ => panic!("expected unnamed expression; got {col:?}"), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9a01e510b..8411f68e6 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -51,13 +51,14 @@ mod alter; pub enum ParserError { TokenizerError(String), ParserError(String), + SpannedParserError(String, Span), RecursionLimitExceeded, } // Use `Parser::expected` instead, if possible macro_rules! parser_err { - ($MSG:expr, $loc:expr) => { - Err(ParserError::ParserError(format!("{}{}", $MSG, $loc))) + ($MSG:expr, $span:expr) => { + Err(ParserError::SpannedParserError(format!("{}", $MSG), $span)) }; } @@ -174,21 +175,41 @@ impl From for ParserError { impl fmt::Display for ParserError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "sql parser error: {}", - match self { - ParserError::TokenizerError(s) => s, - ParserError::ParserError(s) => s, - ParserError::RecursionLimitExceeded => "recursion limit exceeded", - } - ) + if let ParserError::SpannedParserError(m, s) = self { + write!(f, "sql parser error: {}{}", m, s.start) + } else { + write!(f, "sql parser error: {}", self.message()) + } } } #[cfg(feature = "std")] impl std::error::Error for ParserError {} +impl ParserError { + /// Return the short error message of this error. This is only the error message, not including + /// any associated metadata about the error. If you are directly displaying the error to a human, + /// prefer directly formatting this object (which uses its [`Display`] implementation) + /// or calling `self.to_string()`. + pub fn message(&self) -> &str { + match self { + ParserError::TokenizerError(m) => m, + ParserError::ParserError(m) => m, + ParserError::SpannedParserError(m, _) => m, + ParserError::RecursionLimitExceeded => "recursion limit exceeded", + } + } + + /// Return the problematic span in the query from which this error originates, if + /// one is associated with this error. + pub fn span(&self) -> Option { + match self { + ParserError::SpannedParserError(_, s) => Some(*s), + _ => None, + } + } +} + // By default, allow expressions up to this deep before erroring const DEFAULT_REMAINING_DEPTH: usize = 50; @@ -882,7 +903,7 @@ impl<'a> Parser<'a> { let mut export = false; if !dialect_of!(self is MySqlDialect | GenericDialect) { - return parser_err!("Unsupported statement FLUSH", self.peek_token().span.start); + return parser_err!("Unsupported statement FLUSH", self.peek_token().span); } let location = if self.parse_keyword(Keyword::NO_WRITE_TO_BINLOG) { @@ -1541,7 +1562,7 @@ impl<'a> Parser<'a> { // Note also that naively `SELECT date` looks like a syntax error because the `date` type // name is not followed by a string literal, but in fact in PostgreSQL it is a valid // expression that should parse as the column name "date". - let loc = self.peek_token_ref().span.start; + let span = self.peek_token_ref().span; let opt_expr = self.maybe_parse(|parser| { match parser.parse_data_type()? { DataType::Interval { .. } => parser.parse_interval(), @@ -1552,7 +1573,7 @@ impl<'a> Parser<'a> { // name, resulting in `NOT 'a'` being recognized as a `TypedString` instead of // an unary negation `NOT ('a' LIKE 'b')`. To solve this, we don't accept the // `type 'string'` syntax for the custom data types at all. - DataType::Custom(..) => parser_err!("dummy", loc), + DataType::Custom(..) => parser_err!("dummy", span), data_type => Ok(Expr::TypedString(TypedString { data_type, value: parser.parse_value()?, @@ -1667,9 +1688,10 @@ impl<'a> Parser<'a> { Token::QuestionMarkDash => UnaryOperator::QuestionDash, Token::QuestionPipe => UnaryOperator::QuestionPipe, _ => { - return Err(ParserError::ParserError(format!( - "Unexpected token in unary operator parsing: {tok:?}" - ))) + return Err(ParserError::SpannedParserError( + format!("Unexpected token in unary operator parsing: {tok:?}"), + span, + )) } }; Ok(Expr::UnaryOp { @@ -1879,7 +1901,7 @@ impl<'a> Parser<'a> { .all(|access| matches!(access, AccessExpr::Dot(Expr::Identifier(_)))) { let Some(AccessExpr::Dot(Expr::Function(mut func))) = access_chain.pop() else { - return parser_err!("expected function expression", root.span().start); + return parser_err!("expected function expression", root.span()); }; let compound_func_name = [root] @@ -1911,20 +1933,20 @@ impl<'a> Parser<'a> { ) { let Some(AccessExpr::Dot(Expr::OuterJoin(inner_expr))) = access_chain.pop() else { - return parser_err!("expected (+) expression", root.span().start); + return parser_err!("expected (+) expression", root.span()); }; if !Self::is_all_ident(&root, &[]) { - return parser_err!("column identifier before (+)", root.span().start); + return parser_err!("column identifier before (+)", root.span()); }; - let token_start = root.span().start; + let token_span = root.span(); let mut idents = Self::exprs_to_idents(root, vec![])?; match *inner_expr { Expr::CompoundIdentifier(suffix) => idents.extend(suffix), Expr::Identifier(suffix) => idents.push(suffix), _ => { - return parser_err!("column identifier before (+)", token_start); + return parser_err!("column identifier before (+)", token_span); } } @@ -1965,18 +1987,12 @@ impl<'a> Parser<'a> { if let AccessExpr::Dot(Expr::Identifier(ident)) = x { idents.push(ident); } else { - return parser_err!( - format!("Expected identifier, found: {}", x), - x.span().start - ); + return parser_err!(format!("Expected identifier, found: {}", x), x.span()); } } Ok(idents) } else { - parser_err!( - format!("Expected identifier, found: {}", root), - root.span().start - ) + parser_err!(format!("Expected identifier, found: {}", root), root.span()) } } @@ -2505,8 +2521,9 @@ impl<'a> Parser<'a> { { ExtractSyntax::Comma } else { - return Err(ParserError::ParserError( + return Err(ParserError::SpannedParserError( "Expected 'FROM' or ','".to_string(), + self.peek_token_ref().span, )); }; @@ -2528,11 +2545,13 @@ impl<'a> Parser<'a> { CeilFloorKind::DateTimeField(self.parse_date_time_field()?) } else if self.consume_token(&Token::Comma) { // Parse `CEIL/FLOOR(expr, scale)` - match self.parse_value()?.value { + let ValueWithSpan { value, span } = self.parse_value()?; + match value { Value::Number(n, s) => CeilFloorKind::Scale(Value::Number(n, s)), _ => { - return Err(ParserError::ParserError( + return Err(ParserError::SpannedParserError( "Scale field can only be of number type".to_string(), + span, )) } } @@ -2949,7 +2968,7 @@ impl<'a> Parser<'a> { } else if self.dialect.require_interval_qualifier() { return parser_err!( "INTERVAL requires a unit after the literal value", - self.peek_token().span.start + value.span() ); } else { None @@ -3048,10 +3067,7 @@ 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); } // Parse the struct values `(expr1 [, ... ])` @@ -3082,7 +3098,7 @@ impl<'a> Parser<'a> { if typed_syntax { return parser_err!("Typed syntax does not allow AS", { self.prev_token(); - self.peek_token().span.start + self.peek_token().span }); } let field_name = self.parse_identifier()?; @@ -3574,7 +3590,7 @@ impl<'a> Parser<'a> { format!( "Expected one of [=, >, <, =>, =<, !=, ~, ~*, !~, !~*, ~~, ~~*, !~~, !~~*] as comparison operator, found: {op}" ), - span.start + span ); }; @@ -3725,7 +3741,7 @@ impl<'a> Parser<'a> { // Can only happen if `get_next_precedence` got out of sync with this function _ => parser_err!( format!("No infix parser for token {:?}", tok.token), - tok.span.start + tok.span ), } } else if Token::DoubleColon == *tok { @@ -3749,7 +3765,7 @@ impl<'a> Parser<'a> { // Can only happen if `get_next_precedence` got out of sync with this function parser_err!( format!("No infix parser for token {:?}", tok.token), - tok.span.start + tok.span ) } } @@ -4205,27 +4221,18 @@ impl<'a> Parser<'a> { /// Report `found` was encountered instead of `expected` pub fn expected(&self, expected: &str, found: TokenWithSpan) -> Result { - parser_err!( - format!("Expected: {expected}, found: {found}"), - found.span.start - ) + parser_err!(format!("Expected: {expected}, found: {found}"), found.span) } /// report `found` was encountered instead of `expected` pub fn expected_ref(&self, expected: &str, found: &TokenWithSpan) -> Result { - parser_err!( - format!("Expected: {expected}, found: {found}"), - found.span.start - ) + parser_err!(format!("Expected: {expected}, found: {found}"), found.span) } /// Report that the token at `index` was found instead of `expected`. pub fn expected_at(&self, expected: &str, index: usize) -> Result { let found = self.tokens.get(index).unwrap_or(&EOF_TOKEN); - parser_err!( - format!("Expected: {expected}, found: {found}"), - found.span.start - ) + parser_err!(format!("Expected: {expected}, found: {found}"), found.span) } /// If the current token is the `expected` keyword, consume it and returns @@ -4693,14 +4700,14 @@ impl<'a> Parser<'a> { /// Parse either `ALL`, `DISTINCT` or `DISTINCT ON (...)`. Returns [`None`] if `ALL` is parsed /// 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 span = self.peek_token().span; 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); + return parser_err!("Cannot specify both ALL and DISTINCT".to_string(), span); } let on = self.parse_keyword(Keyword::ON); if !on { @@ -5411,7 +5418,7 @@ impl<'a> Parser<'a> { } _ => parser_err!( "Expected table column definitions after TABLE keyword", - p.peek_token().span.start + return_table_name.span.union(&p.peek_token().span) )?, }; @@ -5448,11 +5455,11 @@ impl<'a> Parser<'a> { } else { parser_err!( "Expected a subquery (or bare SELECT statement) after RETURN", - self.peek_token().span.start + self.peek_token().span )? } } else { - parser_err!("Unparsable function body", self.peek_token().span.start)? + parser_err!("Unparsable function body", self.peek_token().span)? }; Ok(Statement::CreateFunction(CreateFunction { @@ -6084,7 +6091,7 @@ impl<'a> Parser<'a> { let loc = self .tokens .get(self.index - 1) - .map_or(Location { line: 0, column: 0 }, |t| t.span.start); + .map_or(Span::empty(), |t| t.span); match keyword { Keyword::AUTHORIZATION => { if authorization_owner.is_some() { @@ -6479,17 +6486,17 @@ impl<'a> Parser<'a> { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; - let loc = self.peek_token().span.start; + let span = self.peek_token().span; 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); + return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP", span); } if object_type == ObjectType::Role && (cascade || restrict || purge) { return parser_err!( "Cannot specify CASCADE, RESTRICT, or PURGE in DROP ROLE", - loc + span ); } let table = if self.parse_keyword(Keyword::ON) { @@ -7045,7 +7052,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::IN)?; FetchPosition::In } else { - return parser_err!("Expected FROM or IN", self.peek_token().span.start); + return parser_err!("Expected FROM or IN", self.peek_token().span); }; let name = self.parse_identifier()?; @@ -7556,7 +7563,7 @@ impl<'a> Parser<'a> { } else { parser_err!( "Expecting DELETE ROWS, PRESERVE ROWS or DROP", - self.peek_token() + self.peek_token().span ) } } @@ -9582,7 +9589,7 @@ impl<'a> Parser<'a> { Expr::Function(f) => Ok(Statement::Call(f)), other => parser_err!( format!("Expected a simple procedure call but found: {other}"), - self.peek_token().span.start + self.peek_token().span ), } } else { @@ -9981,11 +9988,11 @@ impl<'a> Parser<'a> { fn parse_literal_char(&mut self) -> Result { let s = self.parse_literal_string()?; if s.len() != 1 { - let loc = self + let span = self .tokens .get(self.index - 1) - .map_or(Location { line: 0, column: 0 }, |t| t.span.start); - return parser_err!(format!("Expect a char, found {s:?}"), loc); + .map_or(Span::empty(), |t| t.span); + return parser_err!(format!("Expect a char, found {s:?}"), span); } Ok(s.chars().next().unwrap()) } @@ -10311,7 +10318,7 @@ impl<'a> Parser<'a> { if trailing_bracket.0 { return parser_err!( format!("unmatched > after parsing data type {ty}"), - self.peek_token() + self.peek_token().span ); } @@ -10987,7 +10994,7 @@ impl<'a> Parser<'a> { _ => { return parser_err!( "BUG: expected to match GroupBy modifier keyword", - self.peek_token().span.start + self.peek_token().span ) } }); @@ -14063,7 +14070,7 @@ impl<'a> Parser<'a> { } else { return parser_err!( "Expecting number or byte length e.g. 100M", - self.peek_token().span.start + self.peek_token().span ); } } @@ -14291,7 +14298,7 @@ impl<'a> Parser<'a> { "Expected one of DIMENSIONS, METRICS, FACTS or WHERE, got {}", self.peek_token().token ), - self.peek_token().span.start + self.peek_token().span )?; } } @@ -15402,7 +15409,7 @@ impl<'a> Parser<'a> { None => { return parser_err!( "DENY statements must specify an object", - self.peek_token().span.start + self.peek_token().span ) } }; @@ -15454,7 +15461,7 @@ impl<'a> Parser<'a> { if !dialect_of!(self is MySqlDialect | GenericDialect) { return parser_err!( "Unsupported statement REPLACE", - self.peek_token().span.start + self.get_current_token().span ); } @@ -16008,7 +16015,7 @@ impl<'a> Parser<'a> { } fn parse_duplicate_treatment(&mut self) -> Result, ParserError> { - let loc = self.peek_token().span.start; + let span = self.peek_token().span; match ( self.parse_keyword(Keyword::ALL), self.parse_keyword(Keyword::DISTINCT), @@ -16016,7 +16023,7 @@ impl<'a> Parser<'a> { (true, false) => Ok(Some(DuplicateTreatment::All)), (false, true) => Ok(Some(DuplicateTreatment::Distinct)), (false, false) => Ok(None), - (true, true) => parser_err!("Cannot specify both ALL and DISTINCT".to_string(), loc), + (true, true) => parser_err!("Cannot specify both ALL and DISTINCT".to_string(), span), } } @@ -16040,7 +16047,7 @@ impl<'a> Parser<'a> { Expr::Identifier(v) if v.value.to_lowercase() == "from" && v.quote_style.is_none() => { parser_err!( format!("Expected an expression, found: {}", v), - self.peek_token().span.start + self.peek_token().span ) } Expr::BinaryOp { @@ -16053,7 +16060,7 @@ impl<'a> Parser<'a> { let Expr::Identifier(alias) = *left else { return parser_err!( "BUG: expected identifier expression as alias", - self.peek_token().span.start + self.peek_token().span ); }; Ok(SelectItem::ExprWithAlias { @@ -16894,9 +16901,10 @@ impl<'a> Parser<'a> { clause_kind, MergeClauseKind::NotMatched | MergeClauseKind::NotMatchedByTarget ) { - return Err(ParserError::ParserError(format!( - "UPDATE is not allowed in a {clause_kind} merge clause" - ))); + return Err(ParserError::SpannedParserError( + format!("UPDATE is not allowed in a {clause_kind} merge clause"), + self.get_current_token().span, + )); } self.expect_keyword_is(Keyword::SET)?; MergeAction::Update { @@ -16908,9 +16916,10 @@ impl<'a> Parser<'a> { clause_kind, MergeClauseKind::NotMatched | MergeClauseKind::NotMatchedByTarget ) { - return Err(ParserError::ParserError(format!( - "DELETE is not allowed in a {clause_kind} merge clause" - ))); + return Err(ParserError::SpannedParserError( + format!("DELETE is not allowed in a {clause_kind} merge clause"), + self.get_current_token().span, + )); } MergeAction::Delete } @@ -16919,9 +16928,10 @@ impl<'a> Parser<'a> { clause_kind, MergeClauseKind::NotMatched | MergeClauseKind::NotMatchedByTarget ) { - return Err(ParserError::ParserError(format!( - "INSERT is not allowed in a {clause_kind} merge clause" - ))); + return Err(ParserError::SpannedParserError( + format!("INSERT is not allowed in a {clause_kind} merge clause"), + self.get_current_token().span, + )); } let is_mysql = dialect_of!(self is MySqlDialect); @@ -18378,9 +18388,10 @@ mod tests { let ast = Parser::parse_sql(&GenericDialect, sql); assert_eq!( ast, - Err(ParserError::ParserError( - "Expected: [NOT] NULL | TRUE | FALSE | DISTINCT | [form] NORMALIZED FROM after IS, found: a at Line: 1, Column: 16" - .to_string() + Err(ParserError::SpannedParserError( + "Expected: [NOT] NULL | TRUE | FALSE | DISTINCT | [form] NORMALIZED FROM after IS, found: a" + .to_string(), + Span::new(Location::new(1, 16), Location::new(1, 17)) )) ); } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 54a158c1f..6aa23046d 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -574,8 +574,11 @@ impl Span { const EMPTY: Span = Self::empty(); /// Create a new span from a start and end [`Location`] - pub fn new(start: Location, end: Location) -> Span { - Span { start, end } + pub fn new(start: impl Into, end: impl Into) -> Span { + Span { + start: start.into(), + end: end.into(), + } } /// Returns an empty span `(0, 0) -> (0, 0)` diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 03a0ac813..226805452 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -295,13 +295,13 @@ fn parse_begin() { bigquery() .parse_sql_statements("BEGIN SELECT 1; SELECT 2 END") .unwrap_err(), - ParserError::ParserError("Expected: ;, found: END".to_string()) + ParserError::SpannedParserError("Expected: ;, found: END".to_string(), Span::empty()) ); assert_eq!( bigquery() .parse_sql_statements("BEGIN SELECT 1; EXCEPTION WHEN ERROR THEN SELECT 2 END") .unwrap_err(), - ParserError::ParserError("Expected: ;, found: END".to_string()) + ParserError::SpannedParserError("Expected: ;, found: END".to_string(), Span::empty()) ); } @@ -2008,7 +2008,7 @@ fn parse_merge_invalid_statements() { ] { let res = dialects.parse_sql_statements(sql); assert_eq!( - ParserError::ParserError(err_msg.to_string()), + ParserError::SpannedParserError(err_msg.to_string(), Span::empty()), res.unwrap_err() ); } @@ -2132,13 +2132,19 @@ fn parse_big_query_declare() { let error_sql = "DECLARE x"; assert_eq!( - ParserError::ParserError("Expected: a data type name, found: EOF".to_owned()), + ParserError::SpannedParserError( + "Expected: a data type name, found: EOF".to_owned(), + Span::empty() + ), bigquery().parse_sql_statements(error_sql).unwrap_err() ); let error_sql = "DECLARE x 42"; assert_eq!( - ParserError::ParserError("Expected: a data type name, found: 42".to_owned()), + ParserError::SpannedParserError( + "Expected: a data type name, found: 42".to_owned(), + Span::empty() + ), bigquery().parse_sql_statements(error_sql).unwrap_err() ); } @@ -2342,7 +2348,7 @@ fn test_bigquery_create_function() { ]; for (sql, error) in error_sqls { assert_eq!( - ParserError::ParserError(error.to_owned()), + ParserError::SpannedParserError(error.to_owned(), Span::empty()), bigquery().parse_sql_statements(sql).unwrap_err() ); } @@ -2372,7 +2378,7 @@ fn test_bigquery_trim() { // missing comma separation let error_sql = "SELECT TRIM('xyz' 'a')"; assert_eq!( - ParserError::ParserError("Expected: ), found: 'a'".to_owned()), + ParserError::SpannedParserError("Expected: ), found: 'a'".to_owned(), Span::empty()), bigquery().parse_sql_statements(error_sql).unwrap_err() ); } @@ -2535,7 +2541,7 @@ fn test_struct_trailing_and_nested_bracket() { // Bad case with missing closing bracket assert_eq!( - ParserError::ParserError("Expected: >, found: )".to_owned()), + ParserError::SpannedParserError("Expected: >, found: )".to_owned(), Span::empty()), bigquery() .parse_sql_statements("CREATE TABLE my_table(f1 STRUCT after parsing data type STRUCT)".to_owned() + ParserError::SpannedParserError( + "unmatched > after parsing data type STRUCT".to_owned(), + Span::empty() ), bigquery() .parse_sql_statements("CREATE TABLE my_table(f1 STRUCT>)") @@ -2553,8 +2560,9 @@ fn test_struct_trailing_and_nested_bracket() { // Base case with redundant closing bracket in nested struct assert_eq!( - ParserError::ParserError( - "Expected: ',' or ')' after column definition, found: >".to_owned() + ParserError::SpannedParserError( + "Expected: ',' or ')' after column definition, found: >".to_owned(), + Span::empty() ), bigquery() .parse_sql_statements("CREATE TABLE my_table(f1 STRUCT>>, c INT64)") @@ -2566,7 +2574,7 @@ fn test_struct_trailing_and_nested_bracket() { bigquery_and_generic() .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError("unmatched > in STRUCT literal".to_string()) + ParserError::SpannedParserError("unmatched > in STRUCT literal".to_string(), Span::empty()) ); let sql = "SELECT STRUCT>>(NULL)"; @@ -2574,7 +2582,7 @@ fn test_struct_trailing_and_nested_bracket() { bigquery_and_generic() .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError("Expected: (, found: >".to_string()) + ParserError::SpannedParserError("Expected: (, found: >".to_string(), Span::empty()) ); let sql = "CREATE TABLE table (x STRUCT>>)"; @@ -2582,8 +2590,9 @@ fn test_struct_trailing_and_nested_bracket() { bigquery_and_generic() .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError( - "Expected: ',' or ')' after column definition, found: >".to_string() + ParserError::SpannedParserError( + "Expected: ',' or ')' after column definition, found: >".to_string(), + Span::empty() ) ); } diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 44bfcda42..e56fc7634 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -32,7 +32,7 @@ use sqlparser::ast::Value::Boolean; use sqlparser::ast::*; use sqlparser::dialect::ClickHouseDialect; use sqlparser::dialect::GenericDialect; -use sqlparser::parser::ParserError::ParserError; +use sqlparser::parser::ParserError; #[test] fn parse_map_access_expr() { @@ -289,13 +289,19 @@ fn parse_alter_table_attach_and_detach_partition() { clickhouse_and_generic() .parse_sql_statements(format!("ALTER TABLE t0 {operation} PARTITION").as_str()) .unwrap_err(), - ParserError("Expected: an expression, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_string(), + Span::empty() + ) ); assert_eq!( clickhouse_and_generic() .parse_sql_statements(format!("ALTER TABLE t0 {operation} PART").as_str()) .unwrap_err(), - ParserError("Expected: an expression, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_string(), + Span::empty() + ) ); } } @@ -358,19 +364,22 @@ fn parse_alter_table_add_projection() { clickhouse_and_generic() .parse_sql_statements("ALTER TABLE t0 ADD PROJECTION my_name") .unwrap_err(), - ParserError("Expected: (, found: EOF".to_string()) + ParserError::SpannedParserError("Expected: (, found: EOF".to_string(), Span::empty()) ); assert_eq!( clickhouse_and_generic() .parse_sql_statements("ALTER TABLE t0 ADD PROJECTION my_name ()") .unwrap_err(), - ParserError("Expected: SELECT, found: )".to_string()) + ParserError::SpannedParserError("Expected: SELECT, found: )".to_string(), Span::empty()) ); assert_eq!( clickhouse_and_generic() .parse_sql_statements("ALTER TABLE t0 ADD PROJECTION my_name (SELECT)") .unwrap_err(), - ParserError("Expected: an expression, found: )".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: )".to_string(), + Span::empty() + ) ); } @@ -400,7 +409,10 @@ fn parse_alter_table_drop_projection() { clickhouse_and_generic() .parse_sql_statements("ALTER TABLE t0 DROP PROJECTION") .unwrap_err(), - ParserError("Expected: identifier, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ) ); } @@ -447,7 +459,10 @@ fn parse_alter_table_clear_and_materialize_projection() { clickhouse_and_generic() .parse_sql_statements(format!("ALTER TABLE t0 {keyword} PROJECTION",).as_str()) .unwrap_err(), - ParserError("Expected: identifier, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ) ); assert_eq!( @@ -456,7 +471,10 @@ fn parse_alter_table_clear_and_materialize_projection() { format!("ALTER TABLE t0 {keyword} PROJECTION my_name IN PARTITION",).as_str() ) .unwrap_err(), - ParserError("Expected: identifier, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ) ); assert_eq!( @@ -465,7 +483,10 @@ fn parse_alter_table_clear_and_materialize_projection() { format!("ALTER TABLE t0 {keyword} PROJECTION my_name IN",).as_str() ) .unwrap_err(), - ParserError("Expected: end of statement, found: IN".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: IN".to_string(), + Span::empty() + ) ); } } @@ -513,19 +534,28 @@ fn parse_optimize_table() { clickhouse_and_generic() .parse_sql_statements("OPTIMIZE TABLE t0 DEDUPLICATE BY") .unwrap_err(), - ParserError("Expected: an expression, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_string(), + Span::empty() + ) ); assert_eq!( clickhouse_and_generic() .parse_sql_statements("OPTIMIZE TABLE t0 PARTITION") .unwrap_err(), - ParserError("Expected: an expression, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_string(), + Span::empty() + ) ); assert_eq!( clickhouse_and_generic() .parse_sql_statements("OPTIMIZE TABLE t0 PARTITION ID") .unwrap_err(), - ParserError("Expected: identifier, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ) ); } @@ -1063,7 +1093,7 @@ fn parse_settings_in_query() { clickhouse_and_generic() .parse_sql_statements(sql) .unwrap_err(), - ParserError(error_msg.to_string()) + ParserError::SpannedParserError(error_msg.to_string(), Span::empty()) ); } } @@ -1568,7 +1598,10 @@ fn parse_freeze_and_unfreeze_partition() { clickhouse_and_generic() .parse_sql_statements(format!("ALTER TABLE t0 {operation_name} PARTITION").as_str()) .unwrap_err(), - ParserError("Expected: an expression, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_string(), + Span::empty() + ) ); assert_eq!( clickhouse_and_generic() @@ -1576,7 +1609,10 @@ fn parse_freeze_and_unfreeze_partition() { format!("ALTER TABLE t0 {operation_name} PARTITION p0 WITH").as_str() ) .unwrap_err(), - ParserError("Expected: NAME, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: NAME, found: EOF".to_string(), + Span::empty() + ) ); assert_eq!( clickhouse_and_generic() @@ -1584,7 +1620,10 @@ fn parse_freeze_and_unfreeze_partition() { format!("ALTER TABLE t0 {operation_name} PARTITION p0 WITH NAME").as_str() ) .unwrap_err(), - ParserError("Expected: identifier, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ) ); } } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index f1ba5df04..4f910c32f 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -172,7 +172,10 @@ fn parse_replace_into() { let sql = "REPLACE INTO public.customer (id, name, active) VALUES (1, 2, 3)"; assert_eq!( - ParserError::ParserError("Unsupported statement REPLACE at Line: 1, Column: 9".to_string()), + ParserError::SpannedParserError( + "Unsupported statement REPLACE".to_string(), + Span::new((1, 1), (1, 8)) + ), Parser::parse_sql(&dialect, sql,).unwrap_err(), ) } @@ -264,8 +267,9 @@ fn parse_insert_default_values() { let insert_with_columns_and_default_values = "INSERT INTO test_table (test_col) DEFAULT VALUES"; assert_eq!( - ParserError::ParserError( - "Expected: SELECT, VALUES, or a subquery in the query body, found: DEFAULT".to_string() + ParserError::SpannedParserError( + "Expected: SELECT, VALUES, or a subquery in the query body, found: DEFAULT".to_string(), + Span::empty(), ), parse_sql_statements(insert_with_columns_and_default_values).unwrap_err() ); @@ -273,20 +277,29 @@ fn parse_insert_default_values() { let insert_with_default_values_and_hive_after_columns = "INSERT INTO test_table DEFAULT VALUES (some_column)"; assert_eq!( - ParserError::ParserError("Expected: end of statement, found: (".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: (".to_string(), + Span::empty() + ), parse_sql_statements(insert_with_default_values_and_hive_after_columns).unwrap_err() ); let insert_with_default_values_and_hive_partition = "INSERT INTO test_table DEFAULT VALUES PARTITION (some_column)"; assert_eq!( - ParserError::ParserError("Expected: end of statement, found: PARTITION".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: PARTITION".to_string(), + Span::empty() + ), parse_sql_statements(insert_with_default_values_and_hive_partition).unwrap_err() ); let insert_with_default_values_and_values_list = "INSERT INTO test_table DEFAULT VALUES (1)"; assert_eq!( - ParserError::ParserError("Expected: end of statement, found: (".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: (".to_string(), + Span::empty() + ), parse_sql_statements(insert_with_default_values_and_values_list).unwrap_err() ); } @@ -411,14 +424,17 @@ fn parse_update() { let sql = "UPDATE t WHERE 1"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: SET, found: WHERE".to_string()), + ParserError::SpannedParserError("Expected: SET, found: WHERE".to_string(), Span::empty()), res.unwrap_err() ); let sql = "UPDATE t SET a = 1 extrabadstuff"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: extrabadstuff".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: extrabadstuff".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -698,7 +714,7 @@ fn parse_delete_without_from_error() { let dialects = all_dialects_except(|d| d.is::() || d.is::()); let res = dialects.parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: FROM, found: WHERE".to_string()), + ParserError::SpannedParserError("Expected: FROM, found: WHERE".to_string(), Span::empty()), res.unwrap_err() ); } @@ -1017,7 +1033,10 @@ fn parse_outer_join_operator() { let res = dialects.parse_sql_statements("SELECT 1 FROM T WHERE 1 = 2 (+)"); assert_eq!( - ParserError::ParserError("Expected: column identifier before (+), found: 2".to_string()), + ParserError::SpannedParserError( + "Expected: column identifier before (+), found: 2".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -1050,7 +1069,7 @@ fn parse_select_distinct_on() { fn parse_select_distinct_missing_paren() { let result = parse_sql_statements("SELECT DISTINCT (name, id FROM customer"); assert_eq!( - ParserError::ParserError("Expected: ), found: FROM".to_string()), + ParserError::SpannedParserError("Expected: ), found: FROM".to_string(), Span::empty()), result.unwrap_err(), ); } @@ -1064,7 +1083,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("Cannot specify both ALL and DISTINCT".to_string()), + ParserError::SpannedParserError( + "Cannot specify both ALL and DISTINCT".to_string(), + Span::empty() + ), result.unwrap_err(), ); } @@ -1094,7 +1116,10 @@ fn parse_select_into() { let sql = "SELECT * INTO table0 asdf FROM table1"; let result = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: asdf".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: asdf".to_string(), + Span::empty() + ), result.unwrap_err() ) } @@ -1134,7 +1159,10 @@ fn parse_select_wildcard() { let sql = "SELECT * + * FROM foo;"; let result = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: +".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: +".to_string(), + Span::empty() + ), result.unwrap_err(), ); } @@ -1242,7 +1270,10 @@ fn parse_select_expr_star() { // Invalid let res = dialects.parse_sql_statements("SELECT foo.*.* FROM T"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: .".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: .".to_string(), + Span::empty() + ), res.unwrap_err() ); @@ -1256,13 +1287,19 @@ fn parse_select_expr_star() { fn test_eof_after_as() { let res = parse_sql_statements("SELECT foo AS"); assert_eq!( - ParserError::ParserError("Expected: an identifier after AS, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: an identifier after AS, found: EOF".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = parse_sql_statements("SELECT 1 FROM foo AS"); assert_eq!( - ParserError::ParserError("Expected: an identifier after AS, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: an identifier after AS, found: EOF".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -1273,7 +1310,10 @@ fn test_no_infix_error() { let res = dialects.parse_sql_statements("ASSERT-URA<<"); assert_eq!( - ParserError::ParserError("No infix parser for token ShiftLeft".to_string()), + ParserError::SpannedParserError( + "No infix parser for token ShiftLeft".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -1332,7 +1372,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("Cannot specify both ALL and DISTINCT".to_string()), + ParserError::SpannedParserError( + "Cannot specify both ALL and DISTINCT".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -1348,7 +1391,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("Expected: end of statement, found: NOT".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: NOT".to_string(), + Span::empty() + ), res.unwrap_err(), ); } @@ -2302,7 +2348,7 @@ fn parse_in_error() { let sql = "SELECT * FROM customers WHERE segment in segment"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: (, found: segment".to_string()), + ParserError::SpannedParserError("Expected: (, found: segment".to_string(), Span::empty()), res.unwrap_err() ); } @@ -2479,14 +2525,17 @@ fn parse_tuple_invalid() { let sql = "select (1"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: ), found: EOF".to_string()), + ParserError::SpannedParserError("Expected: ), found: EOF".to_string(), Span::empty()), res.unwrap_err() ); let sql = "select (), 2"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: an expression, found: )".to_string()), + ParserError::SpannedParserError( + "Expected: an expression, found: )".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -3198,7 +3247,10 @@ fn parse_extract() { let dialects = all_dialects_except(|d| d.allow_extract_custom()); let res = dialects.parse_sql_statements("SELECT EXTRACT(JIFFY FROM d)"); assert_eq!( - ParserError::ParserError("Expected: date/time field, found: JIFFY".to_string()), + ParserError::SpannedParserError( + "Expected: date/time field, found: JIFFY".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -3297,7 +3349,10 @@ fn parse_ceil_datetime() { let dialects = all_dialects_except(|d| d.allow_extract_custom()); let res = dialects.parse_sql_statements("SELECT CEIL(d TO JIFFY) FROM df"); assert_eq!( - ParserError::ParserError("Expected: date/time field, found: JIFFY".to_string()), + ParserError::SpannedParserError( + "Expected: date/time field, found: JIFFY".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -3324,7 +3379,10 @@ fn parse_floor_datetime() { let dialects = all_dialects_except(|d| d.allow_extract_custom()); let res = dialects.parse_sql_statements("SELECT FLOOR(d TO JIFFY) FROM df"); assert_eq!( - ParserError::ParserError("Expected: date/time field, found: JIFFY".to_string()), + ParserError::SpannedParserError( + "Expected: date/time field, found: JIFFY".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -3529,7 +3587,10 @@ fn parse_window_function_null_treatment_arg() { let sql = "SELECT LAG(1 IGNORE NULLS) IGNORE NULLS OVER () FROM t1"; assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: end of statement, found: NULLS".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: NULLS".to_string(), + Span::empty() + ) ); let sql = "SELECT LAG(1 IGNORE NULLS) IGNORE NULLS OVER () FROM t1"; @@ -3537,7 +3598,7 @@ fn parse_window_function_null_treatment_arg() { all_dialects_where(|d| !d.supports_window_function_null_treatment_arg()) .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError("Expected: ), found: IGNORE".to_string()) + ParserError::SpannedParserError("Expected: ), found: IGNORE".to_string(), Span::empty()) ); } @@ -4258,7 +4319,7 @@ fn parse_create_table_hive_array() { assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: >, found: )".to_string()) + ParserError::SpannedParserError("Expected: >, found: )".to_string(), Span::empty()) ); } @@ -4866,13 +4927,17 @@ fn parse_rename_table() { assert_eq!( parse_sql_statements("RENAME TABLE old_table TO new_table a").unwrap_err(), - ParserError::ParserError("Expected: end of statement, found: a".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: a".to_string(), + Span::empty() + ) ); assert_eq!( parse_sql_statements("RENAME TABLE1 old_table TO new_table a").unwrap_err(), - ParserError::ParserError( - "Expected: KEYWORD `TABLE` after RENAME, found: TABLE1".to_string() + ParserError::SpannedParserError( + "Expected: KEYWORD `TABLE` after RENAME, found: TABLE1".to_string(), + Span::empty(), ) ); } @@ -4907,7 +4972,10 @@ fn test_alter_table_with_on_cluster() { .parse_sql_statements("ALTER TABLE t ON CLUSTER 123 ADD CONSTRAINT bar PRIMARY KEY (baz)"); assert_eq!( res.unwrap_err(), - ParserError::ParserError("Expected: identifier, found: 123".to_string()) + ParserError::SpannedParserError( + "Expected: identifier, found: 123".to_string(), + Span::empty() + ) ) } @@ -5179,7 +5247,10 @@ fn parse_alter_table_alter_column_type() { "{alter_stmt} ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'" )); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: USING".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: USING".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -5210,7 +5281,10 @@ fn parse_alter_table_drop_constraint() { let res = parse_sql_statements("ALTER TABLE tab DROP CONSTRAINT is_active TEXT"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: TEXT".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: TEXT".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -5219,14 +5293,18 @@ fn parse_alter_table_drop_constraint() { fn parse_bad_constraint() { let res = parse_sql_statements("ALTER TABLE tab ADD"); assert_eq!( - ParserError::ParserError("Expected: identifier, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = parse_sql_statements("CREATE TABLE tab (foo int,"); assert_eq!( - ParserError::ParserError( - "Expected: column name or constraint definition, found: EOF".to_string() + ParserError::SpannedParserError( + "Expected: column name or constraint definition, found: EOF".to_string(), + Span::empty(), ), res.unwrap_err() ); @@ -5418,7 +5496,10 @@ fn parse_explain_query_plan() { // missing PLAN keyword should return error assert_eq!( - ParserError::ParserError("Expected: end of statement, found: SELECT".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: SELECT".to_string(), + Span::empty() + ), all_dialects() .parse_sql_statements("EXPLAIN QUERY SELECT sqrt(id) FROM foo") .unwrap_err() @@ -5691,7 +5772,7 @@ fn parse_window_clause() { }); let res = dialects.parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: (, found: window2".to_string()), + ParserError::SpannedParserError("Expected: (, found: window2".to_string(), Span::empty()), res.unwrap_err() ); } @@ -6165,13 +6246,19 @@ fn parse_interval_all() { let result = parse_sql_statements("SELECT INTERVAL '1' SECOND TO SECOND"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: SECOND".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: SECOND".to_string(), + Span::empty() + ), result.unwrap_err(), ); let result = parse_sql_statements("SELECT INTERVAL '10' HOUR (1) TO HOUR (2)"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: (".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: (".to_string(), + Span::empty() + ), result.unwrap_err(), ); @@ -6808,13 +6895,13 @@ fn parse_table_function() { let res = parse_sql_statements("SELECT * FROM TABLE '1' AS a"); assert_eq!( - ParserError::ParserError("Expected: (, found: \'1\'".to_string()), + ParserError::SpannedParserError("Expected: (, found: \'1\'".to_string(), Span::empty()), res.unwrap_err() ); let res = parse_sql_statements("SELECT * FROM TABLE (FUN(a) AS a"); assert_eq!( - ParserError::ParserError("Expected: ), found: AS".to_string()), + ParserError::SpannedParserError("Expected: ), found: AS".to_string(), Span::empty()), res.unwrap_err() ); } @@ -7523,7 +7610,10 @@ fn parse_natural_join() { let sql = "SELECT * FROM t1 natural"; assert_eq!( - ParserError::ParserError("Expected: a join type after NATURAL, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: a join type after NATURAL, found: EOF".to_string(), + Span::empty() + ), parse_sql_statements(sql).unwrap_err(), ); } @@ -7599,7 +7689,7 @@ fn parse_join_syntax_variants() { let dialects = all_dialects_except(|d| d.is_table_alias(&Keyword::OUTER, &mut Parser::new(d))); let res = dialects.parse_sql_statements("SELECT * FROM a OUTER JOIN b ON 1"); assert_eq!( - ParserError::ParserError("Expected: APPLY, found: JOIN".to_string()), + ParserError::SpannedParserError("Expected: APPLY, found: JOIN".to_string(), Span::empty()), res.unwrap_err() ); } @@ -7843,7 +7933,10 @@ 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("Expected: end of statement, found: ".to_string() + sql2_kw), + ParserError::SpannedParserError( + "Expected: end of statement, found: ".to_string() + sql2_kw, + Span::empty() + ), res.unwrap_err() ); } @@ -7911,7 +8004,7 @@ fn parse_overlay() { "SELECT OVERLAY('abccccde' PLACING 'abc' FROM 3 FOR 12)", ); assert_eq!( - ParserError::ParserError("Expected: PLACING, found: FROM".to_owned()), + ParserError::SpannedParserError("Expected: PLACING, found: FROM".to_owned(), Span::empty()), parse_sql_statements("SELECT OVERLAY('abccccde' FROM 3)").unwrap_err(), ); @@ -7962,7 +8055,7 @@ fn parse_trim() { ); assert_eq!( - ParserError::ParserError("Expected: ), found: 'xyz'".to_owned()), + ParserError::SpannedParserError("Expected: ), found: 'xyz'".to_owned(), Span::empty()), parse_sql_statements("SELECT TRIM(FOO 'xyz' FROM 'xyzfooxyz')").unwrap_err() ); @@ -7981,7 +8074,7 @@ fn parse_trim() { ]); assert_eq!( - ParserError::ParserError("Expected: ), found: 'a'".to_owned()), + ParserError::SpannedParserError("Expected: ), found: 'a'".to_owned(), Span::empty()), all_expected_snowflake .parse_sql_statements("SELECT TRIM('xyz', 'a')") .unwrap_err() @@ -8017,8 +8110,9 @@ fn parse_exists_subquery() { let res = all_dialects_except(|d| d.is::()) .parse_sql_statements("SELECT EXISTS ("); assert_eq!( - ParserError::ParserError( - "Expected: SELECT, VALUES, or a subquery in the query body, found: EOF".to_string() + ParserError::SpannedParserError( + "Expected: SELECT, VALUES, or a subquery in the query body, found: EOF".to_string(), + Span::empty(), ), res.unwrap_err(), ); @@ -8026,8 +8120,9 @@ fn parse_exists_subquery() { let res = all_dialects_except(|d| d.is::()) .parse_sql_statements("SELECT EXISTS (NULL)"); assert_eq!( - ParserError::ParserError( - "Expected: SELECT, VALUES, or a subquery in the query body, found: NULL".to_string() + ParserError::SpannedParserError( + "Expected: SELECT, VALUES, or a subquery in the query body, found: NULL".to_string(), + Span::empty(), ), res.unwrap_err(), ); @@ -8512,13 +8607,19 @@ fn parse_drop_table() { let sql = "DROP TABLE"; assert_eq!( - ParserError::ParserError("Expected: identifier, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ), parse_sql_statements(sql).unwrap_err(), ); let sql = "DROP TABLE IF EXISTS foo, bar CASCADE RESTRICT"; assert_eq!( - ParserError::ParserError("Cannot specify both CASCADE and RESTRICT in DROP".to_string()), + ParserError::SpannedParserError( + "Cannot specify both CASCADE and RESTRICT in DROP".to_string(), + Span::empty() + ), parse_sql_statements(sql).unwrap_err(), ); } @@ -8565,7 +8666,10 @@ fn parse_drop_user() { 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("Expected: end of statement, found: 1".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: 1".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -8795,15 +8899,19 @@ fn lateral_derived() { let sql = "SELECT * FROM LATERAL UNNEST ([10,20,30]) as numbers WITH OFFSET;"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: WITH".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: WITH".to_string(), + Span::empty(), + ), res.unwrap_err() ); let sql = "SELECT * FROM a LEFT JOIN LATERAL (b CROSS JOIN c)"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError( - "Expected: SELECT, VALUES, or a subquery in the query body, found: b".to_string() + ParserError::SpannedParserError( + "Expected: SELECT, VALUES, or a subquery in the query body, found: b".to_string(), + Span::empty(), ), res.unwrap_err() ); @@ -8925,19 +9033,28 @@ fn parse_start_transaction() { let res = dialects.parse_sql_statements("START TRANSACTION ISOLATION LEVEL BAD"); assert_eq!( - ParserError::ParserError("Expected: isolation level, found: BAD".to_string()), + ParserError::SpannedParserError( + "Expected: isolation level, found: BAD".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = dialects.parse_sql_statements("START TRANSACTION BAD"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: BAD".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: BAD".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = dialects.parse_sql_statements("START TRANSACTION READ ONLY,"); assert_eq!( - ParserError::ParserError("Expected: transaction mode, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: transaction mode, found: EOF".to_string(), + Span::empty() + ), res.unwrap_err() ); @@ -9091,7 +9208,7 @@ fn parse_set_variable() { ]; for (sql, error) in error_sqls { assert_eq!( - ParserError::ParserError(error.to_string()), + ParserError::SpannedParserError(error.to_string(), Span::empty()), multi_variable_dialects .parse_sql_statements(sql) .unwrap_err() @@ -10069,7 +10186,7 @@ fn test_merge_invalid_statements() { ] { let res = dialects.parse_sql_statements(sql); assert_eq!( - ParserError::ParserError(err_msg.to_string()), + ParserError::SpannedParserError(err_msg.to_string(), Span::empty()), res.unwrap_err() ); } @@ -10348,19 +10465,28 @@ fn parse_offset_and_limit() { // Can't repeat OFFSET / LIMIT let res = parse_sql_statements("SELECT foo FROM bar OFFSET 2 OFFSET 2"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: OFFSET".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: OFFSET".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = parse_sql_statements("SELECT foo FROM bar LIMIT 2 LIMIT 2"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: LIMIT".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: LIMIT".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = parse_sql_statements("SELECT foo FROM bar OFFSET 2 LIMIT 2 OFFSET 2"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: OFFSET".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: OFFSET".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -10437,7 +10563,7 @@ fn parse_position_negative() { let sql = "SELECT POSITION(foo IN) from bar"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: (, found: )".to_string()), + ParserError::SpannedParserError("Expected: (, found: )".to_string(), Span::empty()), res.unwrap_err() ); } @@ -10540,9 +10666,10 @@ fn parse_is_boolean() { let sql = "SELECT f from foo where field is 0"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError( + ParserError::SpannedParserError( "Expected: [NOT] NULL | TRUE | FALSE | DISTINCT | [form] NORMALIZED FROM after IS, found: 0" - .to_string() + .to_string(), + Span::empty(), ), res.unwrap_err() ); @@ -10550,9 +10677,10 @@ fn parse_is_boolean() { let sql = "SELECT s, s IS XYZ NORMALIZED FROM foo"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError( + ParserError::SpannedParserError( "Expected: [NOT] NULL | TRUE | FALSE | DISTINCT | [form] NORMALIZED FROM after IS, found: XYZ" - .to_string() + .to_string(), + Span::empty(), ), res.unwrap_err() ); @@ -10560,9 +10688,10 @@ fn parse_is_boolean() { let sql = "SELECT s, s IS NFKC FROM foo"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError( + ParserError::SpannedParserError( "Expected: [NOT] NULL | TRUE | FALSE | DISTINCT | [form] NORMALIZED FROM after IS, found: FROM" - .to_string() + .to_string(), + Span::empty(), ), res.unwrap_err() ); @@ -10570,9 +10699,10 @@ fn parse_is_boolean() { let sql = "SELECT s, s IS TRIM(' NFKC ') FROM foo"; let res = parse_sql_statements(sql); assert_eq!( - ParserError::ParserError( + ParserError::SpannedParserError( "Expected: [NOT] NULL | TRUE | FALSE | DISTINCT | [form] NORMALIZED FROM after IS, found: TRIM" - .to_string() + .to_string(), + Span::empty(), ), res.unwrap_err() ); @@ -10763,51 +10893,64 @@ fn parse_cache_table() { let res = parse_sql_statements("CACHE TABLE 'table_name' foo"); assert_eq!( - ParserError::ParserError( - "Expected: SELECT, VALUES, or a subquery in the query body, found: foo".to_string() + ParserError::SpannedParserError( + "Expected: SELECT, VALUES, or a subquery in the query body, found: foo".to_string(), + Span::empty(), ), res.unwrap_err() ); let res = parse_sql_statements("CACHE flag TABLE 'table_name' OPTIONS('K1'='V1') foo"); assert_eq!( - ParserError::ParserError( - "Expected: SELECT, VALUES, or a subquery in the query body, found: foo".to_string() + ParserError::SpannedParserError( + "Expected: SELECT, VALUES, or a subquery in the query body, found: foo".to_string(), + Span::empty(), ), res.unwrap_err() ); let res = parse_sql_statements("CACHE TABLE 'table_name' AS foo"); assert_eq!( - ParserError::ParserError( - "Expected: SELECT, VALUES, or a subquery in the query body, found: foo".to_string() + ParserError::SpannedParserError( + "Expected: SELECT, VALUES, or a subquery in the query body, found: foo".to_string(), + Span::empty(), ), res.unwrap_err() ); let res = parse_sql_statements("CACHE flag TABLE 'table_name' OPTIONS('K1'='V1') AS foo"); assert_eq!( - ParserError::ParserError( - "Expected: SELECT, VALUES, or a subquery in the query body, found: foo".to_string() + ParserError::SpannedParserError( + "Expected: SELECT, VALUES, or a subquery in the query body, found: foo".to_string(), + Span::empty(), ), res.unwrap_err() ); let res = parse_sql_statements("CACHE 'table_name'"); assert_eq!( - ParserError::ParserError("Expected: a `TABLE` keyword, found: 'table_name'".to_string()), + ParserError::SpannedParserError( + "Expected: a `TABLE` keyword, found: 'table_name'".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = parse_sql_statements("CACHE 'table_name' OPTIONS('K1'='V1')"); assert_eq!( - ParserError::ParserError("Expected: a `TABLE` keyword, found: OPTIONS".to_string()), + ParserError::SpannedParserError( + "Expected: a `TABLE` keyword, found: OPTIONS".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = parse_sql_statements("CACHE flag 'table_name' OPTIONS('K1'='V1')"); assert_eq!( - ParserError::ParserError("Expected: a `TABLE` keyword, found: 'table_name'".to_string()), + ParserError::SpannedParserError( + "Expected: a `TABLE` keyword, found: 'table_name'".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -10832,19 +10975,25 @@ fn parse_uncache_table() { let res = parse_sql_statements("UNCACHE TABLE 'table_name' foo"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: foo".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: foo".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = parse_sql_statements("UNCACHE 'table_name' foo"); assert_eq!( - ParserError::ParserError("Expected: TABLE, found: 'table_name'".to_string()), + ParserError::SpannedParserError( + "Expected: TABLE, found: 'table_name'".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = parse_sql_statements("UNCACHE IF EXISTS 'table_name' foo"); assert_eq!( - ParserError::ParserError("Expected: TABLE, found: IF".to_string()), + ParserError::SpannedParserError("Expected: TABLE, found: IF".to_string(), Span::empty()), res.unwrap_err() ); } @@ -11637,22 +11786,29 @@ fn parse_trailing_comma() { trailing_commas .parse_sql_statements("SELECT name, age, from employees;") .unwrap_err(), - ParserError::ParserError("Expected an expression, found: from".to_string()) + ParserError::SpannedParserError( + "Expected an expression, found: from".to_string(), + Span::empty() + ) ); assert_eq!( trailing_commas .parse_sql_statements("REVOKE USAGE, SELECT, ON p TO u") .unwrap_err(), - ParserError::ParserError("Expected: a privilege keyword, found: ON".to_string()) + ParserError::SpannedParserError( + "Expected: a privilege keyword, found: ON".to_string(), + Span::empty() + ) ); assert_eq!( trailing_commas .parse_sql_statements("CREATE TABLE employees (name text, age int,)") .unwrap_err(), - ParserError::ParserError( - "Expected: column name or constraint definition, found: )".to_string() + ParserError::SpannedParserError( + "Expected: column name or constraint definition, found: )".to_string(), + Span::empty(), ) ); @@ -11661,7 +11817,10 @@ fn parse_trailing_comma() { unsupported_dialects .parse_sql_statements("SELECT * FROM track ORDER BY milliseconds,") .unwrap_err(), - ParserError::ParserError("Expected: an expression, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_string(), + Span::empty() + ) ); } @@ -11687,7 +11846,10 @@ fn parse_projection_trailing_comma() { unsupported_dialects .parse_sql_statements("SELECT album_id, name, FROM track") .unwrap_err(), - ParserError::ParserError("Expected an expression, found: FROM".to_string()) + ParserError::SpannedParserError( + "Expected an expression, found: FROM".to_string(), + Span::empty() + ) ); } @@ -11881,7 +12043,10 @@ fn parse_execute_immediate() { dialects.verified_stmt("EXECUTE 'SELECT 1'"); assert_eq!( - ParserError::ParserError("Expected: identifier, found: ,".to_string()), + ParserError::SpannedParserError( + "Expected: identifier, found: ,".to_string(), + Span::empty() + ), dialects .parse_sql_statements("EXECUTE IMMEDIATE 'SELECT 1' USING 1 AS, y") .unwrap_err() @@ -13390,7 +13555,10 @@ fn test_truncate_table_with_on_cluster() { all_dialects().verified_stmt("TRUNCATE TABLE t"); assert_eq!( - ParserError::ParserError("Expected: identifier, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ), all_dialects() .parse_sql_statements("TRUNCATE TABLE t ON CLUSTER") .unwrap_err() @@ -14122,14 +14290,20 @@ fn parse_listen_channel() { assert_eq!( dialects.parse_sql_statements("LISTEN *").unwrap_err(), - ParserError::ParserError("Expected: identifier, found: *".to_string()) + ParserError::SpannedParserError( + "Expected: identifier, found: *".to_string(), + Span::empty() + ) ); let dialects = all_dialects_where(|d| !d.supports_listen_notify()); assert_eq!( dialects.parse_sql_statements("LISTEN test1").unwrap_err(), - ParserError::ParserError("Expected: an SQL statement, found: LISTEN".to_string()) + ParserError::SpannedParserError( + "Expected: an SQL statement, found: LISTEN".to_string(), + Span::empty() + ) ); } @@ -14153,14 +14327,20 @@ fn parse_unlisten_channel() { assert_eq!( dialects.parse_sql_statements("UNLISTEN +").unwrap_err(), - ParserError::ParserError("Expected: wildcard or identifier, found: +".to_string()) + ParserError::SpannedParserError( + "Expected: wildcard or identifier, found: +".to_string(), + Span::empty() + ) ); let dialects = all_dialects_where(|d| !d.supports_listen_notify()); assert_eq!( dialects.parse_sql_statements("UNLISTEN test1").unwrap_err(), - ParserError::ParserError("Expected: an SQL statement, found: UNLISTEN".to_string()) + ParserError::SpannedParserError( + "Expected: an SQL statement, found: UNLISTEN".to_string(), + Span::empty() + ) ); } @@ -14189,13 +14369,19 @@ fn parse_notify_channel() { assert_eq!( dialects.parse_sql_statements("NOTIFY *").unwrap_err(), - ParserError::ParserError("Expected: identifier, found: *".to_string()) + ParserError::SpannedParserError( + "Expected: identifier, found: *".to_string(), + Span::empty() + ) ); assert_eq!( dialects .parse_sql_statements("NOTIFY test1, *") .unwrap_err(), - ParserError::ParserError("Expected: literal string, found: *".to_string()) + ParserError::SpannedParserError( + "Expected: literal string, found: *".to_string(), + Span::empty() + ) ); let sql_statements = [ @@ -14207,7 +14393,10 @@ fn parse_notify_channel() { for &sql in &sql_statements { assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: an SQL statement, found: NOTIFY".to_string()) + ParserError::SpannedParserError( + "Expected: an SQL statement, found: NOTIFY".to_string(), + Span::empty() + ) ); } } @@ -14268,14 +14457,18 @@ fn parse_load_data() { only_supports_load_extension_dialects .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError("Expected: end of statement, found: INPATH".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: INPATH".to_string(), + Span::empty() + ) ); assert_eq!( not_supports_load_dialects .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError( - "Expected: `DATA` or an extension name after `LOAD`, found: INPATH".to_string() + ParserError::SpannedParserError( + "Expected: `DATA` or an extension name after `LOAD`, found: INPATH".to_string(), + Span::empty(), ) ); @@ -14307,14 +14500,18 @@ fn parse_load_data() { only_supports_load_extension_dialects .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError("Expected: end of statement, found: LOCAL".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: LOCAL".to_string(), + Span::empty() + ) ); assert_eq!( not_supports_load_dialects .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError( - "Expected: `DATA` or an extension name after `LOAD`, found: LOCAL".to_string() + ParserError::SpannedParserError( + "Expected: `DATA` or an extension name after `LOAD`, found: LOCAL".to_string(), + Span::empty(), ) ); @@ -14419,8 +14616,9 @@ fn parse_load_data() { let sql = "LOAD DATA2 LOCAL INPATH '/local/path/to/data.txt' INTO TABLE test.my_table"; assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError( - "Expected: `DATA` or an extension name after `LOAD`, found: DATA2".to_string() + ParserError::SpannedParserError( + "Expected: `DATA` or an extension name after `LOAD`, found: DATA2".to_string(), + Span::empty(), ) ); } @@ -14442,8 +14640,9 @@ fn test_load_extension() { not_supports_load_extension_dialects .parse_sql_statements(sql) .unwrap_err(), - ParserError::ParserError( - "Expected: `DATA` or an extension name after `LOAD`, found: my_extension".to_string() + ParserError::SpannedParserError( + "Expected: `DATA` or an extension name after `LOAD`, found: my_extension".to_string(), + Span::empty(), ) ); @@ -14507,7 +14706,10 @@ fn parse_bang_not() { for &sql in &sql_statements { assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("No infix parser for token ExclamationMark".to_string()) + ParserError::SpannedParserError( + "No infix parser for token ExclamationMark".to_string(), + Span::empty() + ) ); } @@ -14517,7 +14719,10 @@ fn parse_bang_not() { for &sql in &sql_statements { assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: an expression, found: !".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: !".to_string(), + Span::empty() + ) ); } } @@ -14553,7 +14758,10 @@ fn parse_factorial_operator() { for &sql in &sql_statements { assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: an expression, found: !".to_string()) + ParserError::SpannedParserError( + "Expected: an expression, found: !".to_string(), + Span::empty() + ) ); } @@ -14568,7 +14776,10 @@ fn parse_factorial_operator() { for &sql in &sql_statements { assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("No infix parser for token ExclamationMark".to_string()) + ParserError::SpannedParserError( + "No infix parser for token ExclamationMark".to_string(), + Span::empty() + ) ); } @@ -14581,7 +14792,10 @@ fn parse_factorial_operator() { for &sql in &sql_statements { assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("No infix parser for token ExclamationMark".to_string()) + ParserError::SpannedParserError( + "No infix parser for token ExclamationMark".to_string(), + Span::empty() + ) ); } } @@ -14654,7 +14868,7 @@ fn parse_comments() { all_dialects_where(|d| d.supports_comment_on()) .parse_sql_statements("COMMENT ON TABLE t0") .unwrap_err(), - ParserError::ParserError("Expected: IS, found: EOF".to_string()) + ParserError::SpannedParserError("Expected: IS, found: EOF".to_string(), Span::empty()) ); // missing comment literal @@ -14662,7 +14876,10 @@ fn parse_comments() { all_dialects_where(|d| d.supports_comment_on()) .parse_sql_statements("COMMENT ON TABLE t0 IS") .unwrap_err(), - ParserError::ParserError("Expected: literal string, found: EOF".to_string()) + ParserError::SpannedParserError( + "Expected: literal string, found: EOF".to_string(), + Span::empty() + ) ); // unknown object type @@ -14670,7 +14887,10 @@ fn parse_comments() { all_dialects_where(|d| d.supports_comment_on()) .parse_sql_statements("COMMENT ON UNKNOWN t0 IS 'comment'") .unwrap_err(), - ParserError::ParserError("Expected: comment object_type, found: UNKNOWN".to_string()) + ParserError::SpannedParserError( + "Expected: comment object_type, found: UNKNOWN".to_string(), + Span::empty() + ) ); } @@ -14690,7 +14910,10 @@ fn parse_create_table_select() { for sql in [sql_1, sql_2] { assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: end of statement, found: SELECT".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: SELECT".to_string(), + Span::empty() + ) ); } } @@ -14702,8 +14925,9 @@ fn test_reserved_keywords_for_identifiers() { let sql = "SELECT MAX(interval) FROM tbl"; assert_eq!( dialects.parse_sql_statements(sql), - Err(ParserError::ParserError( - "Expected: an expression, found: )".to_string() + Err(ParserError::SpannedParserError( + "Expected: an expression, found: )".to_string(), + Span::empty(), )) ); @@ -14929,7 +15153,7 @@ fn parse_create_table_with_enum_types() { all_dialects() .parse_sql_statements("CREATE TABLE t0 (foo ENUM8('a' = 1, 'b' = ))") .unwrap_err(), - ParserError::ParserError("Expected: a value, found: )".to_string()) + ParserError::SpannedParserError("Expected: a value, found: )".to_string(), Span::empty()) ); // invalid case that name is not a string @@ -14937,7 +15161,10 @@ fn parse_create_table_with_enum_types() { all_dialects() .parse_sql_statements("CREATE TABLE t0 (foo ENUM8('a' = 1, 2))") .unwrap_err(), - ParserError::ParserError("Expected: literal string, found: 2".to_string()) + ParserError::SpannedParserError( + "Expected: literal string, found: 2".to_string(), + Span::empty() + ) ); } @@ -14979,7 +15206,10 @@ fn parse_update_from_before_select() { let query = "UPDATE t1 FROM (SELECT name, id FROM t1 GROUP BY id) AS t2 SET name = t2.name FROM (SELECT name from t2) AS t2"; assert_eq!( - ParserError::ParserError("Expected: end of statement, found: FROM".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: FROM".to_string(), + Span::empty() + ), parse_sql_statements(query).unwrap_err() ); } @@ -15009,8 +15239,9 @@ fn parse_column_definition_trailing_commas() { unsupported_dialects .parse_sql_statements("CREATE TABLE employees (name text, age int,)") .unwrap_err(), - ParserError::ParserError( - "Expected: column name or constraint definition, found: )".to_string() + ParserError::SpannedParserError( + "Expected: column name or constraint definition, found: )".to_string(), + Span::empty(), ), ); } @@ -15118,11 +15349,11 @@ fn parse_case_statement() { )); assert_eq!( - ParserError::ParserError("Expected: THEN, found: END".to_string()), + ParserError::SpannedParserError("Expected: THEN, found: END".to_string(), Span::empty()), parse_sql_statements("CASE 1 WHEN a END").unwrap_err() ); assert_eq!( - ParserError::ParserError("Expected: WHEN, found: ELSE".to_string()), + ParserError::SpannedParserError("Expected: WHEN, found: ELSE".to_string(), Span::empty()), parse_sql_statements("CASE 1 ELSE SELECT 1; END").unwrap_err() ); } @@ -15201,7 +15432,7 @@ fn parse_if_statement() { dialects.verified_stmt("IF 1 THEN SELECT 1; ELSEIF 1 THEN END IF"); assert_eq!( - ParserError::ParserError("Expected: IF, found: EOF".to_string()), + ParserError::SpannedParserError("Expected: IF, found: EOF".to_string(), Span::empty()), dialects .parse_sql_statements("IF 1 THEN SELECT 1; ELSEIF 1 THEN SELECT 2; END") .unwrap_err() @@ -15281,7 +15512,7 @@ fn parse_raise_statement() { verified_stmt("RAISE"); assert_eq!( - ParserError::ParserError("Expected: =, found: error".to_string()), + ParserError::SpannedParserError("Expected: =, found: error".to_string(), Span::empty()), parse_sql_statements("RAISE USING MESSAGE error").unwrap_err() ); } @@ -16727,7 +16958,10 @@ fn test_select_exclude() { .parse_sql_statements("SELECT *, c1 EXCLUDE c2 FROM test") .err() .unwrap(), - ParserError::ParserError("Expected: end of statement, found: c2".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: c2".to_string(), + Span::empty() + ) ); // Dialects that only support the wildcard form and accept EXCLUDE as an implicity alias @@ -16742,7 +16976,10 @@ fn test_select_exclude() { .parse_sql_statements("SELECT *, c1 EXCLUDE c2 FROM test") .err() .unwrap(), - ParserError::ParserError("Expected: end of statement, found: EXCLUDE".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: EXCLUDE".to_string(), + Span::empty() + ) ); } @@ -16827,11 +17064,17 @@ fn parse_odbc_time_date_timestamp() { let res = supports_dictionary.parse_sql_statements(sql); let res_dict = dictionary_unsupported.parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: :, found: '14:12:01'".to_string()), + ParserError::SpannedParserError( + "Expected: :, found: '14:12:01'".to_string(), + Span::empty() + ), res.unwrap_err() ); assert_eq!( - ParserError::ParserError("Expected: an expression, found: {".to_string()), + ParserError::SpannedParserError( + "Expected: an expression, found: {".to_string(), + Span::empty() + ), res_dict.unwrap_err() ); } @@ -16940,7 +17183,7 @@ fn parse_create_view_if_not_exists() { let sql = "CREATE VIEW IF NOT EXISTS AS SELECT 1"; let res = all_dialects().parse_sql_statements(sql); assert_eq!( - ParserError::ParserError("Expected: AS, found: SELECT".to_string()), + ParserError::SpannedParserError("Expected: AS, found: SELECT".to_string(), Span::empty()), res.unwrap_err() ); } diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 92b635339..b49ba5941 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -82,7 +82,7 @@ fn test_databricks_exists() { let res = databricks().parse_sql_statements("SELECT EXISTS ("); assert_eq!( // TODO: improve this error message... - ParserError::ParserError("Expected: an expression, found: EOF".to_string()), + ParserError::SpannedParserError("Expected: an expression, found: EOF".to_string(), Span::empty()), res.unwrap_err(), ); } @@ -279,7 +279,7 @@ fn parse_use() { for sql in &invalid_cases { assert_eq!( databricks().parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: identifier, found: EOF".to_string()), + ParserError::SpannedParserError("Expected: identifier, found: EOF".to_string(), Span::empty()), ); } } diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index 0f8051955..c2561a566 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -865,7 +865,7 @@ fn test_duckdb_trim() { // missing comma separation let error_sql = "SELECT TRIM('xyz' 'a')"; assert_eq!( - ParserError::ParserError("Expected: ), found: 'a'".to_owned()), + ParserError::SpannedParserError("Expected: ), found: 'a'".to_owned(), Span::empty()), duckdb().parse_sql_statements(error_sql).unwrap_err() ); } diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 56a72ec84..00e79ea0f 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -28,6 +28,7 @@ use sqlparser::ast::{ use sqlparser::dialect::{AnsiDialect, GenericDialect, HiveDialect}; use sqlparser::parser::ParserError; use sqlparser::test_utils::*; +use sqlparser::tokenizer::Span; #[test] fn parse_table_create() { @@ -147,7 +148,10 @@ fn create_table_with_comment() { ); assert_eq!( hive().parse_sql_statements(invalid_sql).unwrap_err(), - ParserError::ParserError("Expected: end of statement, found: COMMENT".to_string()) + ParserError::SpannedParserError( + "Expected: end of statement, found: COMMENT".to_string(), + Span::empty() + ) ); } @@ -198,28 +202,28 @@ fn create_table_with_clustered_by() { hive_and_generic().parse_sql_statements( "CREATE TABLE db.table_name (a INT, b STRING) PARTITIONED BY (a INT, b STRING) CLUSTERED BY (a, b)" ).unwrap_err(), - ParserError::ParserError("Expected: INTO, found: EOF".to_string()) + ParserError::SpannedParserError("Expected: INTO, found: EOF".to_string(), Span::empty()) ); // missing CLUSTER BY columns assert_eq!( hive_and_generic().parse_sql_statements( "CREATE TABLE db.table_name (a INT, b STRING) PARTITIONED BY (a INT, b STRING) CLUSTERED BY () INTO 4 BUCKETS" ).unwrap_err(), - ParserError::ParserError("Expected: identifier, found: )".to_string()) + ParserError::SpannedParserError("Expected: identifier, found: )".to_string(), Span::empty()) ); // missing SORT BY columns assert_eq!( hive_and_generic().parse_sql_statements( "CREATE TABLE db.table_name (a INT, b STRING) PARTITIONED BY (a INT, b STRING) CLUSTERED BY (a, b) SORTED BY INTO 4 BUCKETS" ).unwrap_err(), - ParserError::ParserError("Expected: (, found: INTO".to_string()) + ParserError::SpannedParserError("Expected: (, found: INTO".to_string(), Span::empty()) ); // missing number BUCKETS assert_eq!( hive_and_generic().parse_sql_statements( "CREATE TABLE db.table_name (a INT, b STRING) PARTITIONED BY (a INT, b STRING) CLUSTERED BY (a, b) SORTED BY (a ASC, b DESC) INTO" ).unwrap_err(), - ParserError::ParserError("Expected: a value, found: EOF".to_string()) + ParserError::SpannedParserError("Expected: a value, found: EOF".to_string(), Span::empty()) ); } @@ -387,8 +391,9 @@ fn set_statement_with_minus() { assert_eq!( hive().parse_sql_statements("SET hive.tez.java.opts = -"), - Err(ParserError::ParserError( - "Expected: variable value, found: EOF".to_string() + Err(ParserError::SpannedParserError( + "Expected: variable value, found: EOF".to_string(), + Span::empty(), )) ) } @@ -428,15 +433,19 @@ fn parse_create_function() { assert_eq!( unsupported_dialects.parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError( - "Expected: an object type after CREATE, found: FUNCTION".to_string() + ParserError::SpannedParserError( + "Expected: an object type after CREATE, found: FUNCTION".to_string(), + Span::empty(), ) ); let sql = "CREATE TEMPORARY FUNCTION mydb.myfunc AS 'org.random.class.Name' USING JAR"; assert_eq!( hive().parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: literal string, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: literal string, found: EOF".to_string(), + Span::empty() + ), ); } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index a947db49b..885c52b12 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -367,7 +367,7 @@ fn parse_create_function() { END\ "; assert_eq!( - ParserError::ParserError("Unparsable function body".to_owned()), + ParserError::SpannedParserError("Unparsable function body".to_owned(), Span::empty()), ms().parse_sql_statements(create_multi_statement_tvf_without_table_definition) .unwrap_err() ); @@ -379,8 +379,9 @@ fn parse_create_function() { RETURN 'hi'\ "; assert_eq!( - ParserError::ParserError( - "Expected a subquery (or bare SELECT statement) after RETURN".to_owned() + ParserError::SpannedParserError( + "Expected a subquery (or bare SELECT statement) after RETURN".to_owned(), + Span::empty(), ), ms().parse_sql_statements(create_inline_tvf_without_subquery_or_bare_select) .unwrap_err() @@ -1334,7 +1335,7 @@ fn parse_convert() { let error_sql = "SELECT CONVERT(INT, 'foo',) FROM T"; assert_eq!( - ParserError::ParserError("Expected: an expression, found: )".to_owned()), + ParserError::SpannedParserError("Expected: an expression, found: )".to_owned(), Span::empty()), ms().parse_sql_statements(error_sql).unwrap_err() ); } diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index e43df87ab..98272453f 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1321,7 +1321,7 @@ fn parse_create_table_both_options_and_as_query() { r"CREATE TABLE foo (id INT(11)) ENGINE = InnoDB AS SELECT 1 DEFAULT CHARSET = utf8mb3"; assert!(matches!( mysql_and_generic().parse_sql_statements(sql), - Err(ParserError::ParserError(_)) + Err(ParserError::SpannedParserError(_, _)) )); } @@ -2120,7 +2120,7 @@ fn parse_insert_as() { let sql = r"INSERT INTO `table` (`date`) VALUES ('2024-01-01') AS `alias` ()"; assert!(matches!( mysql_and_generic().parse_sql_statements(sql), - Err(ParserError::ParserError(_)) + Err(ParserError::SpannedParserError(_, _)) )); let sql = r"INSERT INTO `table` (`id`, `date`) VALUES (1, '2024-01-01') AS `alias` (`mek_id`, `mek_date`)"; diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 9ba0fb978..17d3bd36d 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -307,7 +307,7 @@ fn parse_create_sequence() { assert!(matches!( pg().parse_sql_statements("CREATE SEQUENCE foo INCREMENT 1 NO MINVALUE NO"), - Err(ParserError::ParserError(_)) + Err(ParserError::SpannedParserError(_, _)) )); } @@ -806,7 +806,7 @@ fn parse_alter_table_alter_column_add_generated() { "ALTER TABLE t ALTER COLUMN id ADD GENERATED ( INCREMENT 1 MINVALUE 1 )", ); assert_eq!( - ParserError::ParserError("Expected: AS, found: (".to_string()), + ParserError::SpannedParserError("Expected: AS, found: (".to_string(), Span::empty()), res.unwrap_err() ); @@ -814,14 +814,14 @@ fn parse_alter_table_alter_column_add_generated() { "ALTER TABLE t ALTER COLUMN id ADD GENERATED AS IDENTITY ( INCREMENT )", ); assert_eq!( - ParserError::ParserError("Expected: a value, found: )".to_string()), + ParserError::SpannedParserError("Expected: a value, found: )".to_string(), Span::empty()), res.unwrap_err() ); let res = pg().parse_sql_statements("ALTER TABLE t ALTER COLUMN id ADD GENERATED AS IDENTITY ("); assert_eq!( - ParserError::ParserError("Expected: ), found: EOF".to_string()), + ParserError::SpannedParserError("Expected: ), found: EOF".to_string(), Span::empty()), res.unwrap_err() ); } @@ -930,7 +930,10 @@ fn parse_alter_table_owner_to() { let res = pg().parse_sql_statements("ALTER TABLE tab OWNER TO CREATE FOO"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: FOO".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: FOO".to_string(), + Span::empty() + ), res.unwrap_err() ); @@ -961,25 +964,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("Expected: end of statement, found: EXISTS".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: EXISTS".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = pg().parse_sql_statements("CREATE TABLE IF EXISTS uk_cities ()"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: EXISTS".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: EXISTS".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = pg().parse_sql_statements("CREATE TABLE IF uk_cities ()"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: uk_cities".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: uk_cities".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = pg().parse_sql_statements("CREATE TABLE IF NOT uk_cities ()"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: NOT".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: NOT".to_string(), + Span::empty() + ), res.unwrap_err() ); } @@ -1534,22 +1549,25 @@ fn parse_set() { assert_eq!( pg_and_generic().parse_sql_statements("SET"), - Err(ParserError::ParserError( - "Expected: identifier, found: EOF".to_string() + Err(ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty(), )), ); assert_eq!( pg_and_generic().parse_sql_statements("SET a b"), - Err(ParserError::ParserError( - "Expected: equals sign or TO, found: b".to_string() + Err(ParserError::SpannedParserError( + "Expected: equals sign or TO, found: b".to_string(), + Span::empty(), )), ); assert_eq!( pg_and_generic().parse_sql_statements("SET a ="), - Err(ParserError::ParserError( - "Expected: variable value, found: EOF".to_string() + Err(ParserError::SpannedParserError( + "Expected: variable value, found: EOF".to_string(), + Span::empty(), )), ); } @@ -2830,7 +2848,7 @@ fn parse_create_table_with_inherits() { fn parse_create_table_with_empty_inherits_fails() { assert!(matches!( pg().parse_sql_statements("CREATE TABLE child_table (child_column INT) INHERITS ()"), - Err(ParserError::ParserError(_)) + Err(ParserError::SpannedParserError(_, _)) )); } @@ -4731,13 +4749,19 @@ fn parse_drop_procedure() { let res = pg().parse_sql_statements("DROP PROCEDURE testproc DROP"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: DROP".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: DROP".to_string(), + Span::empty() + ), res.unwrap_err() ); let res = pg().parse_sql_statements("DROP PROCEDURE testproc SET NULL"); assert_eq!( - ParserError::ParserError("Expected: end of statement, found: SET".to_string()), + ParserError::SpannedParserError( + "Expected: end of statement, found: SET".to_string(), + Span::empty() + ), res.unwrap_err() ); } diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 2be5eae8c..71ab9b3ae 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -423,15 +423,17 @@ fn test_snowflake_create_global_table() { fn test_snowflake_create_invalid_local_global_table() { assert_eq!( snowflake().parse_sql_statements("CREATE LOCAL GLOBAL TABLE my_table (a INT)"), - Err(ParserError::ParserError( - "Expected: an SQL statement, found: LOCAL".to_string() + Err(ParserError::SpannedParserError( + "Expected: an SQL statement, found: LOCAL".to_string(), + Span::empty(), )) ); assert_eq!( snowflake().parse_sql_statements("CREATE GLOBAL LOCAL TABLE my_table (a INT)"), - Err(ParserError::ParserError( - "Expected: an SQL statement, found: GLOBAL".to_string() + Err(ParserError::SpannedParserError( + "Expected: an SQL statement, found: GLOBAL".to_string(), + Span::empty(), )) ); } @@ -440,22 +442,25 @@ fn test_snowflake_create_invalid_local_global_table() { fn test_snowflake_create_invalid_temporal_table() { assert_eq!( snowflake().parse_sql_statements("CREATE TEMP TEMPORARY TABLE my_table (a INT)"), - Err(ParserError::ParserError( - "Expected: an object type after CREATE, found: TEMPORARY".to_string() + Err(ParserError::SpannedParserError( + "Expected: an object type after CREATE, found: TEMPORARY".to_string(), + Span::empty(), )) ); assert_eq!( snowflake().parse_sql_statements("CREATE TEMP VOLATILE TABLE my_table (a INT)"), - Err(ParserError::ParserError( - "Expected: an object type after CREATE, found: VOLATILE".to_string() + Err(ParserError::SpannedParserError( + "Expected: an object type after CREATE, found: VOLATILE".to_string(), + Span::empty(), )) ); assert_eq!( snowflake().parse_sql_statements("CREATE TEMP TRANSIENT TABLE my_table (a INT)"), - Err(ParserError::ParserError( - "Expected: an object type after CREATE, found: TRANSIENT".to_string() + Err(ParserError::SpannedParserError( + "Expected: an object type after CREATE, found: TRANSIENT".to_string(), + Span::empty(), )) ); } @@ -1845,13 +1850,13 @@ fn parse_snowflake_declare_cursor() { let error_sql = "DECLARE c1 CURSOR SELECT id FROM invoices"; assert_eq!( - ParserError::ParserError("Expected: FOR, found: SELECT".to_owned()), + ParserError::SpannedParserError("Expected: FOR, found: SELECT".to_owned(), Span::empty()), snowflake().parse_sql_statements(error_sql).unwrap_err() ); let error_sql = "DECLARE c1 CURSOR res"; assert_eq!( - ParserError::ParserError("Expected: FOR, found: res".to_owned()), + ParserError::SpannedParserError("Expected: FOR, found: res".to_owned(), Span::empty()), snowflake().parse_sql_statements(error_sql).unwrap_err() ); } @@ -1899,13 +1904,19 @@ fn parse_snowflake_declare_result_set() { let error_sql = "DECLARE res RESULTSET DEFAULT"; assert_eq!( - ParserError::ParserError("Expected: an expression, found: EOF".to_owned()), + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_owned(), + Span::empty() + ), snowflake().parse_sql_statements(error_sql).unwrap_err() ); let error_sql = "DECLARE res RESULTSET :="; assert_eq!( - ParserError::ParserError("Expected: an expression, found: EOF".to_owned()), + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_owned(), + Span::empty() + ), snowflake().parse_sql_statements(error_sql).unwrap_err() ); } @@ -1991,19 +2002,28 @@ fn parse_snowflake_declare_variable() { let error_sql = "DECLARE profit INT 2"; assert_eq!( - ParserError::ParserError("Expected: end of statement, found: 2".to_owned()), + ParserError::SpannedParserError( + "Expected: end of statement, found: 2".to_owned(), + Span::empty() + ), snowflake().parse_sql_statements(error_sql).unwrap_err() ); let error_sql = "DECLARE profit INT DEFAULT"; assert_eq!( - ParserError::ParserError("Expected: an expression, found: EOF".to_owned()), + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_owned(), + Span::empty() + ), snowflake().parse_sql_statements(error_sql).unwrap_err() ); let error_sql = "DECLARE profit DEFAULT"; assert_eq!( - ParserError::ParserError("Expected: an expression, found: EOF".to_owned()), + ParserError::SpannedParserError( + "Expected: an expression, found: EOF".to_owned(), + Span::empty() + ), snowflake().parse_sql_statements(error_sql).unwrap_err() ); } @@ -2038,7 +2058,10 @@ fn parse_snowflake_declare_multi_statements() { let error_sql = "DECLARE profit DEFAULT 42 c1 CURSOR FOR res;"; assert_eq!( - ParserError::ParserError("Expected: end of statement, found: c1".to_owned()), + ParserError::SpannedParserError( + "Expected: end of statement, found: c1".to_owned(), + Span::empty() + ), snowflake().parse_sql_statements(error_sql).unwrap_err() ); } @@ -2763,7 +2786,7 @@ fn test_snowflake_trim() { // missing comma separation let error_sql = "SELECT TRIM('xyz' 'a')"; assert_eq!( - ParserError::ParserError("Expected: ), found: 'a'".to_owned()), + ParserError::SpannedParserError("Expected: ), found: 'a'".to_owned(), Span::empty()), snowflake().parse_sql_statements(error_sql).unwrap_err() ); } @@ -3247,7 +3270,10 @@ fn parse_use() { for sql in &invalid_cases { assert_eq!( snowflake().parse_sql_statements(sql).unwrap_err(), - ParserError::ParserError("Expected: identifier, found: EOF".to_string()), + ParserError::SpannedParserError( + "Expected: identifier, found: EOF".to_string(), + Span::empty() + ), ); } diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index f1f6cf49b..f58a4c1c3 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -30,7 +30,7 @@ use sqlparser::ast::Value::Placeholder; use sqlparser::ast::*; use sqlparser::dialect::{GenericDialect, SQLiteDialect}; use sqlparser::parser::{ParserError, ParserOptions}; -use sqlparser::tokenizer::Token; +use sqlparser::tokenizer::{Span, Token}; #[test] fn pragma_no_value() { @@ -356,9 +356,10 @@ fn test_parse_create_table_on_conflict_col_err() { .unwrap_err(); assert_eq!( err, - ParserError::ParserError( + ParserError::SpannedParserError( "Expected: one of ROLLBACK or ABORT or FAIL or IGNORE or REPLACE, found: BOH" - .to_string() + .to_string(), + Span::empty() ) ); }