From 9a42c3fbf50f20415ae14e61cf47bbe5417253ca Mon Sep 17 00:00:00 2001 From: Tyler White <50381805+IndexSeek@users.noreply.github.com> Date: Thu, 7 Aug 2025 21:34:38 -0400 Subject: [PATCH 1/3] feat: include end token in ALTER TABLE statement for accurate span calculation --- src/ast/mod.rs | 4 ++++ src/ast/spans.rs | 20 +++++++++++++++++++- src/parser/mod.rs | 7 +++++++ src/test_utils.rs | 1 + tests/sqlparser_common.rs | 19 +++++++++++++++++++ tests/sqlparser_mysql.rs | 1 + 6 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c6212f1e4..5eeb97d91 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3382,6 +3382,9 @@ pub enum Statement { /// Snowflake "ICEBERG" clause for Iceberg tables /// iceberg: bool, + /// Token that represents the end of the statement (semicolon or EOF) + /// Used for accurate span calculation + end_token: AttachedToken, }, /// ```sql /// ALTER INDEX @@ -5419,6 +5422,7 @@ impl fmt::Display for Statement { location, on_cluster, iceberg, + end_token: _, } => { if *iceberg { write!(f, "ALTER ICEBERG TABLE ")?; diff --git a/src/ast/spans.rs b/src/ast/spans.rs index dec26566c..e5ef40402 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -434,10 +434,12 @@ impl Spanned for Statement { location: _, on_cluster, iceberg: _, + end_token, } => union_spans( core::iter::once(name.span()) .chain(operations.iter().map(|i| i.span())) - .chain(on_cluster.iter().map(|i| i.span)), + .chain(on_cluster.iter().map(|i| i.span)) + .chain(core::iter::once(end_token.0.span)), ), Statement::AlterIndex { name, operation } => name.span().union(&operation.span()), Statement::AlterView { @@ -2548,4 +2550,20 @@ pub mod tests { stmt => panic!("expected query; got {stmt:?}"), } } + + #[test] + fn test_alter_table_multiline_span() { + let sql = r#"-- foo +ALTER TABLE users + ADD COLUMN foo + varchar; -- hi there"#; + + let r = Parser::parse_sql(&crate::dialect::PostgreSqlDialect {}, sql).unwrap(); + assert_eq!(1, r.len()); + + let stmt_span = r[0].span(); + + assert_eq!(stmt_span.start, (2, 13).into()); + assert_eq!(stmt_span.end, (4, 11).into()); + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c3230a215..b112d3013 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9227,6 +9227,12 @@ impl<'a> Parser<'a> { }); } + let end_token = if self.peek_token_ref().token == Token::SemiColon { + self.peek_token_ref().clone() + } else { + self.get_current_token().clone() + }; + Ok(Statement::AlterTable { name: table_name, if_exists, @@ -9235,6 +9241,7 @@ impl<'a> Parser<'a> { location, on_cluster, iceberg, + end_token: AttachedToken(end_token), }) } diff --git a/src/test_utils.rs b/src/test_utils.rs index 654f2723e..ab2cf89b2 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -351,6 +351,7 @@ pub fn alter_table_op_with_name(stmt: Statement, expected_name: &str) -> AlterTa on_cluster: _, location: _, iceberg, + end_token: _, } => { assert_eq!(name.to_string(), expected_name); assert!(!if_exists); diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a64733d67..5403d4f13 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4758,6 +4758,25 @@ fn parse_alter_table() { } } +#[test] +fn alter_table_span_includes_semicolon() { + use sqlparser::ast::Spanned; + use sqlparser::dialect::PostgreSqlDialect; + use sqlparser::parser::Parser; + + let sql = r#"-- foo +ALTER TABLE users + ADD COLUMN foo + varchar; -- hi there"#; + let dialect = PostgreSqlDialect {}; + let ast = Parser::parse_sql(&dialect, sql).unwrap(); + let stmt = &ast[0]; + let span = stmt.span(); + + assert_eq!(span.end.line, 4); + assert_eq!(span.end.column, 11); +} + #[test] fn parse_rename_table() { match verified_stmt("RENAME TABLE test.test1 TO test_db.test2") { diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 3ea0543f1..732ef2f9f 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -2635,6 +2635,7 @@ fn parse_alter_table_add_column() { iceberg, location: _, on_cluster: _, + end_token: _, } => { assert_eq!(name.to_string(), "tab"); assert!(!if_exists); From 68ebe75a021bc1fb7d760b4ce7c462233e44e3f5 Mon Sep 17 00:00:00 2001 From: Tyler White <50381805+IndexSeek@users.noreply.github.com> Date: Fri, 8 Aug 2025 21:31:22 -0400 Subject: [PATCH 2/3] Update src/ast/mod.rs Co-authored-by: Ifeanyi Ubah --- src/ast/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 5eeb97d91..421e49a49 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3383,7 +3383,6 @@ pub enum Statement { /// iceberg: bool, /// Token that represents the end of the statement (semicolon or EOF) - /// Used for accurate span calculation end_token: AttachedToken, }, /// ```sql From 02be467d99417e0fc0ecd97b2fcf3b275a77f2c2 Mon Sep 17 00:00:00 2001 From: Tyler White <50381805+IndexSeek@users.noreply.github.com> Date: Fri, 8 Aug 2025 21:32:49 -0400 Subject: [PATCH 3/3] refactor: remove redundant test for ALTER TABLE span calculation --- tests/sqlparser_common.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 5403d4f13..a64733d67 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4758,25 +4758,6 @@ fn parse_alter_table() { } } -#[test] -fn alter_table_span_includes_semicolon() { - use sqlparser::ast::Spanned; - use sqlparser::dialect::PostgreSqlDialect; - use sqlparser::parser::Parser; - - let sql = r#"-- foo -ALTER TABLE users - ADD COLUMN foo - varchar; -- hi there"#; - let dialect = PostgreSqlDialect {}; - let ast = Parser::parse_sql(&dialect, sql).unwrap(); - let stmt = &ast[0]; - let span = stmt.span(); - - assert_eq!(span.end.line, 4); - assert_eq!(span.end.column, 11); -} - #[test] fn parse_rename_table() { match verified_stmt("RENAME TABLE test.test1 TO test_db.test2") {