diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 2f723f01..3ace38c0 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -26,8 +26,8 @@ pub use self::ddl::{ }; pub use self::operator::{BinaryOperator, UnaryOperator}; pub use self::query::{ - Cte, Fetch, Join, JoinConstraint, JoinOperator, OrderByExpr, Query, Select, SelectItem, - SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, Values, + Cte, Fetch, Join, JoinConstraint, JoinOperator, Offset, OffsetRows, OrderByExpr, Query, Select, + SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, Values, }; pub use self::value::{DateTimeField, Value}; diff --git a/src/ast/query.rs b/src/ast/query.rs index 3588257e..43342198 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -24,8 +24,8 @@ pub struct Query { pub order_by: Vec, /// `LIMIT { | ALL }` pub limit: Option, - /// `OFFSET { ROW | ROWS }` - pub offset: Option, + /// `OFFSET [ { ROW | ROWS } ]` + pub offset: Option, /// `FETCH { FIRST | NEXT } [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }` pub fetch: Option, } @@ -43,7 +43,7 @@ impl fmt::Display for Query { write!(f, " LIMIT {}", limit)?; } if let Some(ref offset) = self.offset { - write!(f, " OFFSET {} ROWS", offset)?; + write!(f, " {}", offset)?; } if let Some(ref fetch) = self.fetch { write!(f, " {}", fetch)?; @@ -391,6 +391,35 @@ impl fmt::Display for OrderByExpr { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Offset { + pub value: Expr, + pub rows: OffsetRows, +} + +impl fmt::Display for Offset { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "OFFSET {}{}", self.value, self.rows) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum OffsetRows { + None, + Row, + Rows, +} + +impl fmt::Display for OffsetRows { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + OffsetRows::None => Ok(()), + OffsetRows::Row => write!(f, " ROW"), + OffsetRows::Rows => write!(f, " ROWS"), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Fetch { pub with_ties: bool, diff --git a/src/parser.rs b/src/parser.rs index cdaf8989..3b61ad11 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1970,10 +1970,16 @@ impl Parser { } /// Parse an OFFSET clause - pub fn parse_offset(&mut self) -> Result { + pub fn parse_offset(&mut self) -> Result { let value = Expr::Value(self.parse_number_value()?); - self.expect_one_of_keywords(&["ROW", "ROWS"])?; - Ok(value) + let rows = if self.parse_keyword("ROW") { + OffsetRows::Row + } else if self.parse_keyword("ROWS") { + OffsetRows::Rows + } else { + OffsetRows::None + }; + Ok(Offset { value, rows }) } /// Parse a FETCH clause diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 939673dc..fe4013fa 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2265,34 +2265,52 @@ fn parse_invalid_subquery_without_parens() { #[test] fn parse_offset() { + let expect = Some(Offset { + value: Expr::Value(number("2")), + rows: OffsetRows::Rows, + }); let ast = verified_query("SELECT foo FROM bar OFFSET 2 ROWS"); - assert_eq!(ast.offset, Some(Expr::Value(number("2")))); + assert_eq!(ast.offset, expect); let ast = verified_query("SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS"); - assert_eq!(ast.offset, Some(Expr::Value(number("2")))); + assert_eq!(ast.offset, expect); let ast = verified_query("SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS"); - assert_eq!(ast.offset, Some(Expr::Value(number("2")))); + assert_eq!(ast.offset, expect); let ast = verified_query("SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS"); - assert_eq!(ast.offset, Some(Expr::Value(number("2")))); + assert_eq!(ast.offset, expect); let ast = verified_query("SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS"); - assert_eq!(ast.offset, Some(Expr::Value(number("2")))); + assert_eq!(ast.offset, expect); match ast.body { SetExpr::Select(s) => match only(s.from).relation { TableFactor::Derived { subquery, .. } => { - assert_eq!(subquery.offset, Some(Expr::Value(number("2")))); + assert_eq!(subquery.offset, expect); } _ => panic!("Test broke"), }, _ => panic!("Test broke"), } let ast = verified_query("SELECT 'foo' OFFSET 0 ROWS"); - assert_eq!(ast.offset, Some(Expr::Value(number("0")))); -} - -#[test] -fn parse_singular_row_offset() { - one_statement_parses_to( - "SELECT foo FROM bar OFFSET 1 ROW", - "SELECT foo FROM bar OFFSET 1 ROWS", + assert_eq!( + ast.offset, + Some(Offset { + value: Expr::Value(number("0")), + rows: OffsetRows::Rows, + }) + ); + let ast = verified_query("SELECT 'foo' OFFSET 1 ROW"); + assert_eq!( + ast.offset, + Some(Offset { + value: Expr::Value(number("1")), + rows: OffsetRows::Row, + }) + ); + let ast = verified_query("SELECT 'foo' OFFSET 1"); + assert_eq!( + ast.offset, + Some(Offset { + value: Expr::Value(number("1")), + rows: OffsetRows::None, + }) ); } @@ -2343,7 +2361,13 @@ fn parse_fetch() { let ast = verified_query( "SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY", ); - assert_eq!(ast.offset, Some(Expr::Value(number("2")))); + assert_eq!( + ast.offset, + Some(Offset { + value: Expr::Value(number("2")), + rows: OffsetRows::Rows, + }) + ); assert_eq!(ast.fetch, fetch_first_two_rows_only); let ast = verified_query( "SELECT foo FROM (SELECT * FROM bar FETCH FIRST 2 ROWS ONLY) FETCH FIRST 2 ROWS ONLY", @@ -2359,12 +2383,24 @@ fn parse_fetch() { _ => panic!("Test broke"), } let ast = verified_query("SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY) OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY"); - assert_eq!(ast.offset, Some(Expr::Value(number("2")))); + assert_eq!( + ast.offset, + Some(Offset { + value: Expr::Value(number("2")), + rows: OffsetRows::Rows, + }) + ); assert_eq!(ast.fetch, fetch_first_two_rows_only); match ast.body { SetExpr::Select(s) => match only(s.from).relation { TableFactor::Derived { subquery, .. } => { - assert_eq!(subquery.offset, Some(Expr::Value(number("2")))); + assert_eq!( + subquery.offset, + Some(Offset { + value: Expr::Value(number("2")), + rows: OffsetRows::Rows, + }) + ); assert_eq!(subquery.fetch, fetch_first_two_rows_only); } _ => panic!("Test broke"),