From 4c98e8fcb5df2b8fb81a6183598b51696abe6e73 Mon Sep 17 00:00:00 2001 From: Takahiro Ebato Date: Sun, 5 Jan 2025 13:43:32 +0900 Subject: [PATCH] fix(sqlparser): make LIMIT and OFFSET order flexible --- src/sqlparser/src/parser.rs | 20 ++++++++++---------- src/sqlparser/tests/testdata/select.yaml | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/sqlparser/src/parser.rs b/src/sqlparser/src/parser.rs index e81c06371d617..9eb3d9e439967 100644 --- a/src/sqlparser/src/parser.rs +++ b/src/sqlparser/src/parser.rs @@ -4248,17 +4248,17 @@ impl Parser<'_> { vec![] }; - let limit = if self.parse_keyword(Keyword::LIMIT) { - self.parse_limit()? - } else { - None - }; + let mut limit = None; + let mut offset = None; + for _x in 0..2 { + if limit.is_none() && self.parse_keyword(Keyword::LIMIT) { + limit = self.parse_limit()? + } - let offset = if self.parse_keyword(Keyword::OFFSET) { - Some(self.parse_offset()?) - } else { - None - }; + if offset.is_none() && self.parse_keyword(Keyword::OFFSET) { + offset = Some(self.parse_offset()?) + } + } let fetch = if self.parse_keyword(Keyword::FETCH) { if limit.is_some() { diff --git a/src/sqlparser/tests/testdata/select.yaml b/src/sqlparser/tests/testdata/select.yaml index 72de88bd1dfb4..78f7c6a001dbd 100644 --- a/src/sqlparser/tests/testdata/select.yaml +++ b/src/sqlparser/tests/testdata/select.yaml @@ -237,3 +237,25 @@ SELECT a0 + b0 FROM a_log, "B_log" formatted_sql: WITH a_log AS changelog from a, "B_log" AS changelog from public."B" SELECT a0 + b0 FROM a_log, "B_log" formatted_ast: 'Query(Query { with: Some(With { recursive: false, cte_tables: [Cte { alias: TableAlias { name: Ident { value: "a_log", quote_style: None }, columns: [] }, cte_inner: ChangeLog(ObjectName([Ident { value: "a", quote_style: None }])) }, Cte { alias: TableAlias { name: Ident { value: "B_log", quote_style: Some(''"'') }, columns: [] }, cte_inner: ChangeLog(ObjectName([Ident { value: "public", quote_style: None }, Ident { value: "B", quote_style: Some(''"'') }])) }] }), body: Select(Select { distinct: All, projection: [UnnamedExpr(BinaryOp { left: Identifier(Ident { value: "a0", quote_style: None }), op: Plus, right: Identifier(Ident { value: "b0", quote_style: None }) })], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "a_log", quote_style: None }]), alias: None, as_of: None }, joins: [] }, TableWithJoins { relation: Table { name: ObjectName([Ident { value: "B_log", quote_style: Some(''"'') }]), alias: None, as_of: None }, joins: [] }], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' +- input: SELECT * FROM t LIMIT 1 + formatted_sql: SELECT * FROM t LIMIT 1 + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [Wildcard(None)], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "t", quote_style: None }]), alias: None, as_of: None }, joins: [] }], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: Some(Value(Number("1"))), offset: None, fetch: None })' +- input: SELECT * FROM t OFFSET 1 + formatted_sql: SELECT * FROM t OFFSET 1 + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [Wildcard(None)], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "t", quote_style: None }]), alias: None, as_of: None }, joins: [] }], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: Some("1"), fetch: None })' +- input: SELECT * FROM t LIMIT 1 OFFSET 2 + formatted_sql: SELECT * FROM t LIMIT 1 OFFSET 2 + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [Wildcard(None)], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "t", quote_style: None }]), alias: None, as_of: None }, joins: [] }], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: Some(Value(Number("1"))), offset: Some("2"), fetch: None })' +- input: SELECT * FROM t OFFSET 2 LIMIT 1 + formatted_sql: SELECT * FROM t LIMIT 1 OFFSET 2 + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [Wildcard(None)], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "t", quote_style: None }]), alias: None, as_of: None }, joins: [] }], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: Some(Value(Number("1"))), offset: Some("2"), fetch: None })' +- input: SELECT * FROM t LIMIT 1 LIMIT 2 + error_msg: |- + sql parser error: expected end of statement, found: LIMIT + LINE 1: SELECT * FROM t LIMIT 1 LIMIT 2 + ^ +- input: SELECT * FROM t OFFSET 1 OFFSET 2 + error_msg: |- + sql parser error: expected end of statement, found: OFFSET + LINE 1: SELECT * FROM t OFFSET 1 OFFSET 2 + ^