Skip to content

Commit 9e7ae4b

Browse files
committed
Include UPDATE keyword in statement span
1 parent 308a723 commit 9e7ae4b

File tree

6 files changed

+94
-30
lines changed

6 files changed

+94
-30
lines changed

src/ast/dml.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ use sqlparser_derive::{Visit, VisitMut};
2727
use crate::display_utils::{indented_list, Indent, SpaceOrNewline};
2828

2929
use super::{
30-
display_comma_separated, query::InputFormatClause, Assignment, Expr, FromTable, Ident,
31-
InsertAliases, MysqlInsertPriority, ObjectName, OnInsert, OrderByExpr, Query, SelectItem,
32-
Setting, SqliteOnConflict, TableObject, TableWithJoins, UpdateTableFromKind,
30+
display_comma_separated, helpers::attached_token::AttachedToken, query::InputFormatClause,
31+
Assignment, Expr, FromTable, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnInsert,
32+
OrderByExpr, Query, SelectItem, Setting, SqliteOnConflict, TableObject, TableWithJoins,
33+
UpdateTableFromKind,
3334
};
3435

3536
/// INSERT statement.
@@ -246,6 +247,8 @@ impl Display for Delete {
246247
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
247248
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
248249
pub struct Update {
250+
/// Token for the `UPDATE` keyword
251+
pub update_token: AttachedToken,
249252
/// TABLE
250253
pub table: TableWithJoins,
251254
/// Column assignments

src/ast/spans.rs

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,32 @@
1616
// under the License.
1717

1818
use crate::ast::{
19-
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterSchemaOperation, AlterTable,
20-
ColumnOptions, CreateView, ExportData, Owner, TypedString,
19+
AlterSchemaOperation, AlterTable, ColumnOptions, CreateView, ExportData, Owner, TypedString,
20+
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind,
2121
};
2222
use core::iter;
2323

2424
use crate::tokenizer::Span;
2525

2626
use super::{
27-
dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation,
28-
AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget,
29-
AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef,
30-
ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements,
31-
ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
32-
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr,
33-
ExprWithAlias, Fetch, FromTable, Function, FunctionArg, FunctionArgExpr,
34-
FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound,
35-
IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join,
36-
JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause,
27+
AccessExpr, AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Analyze, Array,
28+
Assignment, AssignmentTarget, AttachedToken, BeginEndStatements, CaseStatement, CloseCursor,
29+
ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatementBlock,
30+
ConditionalStatements, ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource,
31+
CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem,
32+
ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable, Function, FunctionArg,
33+
FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr,
34+
HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr,
35+
Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause,
3736
MatchRecognizePattern, Measure, NamedParenthesizedList, NamedWindowDefinition, ObjectName,
3837
ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy,
3938
OrderByExpr, OrderByKind, Partition, PivotValueSource, ProjectionSelect, Query, RaiseStatement,
4039
RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
4140
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript,
4241
SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject,
4342
TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Value, Values,
44-
ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill,
43+
ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill, dcl::SecondaryRoles,
44+
value::ValueWithSpan,
4545
};
4646

4747
/// Given an iterator of spans, return the [Span::union] of all spans.
@@ -867,6 +867,7 @@ impl Spanned for Delete {
867867
impl Spanned for Update {
868868
fn span(&self) -> Span {
869869
let Update {
870+
update_token,
870871
table,
871872
assignments,
872873
from,
@@ -878,6 +879,7 @@ impl Spanned for Update {
878879

879880
union_spans(
880881
core::iter::once(table.span())
882+
.chain(core::iter::once(update_token.0.span))
881883
.chain(assignments.iter().map(|i| i.span()))
882884
.chain(from.iter().map(|i| i.span()))
883885
.chain(selection.iter().map(|i| i.span()))
@@ -2438,25 +2440,37 @@ pub mod tests {
24382440
#[test]
24392441
pub fn test_cte() {
24402442
let dialect = &GenericDialect;
2441-
let mut test = SpanTest::new(dialect, "WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner");
2443+
let mut test = SpanTest::new(
2444+
dialect,
2445+
"WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner",
2446+
);
24422447

24432448
let query = test.0.parse_query().unwrap();
24442449

24452450
let select_span = query.span();
24462451

2447-
assert_eq!(test.get_source(select_span), "WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner");
2452+
assert_eq!(
2453+
test.get_source(select_span),
2454+
"WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner"
2455+
);
24482456
}
24492457

24502458
#[test]
24512459
pub fn test_snowflake_lateral_flatten() {
24522460
let dialect = &SnowflakeDialect;
2453-
let mut test = SpanTest::new(dialect, "SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED");
2461+
let mut test = SpanTest::new(
2462+
dialect,
2463+
"SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED",
2464+
);
24542465

24552466
let query = test.0.parse_select().unwrap();
24562467

24572468
let select_span = query.span();
24582469

2459-
assert_eq!(test.get_source(select_span), "SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED");
2470+
assert_eq!(
2471+
test.get_source(select_span),
2472+
"SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED"
2473+
);
24602474
}
24612475

24622476
#[test]
@@ -2535,4 +2549,22 @@ ALTER TABLE users
25352549
assert_eq!(stmt_span.start, (2, 13).into());
25362550
assert_eq!(stmt_span.end, (4, 11).into());
25372551
}
2552+
2553+
#[test]
2554+
fn test_update_statement_span() {
2555+
let sql = r#"-- foo
2556+
UPDATE foo
2557+
/* bar */
2558+
SET bar = 3
2559+
WHERE quux > 42 ;
2560+
"#;
2561+
2562+
let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap();
2563+
assert_eq!(1, r.len());
2564+
2565+
let stmt_span = r[0].span();
2566+
2567+
assert_eq!(stmt_span.start, (2, 7).into());
2568+
assert_eq!(stmt_span.end, (5, 17).into());
2569+
}
25382570
}

src/parser/mod.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ impl<'a> Parser<'a> {
590590
Keyword::INSERT => self.parse_insert(),
591591
Keyword::REPLACE => self.parse_replace(),
592592
Keyword::UNCACHE => self.parse_uncache_table(),
593-
Keyword::UPDATE => self.parse_update(),
593+
Keyword::UPDATE => self.parse_update(next_token),
594594
Keyword::ALTER => self.parse_alter(),
595595
Keyword::CALL => self.parse_call(),
596596
Keyword::COPY => self.parse_copy(),
@@ -12009,7 +12009,7 @@ impl<'a> Parser<'a> {
1200912009
} else if self.parse_keyword(Keyword::UPDATE) {
1201012010
Ok(Query {
1201112011
with,
12012-
body: self.parse_update_setexpr_boxed()?,
12012+
body: self.parse_update_setexpr_boxed(self.get_current_token().clone())?,
1201312013
order_by: None,
1201412014
limit_clause: None,
1201512015
fetch: None,
@@ -15734,11 +15734,14 @@ impl<'a> Parser<'a> {
1573415734
/// Parse an UPDATE statement, returning a `Box`ed SetExpr
1573515735
///
1573615736
/// This is used to reduce the size of the stack frames in debug builds
15737-
fn parse_update_setexpr_boxed(&mut self) -> Result<Box<SetExpr>, ParserError> {
15738-
Ok(Box::new(SetExpr::Update(self.parse_update()?)))
15737+
fn parse_update_setexpr_boxed(
15738+
&mut self,
15739+
update_token: TokenWithSpan,
15740+
) -> Result<Box<SetExpr>, ParserError> {
15741+
Ok(Box::new(SetExpr::Update(self.parse_update(update_token)?)))
1573915742
}
1574015743

15741-
pub fn parse_update(&mut self) -> Result<Statement, ParserError> {
15744+
pub fn parse_update(&mut self, update_token: TokenWithSpan) -> Result<Statement, ParserError> {
1574215745
let or = self.parse_conflict_clause();
1574315746
let table = self.parse_table_and_joins()?;
1574415747
let from_before_set = if self.parse_keyword(Keyword::FROM) {
@@ -15773,6 +15776,7 @@ impl<'a> Parser<'a> {
1577315776
None
1577415777
};
1577515778
Ok(Update {
15779+
update_token: update_token.into(),
1577615780
table,
1577715781
assignments,
1577815782
from,

tests/sqlparser_common.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ use sqlparser::dialect::{
3939
};
4040
use sqlparser::keywords::{Keyword, ALL_KEYWORDS};
4141
use sqlparser::parser::{Parser, ParserError, ParserOptions};
42-
use sqlparser::tokenizer::Tokenizer;
43-
use sqlparser::tokenizer::{Location, Span};
42+
use sqlparser::tokenizer::{Location, Span, TokenWithSpan};
43+
use sqlparser::tokenizer::{Token, Tokenizer};
4444
use test_utils::{
4545
all_dialects, all_dialects_where, all_dialects_with_options, alter_table_op, assert_eq_vec,
4646
call, expr_from_projection, join, number, only, table, table_alias, table_from_name,
@@ -440,6 +440,10 @@ fn parse_update_set_from() {
440440
assert_eq!(
441441
stmt,
442442
Statement::Update(Update {
443+
update_token: AttachedToken(TokenWithSpan {
444+
token: Token::make_keyword("UPDATE"),
445+
span: Span::new((1, 1).into(), (1, 7).into()),
446+
}),
443447
table: TableWithJoins {
444448
relation: table_from_name(ObjectName::from(vec![Ident::new("t1")])),
445449
joins: vec![],
@@ -535,6 +539,7 @@ fn parse_update_with_table_alias() {
535539
returning,
536540
or: None,
537541
limit: None,
542+
update_token,
538543
}) => {
539544
assert_eq!(
540545
TableWithJoins {
@@ -583,6 +588,13 @@ fn parse_update_with_table_alias() {
583588
selection
584589
);
585590
assert_eq!(None, returning);
591+
assert_eq!(
592+
AttachedToken(TokenWithSpan {
593+
token: Token::make_keyword("UPDATE"),
594+
span: Span::new((1, 1).into(), (1, 7).into()),
595+
}),
596+
update_token
597+
);
586598
}
587599
_ => unreachable!(),
588600
}

tests/sqlparser_mysql.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use sqlparser::ast::MysqlInsertPriority::{Delayed, HighPriority, LowPriority};
2626
use sqlparser::ast::*;
2727
use sqlparser::dialect::{GenericDialect, MySqlDialect};
2828
use sqlparser::parser::{ParserError, ParserOptions};
29-
use sqlparser::tokenizer::Span;
3029
use sqlparser::tokenizer::Token;
30+
use sqlparser::tokenizer::{Span, TokenWithSpan};
3131
use test_utils::*;
3232

3333
#[macro_use]
@@ -2623,6 +2623,7 @@ fn parse_update_with_joins() {
26232623
returning,
26242624
or: None,
26252625
limit: None,
2626+
update_token,
26262627
}) => {
26272628
assert_eq!(
26282629
TableWithJoins {
@@ -2697,6 +2698,13 @@ fn parse_update_with_joins() {
26972698
selection
26982699
);
26992700
assert_eq!(None, returning);
2701+
assert_eq!(
2702+
AttachedToken(TokenWithSpan {
2703+
token: Token::make_keyword("UPDATE"),
2704+
span: Span::new((1, 1).into(), (1, 7).into()),
2705+
}),
2706+
update_token
2707+
);
27002708
}
27012709
_ => unreachable!(),
27022710
}

tests/sqlparser_sqlite.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#[macro_use]
2323
mod test_utils;
2424

25+
use sqlparser::ast::helpers::attached_token::AttachedToken;
2526
use sqlparser::keywords::Keyword;
2627
use test_utils::*;
2728

@@ -30,7 +31,7 @@ use sqlparser::ast::Value::Placeholder;
3031
use sqlparser::ast::*;
3132
use sqlparser::dialect::{GenericDialect, SQLiteDialect};
3233
use sqlparser::parser::{ParserError, ParserOptions};
33-
use sqlparser::tokenizer::Token;
34+
use sqlparser::tokenizer::{Span, Token, TokenWithSpan};
3435

3536
#[test]
3637
fn pragma_no_value() {
@@ -494,7 +495,11 @@ fn parse_update_tuple_row_values() {
494495
},
495496
from: None,
496497
returning: None,
497-
limit: None
498+
limit: None,
499+
update_token: AttachedToken(TokenWithSpan {
500+
token: Token::make_keyword("UPDATE"),
501+
span: Span::new((1, 1).into(), (1, 7).into())
502+
})
498503
})
499504
);
500505
}

0 commit comments

Comments
 (0)