From dc3e8fca8451e29588ddbfacd9752423f6bf3a5c Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Tue, 31 Mar 2026 07:09:27 +0200 Subject: [PATCH 1/7] Provide `Parens` and track Value's `(expressions)` span --- src/ast/mod.rs | 41 ++++++++++++++++++++++++++++++++++ src/ast/query.rs | 2 +- src/ast/spans.rs | 36 ++++++++++-------------------- src/parser/mod.rs | 18 ++++++++++----- tests/sqlparser_bigquery.rs | 10 ++++----- tests/sqlparser_common.rs | 10 ++++----- tests/sqlparser_databricks.rs | 8 +++---- tests/sqlparser_mysql.rs | 42 +++++++++++++++++------------------ tests/sqlparser_postgres.rs | 16 ++++++------- 9 files changed, 110 insertions(+), 73 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c10b383eb..6f77ab08c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -35,6 +35,7 @@ use core::{ fmt::{self, Display}, hash, }; +use std::ops::DerefMut; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -200,6 +201,46 @@ fn format_statement_list(f: &mut fmt::Formatter, statements: &[Statement]) -> fm write!(f, ";") } +/// A item `T` enclosed in a pair of parentheses +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct Parens { + /// the opening parenthesis token, i.e. `(` + pub opening_token: AttachedToken, + /// content enclosed in parentheses + pub content: T, + /// the closing parenthesis token, i.e. `)` + pub closing_token: AttachedToken, +} + +impl Parens { + /// Constructor wrapping `content` into `Parens` with an empty span; + /// useful for testing purposes. + #[inline] + pub fn with_empty_span(content: T) -> Self { + Self { + opening_token: AttachedToken::empty(), + content, + closing_token: AttachedToken::empty(), + } + } +} + +impl Deref for Parens { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.content + } +} + +impl DerefMut for Parens { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.content + } +} + /// An identifier, decomposed into its value or character data and the quote style. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/ast/query.rs b/src/ast/query.rs index 49ba86f1f..817601f8f 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -3628,7 +3628,7 @@ pub struct Values { /// pub value_keyword: bool, /// The list of rows, each row is a list of expressions. - pub rows: Vec>, + pub rows: Vec>>, } impl fmt::Display for Values { diff --git a/src/ast/spans.rs b/src/ast/spans.rs index e7a8f94f2..9241c4283 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -28,26 +28,7 @@ use core::iter; use crate::tokenizer::Span; use super::{ - comments, dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation, - AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, - AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, - ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, - ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, - CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, - ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr, - FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, - IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join, - JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, - MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, - MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName, - ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy, - OrderByExpr, OrderByKind, OutputClause, Partition, PartitionBoundValue, PivotValueSource, - ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction, - RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, - SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, - TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update, - UpdateTableFromKind, Use, Values, ViewColumnDef, WhileStatement, WildcardAdditionalOptions, - With, WithFill, + AccessExpr, AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Parens, Partition, PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Values, ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill, comments, dcl::SecondaryRoles, value::ValueWithSpan }; /// Given an iterator of spans, return the [Span::union] of all spans. @@ -106,6 +87,12 @@ impl Spanned for TokenWithSpan { } } +impl Spanned for Parens { + fn span(&self) -> Span { + union_spans([self.opening_token.0.span, self.closing_token.0.span].into_iter()) + } +} + impl Spanned for Query { fn span(&self) -> Span { let Query { @@ -239,10 +226,11 @@ impl Spanned for Values { rows, } = self; - union_spans( - rows.iter() - .map(|row| union_spans(row.iter().map(|expr| expr.span()))), - ) + match &rows[..] { + [] => Span::empty(), + [f] => f.span(), + [f, .., l] => union_spans([f.span(), l.span()].into_iter()), + } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e433d8f2f..40abd52f8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -19027,14 +19027,22 @@ impl<'a> Parser<'a> { explicit_row = true; } - parser.expect_token(&Token::LParen)?; + let opening_paren = parser.expect_token(&Token::LParen)?; if allow_empty && parser.peek_token().token == Token::RParen { - parser.next_token(); - Ok(vec![]) + let closing_paren = parser.next_token(); + Ok(Parens { + opening_token: opening_paren.into(), + content: vec![], + closing_token: closing_paren.into() + }) } else { let exprs = parser.parse_comma_separated(Parser::parse_expr)?; - parser.expect_token(&Token::RParen)?; - Ok(exprs) + let closing_paren = parser.expect_token(&Token::RParen)?; + Ok(Parens { + opening_token: opening_paren.into(), + content: exprs, + closing_token: closing_paren.into(), + }) } })?; Ok(Values { diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 79db34b06..73e522d17 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1821,7 +1821,7 @@ fn parse_merge() { kind: MergeInsertKind::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![Expr::value(number("1")), Expr::value(number("2"))]], + rows: vec![Parens::with_empty_span(vec![Expr::value(number("1")), Expr::value(number("2"))])], }), insert_predicate: None, }); @@ -2003,10 +2003,10 @@ fn parse_merge() { kind: MergeInsertKind::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::value(number("1")), Expr::Identifier(Ident::new("DEFAULT")), - ]] + ])] }), insert_predicate: None, }) @@ -2022,10 +2022,10 @@ fn parse_merge() { kind: MergeInsertKind::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::value(number("1")), Expr::Identifier(Ident::new("DEFAULT")), - ]] + ])] }), insert_predicate: None, }) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index c76899f04..2b3610702 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -100,8 +100,8 @@ fn parse_insert_values() { Expr::value(number("2")), Expr::value(number("3")), ]; - let rows1 = vec![row.clone()]; - let rows2 = vec![row.clone(), row]; + let rows1 = vec![Parens::with_empty_span(row.clone())]; + let rows2 = vec![Parens::with_empty_span(row.clone()), Parens::with_empty_span(row)]; let sql = "INSERT customer VALUES (1, 2, 3)"; check_one(sql, "customer", &[], &rows1, false); @@ -140,7 +140,7 @@ fn parse_insert_values() { sql: &str, expected_table_name: &str, expected_columns: &[String], - expected_rows: &[Vec], + expected_rows: &[Parens>], expected_value_keyword: bool, ) { match verified_stmt(sql) { @@ -10100,7 +10100,7 @@ fn parse_merge() { kind: MergeInsertKind::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::CompoundIdentifier(vec![ Ident::new("stg"), Ident::new("A") @@ -10113,7 +10113,7 @@ fn parse_merge() { Ident::new("stg"), Ident::new("C") ]), - ]] + ])] }), insert_predicate: None, }), diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 405dffaf2..0ed0b4118 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -174,14 +174,14 @@ fn test_values_clause() { value_keyword: false, explicit_row: false, rows: vec![ - vec![ + Parens::with_empty_span(vec![ Expr::Value((Value::DoubleQuotedString("one".to_owned())).with_empty_span()), Expr::value(number("1")), - ], - vec![ + ]), + Parens::with_empty_span(vec![ Expr::Value((Value::SingleQuotedString("two".to_owned())).with_empty_span()), Expr::value(number("2")), - ], + ]), ], }; diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index f0de7d817..b7b6cd9d9 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1940,27 +1940,27 @@ fn parse_simple_insert() { value_keyword: false, explicit_row: false, rows: vec![ - vec![ + Parens::with_empty_span(vec![ Expr::Value( (Value::SingleQuotedString("Test Some Inserts".to_string())) .with_empty_span() ), Expr::value(number("1")) - ], - vec![ + ]), + Parens::with_empty_span(vec![ Expr::Value( (Value::SingleQuotedString("Test Entry 2".to_string())) .with_empty_span() ), Expr::value(number("2")) - ], - vec![ + ]), + Parens::with_empty_span(vec![ Expr::Value( (Value::SingleQuotedString("Test Entry 3".to_string())) .with_empty_span() ), Expr::value(number("3")) - ] + ]) ] })), order_by: None, @@ -2011,13 +2011,13 @@ fn parse_ignore_insert() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::Value( (Value::SingleQuotedString("Test Some Inserts".to_string())) .with_empty_span() ), Expr::value(number("1")) - ]] + ])] })), order_by: None, limit_clause: None, @@ -2067,13 +2067,13 @@ fn parse_priority_insert() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::Value( (Value::SingleQuotedString("Test Some Inserts".to_string())) .with_empty_span() ), Expr::value(number("1")) - ]] + ])] })), order_by: None, limit_clause: None, @@ -2120,13 +2120,13 @@ fn parse_priority_insert() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::Value( (Value::SingleQuotedString("Test Some Inserts".to_string())) .with_empty_span() ), Expr::value(number("1")) - ]] + ])] })), order_by: None, limit_clause: None, @@ -2176,9 +2176,9 @@ fn parse_insert_as() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![Expr::Value( + rows: vec![Parens::with_empty_span(vec![Expr::Value( (Value::SingleQuotedString("2024-01-01".to_string())).with_empty_span() - )]] + )])] })), order_by: None, limit_clause: None, @@ -2239,13 +2239,13 @@ fn parse_insert_as() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::value(number("1")), Expr::Value( (Value::SingleQuotedString("2024-01-01".to_string())) .with_empty_span() ) - ]] + ])] })), order_by: None, limit_clause: None, @@ -2296,13 +2296,13 @@ fn parse_replace_insert() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::Value( (Value::SingleQuotedString("Test Some Inserts".to_string())) .with_empty_span() ), Expr::value(number("1")) - ]] + ])] })), order_by: None, limit_clause: None, @@ -2344,7 +2344,7 @@ fn parse_empty_row_insert() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![], vec![]] + rows: vec![Parens::with_empty_span(vec![]), Parens::with_empty_span(vec![])] })), order_by: None, limit_clause: None, @@ -2395,7 +2395,7 @@ fn parse_insert_with_on_duplicate_update() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::Value( (Value::SingleQuotedString("accounting_manager".to_string())) .with_empty_span() @@ -2410,7 +2410,7 @@ fn parse_insert_with_on_duplicate_update() { Expr::Value((Value::Boolean(true)).with_empty_span()), Expr::Value((Value::Boolean(true)).with_empty_span()), Expr::Value((Value::Boolean(true)).with_empty_span()), - ]] + ])] })), order_by: None, limit_clause: None, diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 07b62dd93..7ca8ee8f7 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -2070,11 +2070,11 @@ fn parse_prepare() { assert_eq!(table_name.to_string(), "customers"); assert!(columns.is_empty()); - let expected_values = [vec![ + let expected_values = [Parens::with_empty_span(vec![ Expr::Identifier("a1".into()), Expr::Identifier("a2".into()), Expr::Identifier("a3".into()), - ]]; + ])]; match &*source.body { SetExpr::Values(Values { rows, .. }) => { assert_eq!(rows.as_slice(), &expected_values) @@ -5774,10 +5774,10 @@ fn test_simple_postgres_insert_with_alias() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::Identifier(Ident::new("DEFAULT")), Expr::Value((Value::Number("123".to_string(), false)).with_empty_span()) - ]] + ])] })), order_by: None, limit_clause: None, @@ -5854,13 +5854,13 @@ fn test_simple_postgres_insert_with_alias() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::Identifier(Ident::new("DEFAULT")), Expr::Value( (Value::Number(bigdecimal::BigDecimal::new(123.into(), 0), false)) .with_empty_span() ) - ]] + ])] })), order_by: None, limit_clause: None, @@ -5936,12 +5936,12 @@ fn test_simple_insert_with_quoted_alias() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![vec![ + rows: vec![Parens::with_empty_span(vec![ Expr::Identifier(Ident::new("DEFAULT")), Expr::Value( (Value::SingleQuotedString("0123".to_string())).with_empty_span() ) - ]] + ])] })), order_by: None, limit_clause: None, From a13cf0f43f871967f2c006766c4dcda99c5113fa Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Tue, 31 Mar 2026 07:17:13 +0200 Subject: [PATCH 2/7] Cargo fmt --- src/ast/mod.rs | 2 +- src/ast/spans.rs | 21 ++++++++++++++++++++- src/parser/mod.rs | 2 +- tests/sqlparser_bigquery.rs | 5 ++++- tests/sqlparser_common.rs | 5 ++++- tests/sqlparser_mysql.rs | 5 ++++- 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 6f77ab08c..006b68394 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -235,7 +235,7 @@ impl Deref for Parens { } } -impl DerefMut for Parens { +impl DerefMut for Parens { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.content } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 9241c4283..aeb8593b5 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -28,7 +28,26 @@ use core::iter; use crate::tokenizer::Span; use super::{ - AccessExpr, AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Parens, Partition, PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Values, ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill, comments, dcl::SecondaryRoles, value::ValueWithSpan + comments, dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation, + AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, + AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, + ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, + ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, + CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, + ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr, + FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, + IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join, + JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, + MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, + MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName, + ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy, + OrderByExpr, OrderByKind, OutputClause, Parens, Partition, PartitionBoundValue, + PivotValueSource, ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, + ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, + SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, + TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered, + TableWithJoins, Update, UpdateTableFromKind, Use, Values, ViewColumnDef, WhileStatement, + WildcardAdditionalOptions, With, WithFill, }; /// Given an iterator of spans, return the [Span::union] of all spans. diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 40abd52f8..1ec0739da 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -19033,7 +19033,7 @@ impl<'a> Parser<'a> { Ok(Parens { opening_token: opening_paren.into(), content: vec![], - closing_token: closing_paren.into() + closing_token: closing_paren.into(), }) } else { let exprs = parser.parse_comma_separated(Parser::parse_expr)?; diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 73e522d17..4bdb54f74 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1821,7 +1821,10 @@ fn parse_merge() { kind: MergeInsertKind::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![Parens::with_empty_span(vec![Expr::value(number("1")), Expr::value(number("2"))])], + rows: vec![Parens::with_empty_span(vec![ + Expr::value(number("1")), + Expr::value(number("2")), + ])], }), insert_predicate: None, }); diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 2b3610702..ebfa569a8 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -101,7 +101,10 @@ fn parse_insert_values() { Expr::value(number("3")), ]; let rows1 = vec![Parens::with_empty_span(row.clone())]; - let rows2 = vec![Parens::with_empty_span(row.clone()), Parens::with_empty_span(row)]; + let rows2 = vec![ + Parens::with_empty_span(row.clone()), + Parens::with_empty_span(row), + ]; let sql = "INSERT customer VALUES (1, 2, 3)"; check_one(sql, "customer", &[], &rows1, false); diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index b7b6cd9d9..04973dc20 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -2344,7 +2344,10 @@ fn parse_empty_row_insert() { body: Box::new(SetExpr::Values(Values { value_keyword: false, explicit_row: false, - rows: vec![Parens::with_empty_span(vec![]), Parens::with_empty_span(vec![])] + rows: vec![ + Parens::with_empty_span(vec![]), + Parens::with_empty_span(vec![]) + ] })), order_by: None, limit_clause: None, From ac48f78a76a53739b663f34a6838847404608715 Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Tue, 31 Mar 2026 07:27:57 +0200 Subject: [PATCH 3/7] Remove #[inline] hint --- src/ast/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 006b68394..07fc6a5a7 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -217,7 +217,6 @@ pub struct Parens { impl Parens { /// Constructor wrapping `content` into `Parens` with an empty span; /// useful for testing purposes. - #[inline] pub fn with_empty_span(content: T) -> Self { Self { opening_token: AttachedToken::empty(), From fedc816438308ebacbbb1c55c87a788d2f27951a Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Tue, 31 Mar 2026 07:29:05 +0200 Subject: [PATCH 4/7] no_std compliance --- src/ast/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 07fc6a5a7..886bea26d 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -30,12 +30,11 @@ use helpers::{ }; use core::cmp::Ordering; -use core::ops::Deref; +use core::ops::{Deref, DerefMut}; use core::{ fmt::{self, Display}, hash, }; -use std::ops::DerefMut; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; From 3ca961172a7260477e54285829b85f775bba2a4d Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Tue, 31 Mar 2026 07:32:43 +0200 Subject: [PATCH 5/7] Refactor --- src/ast/spans.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index aeb8593b5..bc00254dd 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -108,7 +108,7 @@ impl Spanned for TokenWithSpan { impl Spanned for Parens { fn span(&self) -> Span { - union_spans([self.opening_token.0.span, self.closing_token.0.span].into_iter()) + self.opening_token.0.span.union(&self.closing_token.0.span) } } @@ -248,7 +248,7 @@ impl Spanned for Values { match &rows[..] { [] => Span::empty(), [f] => f.span(), - [f, .., l] => union_spans([f.span(), l.span()].into_iter()), + [f, .., l] => f.span().union(&l.span()), } } } From 24564ad7e17d2b68928ebca0bf3e787554ad269e Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Tue, 31 Mar 2026 07:38:03 +0200 Subject: [PATCH 6/7] Refactor more --- src/parser/mod.rs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1ec0739da..61e4f138a 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -19026,24 +19026,15 @@ impl<'a> Parser<'a> { if parser.parse_keyword(Keyword::ROW) { explicit_row = true; } - - let opening_paren = parser.expect_token(&Token::LParen)?; - if allow_empty && parser.peek_token().token == Token::RParen { - let closing_paren = parser.next_token(); - Ok(Parens { - opening_token: opening_paren.into(), - content: vec![], - closing_token: closing_paren.into(), - }) - } else { - let exprs = parser.parse_comma_separated(Parser::parse_expr)?; - let closing_paren = parser.expect_token(&Token::RParen)?; - Ok(Parens { - opening_token: opening_paren.into(), - content: exprs, - closing_token: closing_paren.into(), - }) - } + Ok(Parens { + opening_token: parser.expect_token(&Token::LParen)?.into(), + content: if allow_empty && parser.peek_token_ref().token == Token::RParen { + vec![] + } else { + parser.parse_comma_separated(Parser::parse_expr)? + }, + closing_token: parser.expect_token(&Token::RParen)?.into() + }) })?; Ok(Values { explicit_row, From 7c9a9f2e6de29c1d3e72a431bad958331fb06f3d Mon Sep 17 00:00:00 2001 From: Petr Novotnik Date: Tue, 31 Mar 2026 08:26:40 +0200 Subject: [PATCH 7/7] Cargo fmt --- src/parser/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 61e4f138a..41b84c24b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -19033,7 +19033,7 @@ impl<'a> Parser<'a> { } else { parser.parse_comma_separated(Parser::parse_expr)? }, - closing_token: parser.expect_token(&Token::RParen)?.into() + closing_token: parser.expect_token(&Token::RParen)?.into(), }) })?; Ok(Values {