Merge pull request #158 from mjibson/offset-rows

Add support for OFFSET without the ROWS keyword (a MySQL quirk, documented at https://dev.mysql.com/doc/refman/8.0/en/select.html)

Teach the parser to remember which variant it saw (ROWS/ROW/none).
This commit is contained in:
Nickolay Ponomarev 2020-04-20 05:38:04 +03:00 committed by GitHub
commit af852e78f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 96 additions and 25 deletions

View file

@ -26,8 +26,8 @@ pub use self::ddl::{
}; };
pub use self::operator::{BinaryOperator, UnaryOperator}; pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{ pub use self::query::{
Cte, Fetch, Join, JoinConstraint, JoinOperator, OrderByExpr, Query, Select, SelectItem, Cte, Fetch, Join, JoinConstraint, JoinOperator, Offset, OffsetRows, OrderByExpr, Query, Select,
SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, Values, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, Values,
}; };
pub use self::value::{DateTimeField, Value}; pub use self::value::{DateTimeField, Value};

View file

@ -24,8 +24,8 @@ pub struct Query {
pub order_by: Vec<OrderByExpr>, pub order_by: Vec<OrderByExpr>,
/// `LIMIT { <N> | ALL }` /// `LIMIT { <N> | ALL }`
pub limit: Option<Expr>, pub limit: Option<Expr>,
/// `OFFSET <N> { ROW | ROWS }` /// `OFFSET <N> [ { ROW | ROWS } ]`
pub offset: Option<Expr>, pub offset: Option<Offset>,
/// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }` /// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }`
pub fetch: Option<Fetch>, pub fetch: Option<Fetch>,
} }
@ -43,7 +43,7 @@ impl fmt::Display for Query {
write!(f, " LIMIT {}", limit)?; write!(f, " LIMIT {}", limit)?;
} }
if let Some(ref offset) = self.offset { if let Some(ref offset) = self.offset {
write!(f, " OFFSET {} ROWS", offset)?; write!(f, " {}", offset)?;
} }
if let Some(ref fetch) = self.fetch { if let Some(ref fetch) = self.fetch {
write!(f, " {}", 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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Fetch { pub struct Fetch {
pub with_ties: bool, pub with_ties: bool,

View file

@ -1970,10 +1970,16 @@ impl Parser {
} }
/// Parse an OFFSET clause /// Parse an OFFSET clause
pub fn parse_offset(&mut self) -> Result<Expr, ParserError> { pub fn parse_offset(&mut self) -> Result<Offset, ParserError> {
let value = Expr::Value(self.parse_number_value()?); let value = Expr::Value(self.parse_number_value()?);
self.expect_one_of_keywords(&["ROW", "ROWS"])?; let rows = if self.parse_keyword("ROW") {
Ok(value) OffsetRows::Row
} else if self.parse_keyword("ROWS") {
OffsetRows::Rows
} else {
OffsetRows::None
};
Ok(Offset { value, rows })
} }
/// Parse a FETCH clause /// Parse a FETCH clause

View file

@ -2265,34 +2265,52 @@ fn parse_invalid_subquery_without_parens() {
#[test] #[test]
fn parse_offset() { 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"); 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"); 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"); 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"); 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"); 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 { match ast.body {
SetExpr::Select(s) => match only(s.from).relation { SetExpr::Select(s) => match only(s.from).relation {
TableFactor::Derived { subquery, .. } => { TableFactor::Derived { subquery, .. } => {
assert_eq!(subquery.offset, Some(Expr::Value(number("2")))); assert_eq!(subquery.offset, expect);
} }
_ => panic!("Test broke"), _ => panic!("Test broke"),
}, },
_ => panic!("Test broke"), _ => panic!("Test broke"),
} }
let ast = verified_query("SELECT 'foo' OFFSET 0 ROWS"); let ast = verified_query("SELECT 'foo' OFFSET 0 ROWS");
assert_eq!(ast.offset, Some(Expr::Value(number("0")))); assert_eq!(
} ast.offset,
Some(Offset {
#[test] value: Expr::Value(number("0")),
fn parse_singular_row_offset() { rows: OffsetRows::Rows,
one_statement_parses_to( })
"SELECT foo FROM bar OFFSET 1 ROW", );
"SELECT foo FROM bar OFFSET 1 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( let ast = verified_query(
"SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY", "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); assert_eq!(ast.fetch, fetch_first_two_rows_only);
let ast = verified_query( let ast = verified_query(
"SELECT foo FROM (SELECT * FROM bar FETCH FIRST 2 ROWS ONLY) FETCH FIRST 2 ROWS ONLY", "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"), _ => 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"); 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); assert_eq!(ast.fetch, fetch_first_two_rows_only);
match ast.body { match ast.body {
SetExpr::Select(s) => match only(s.from).relation { SetExpr::Select(s) => match only(s.from).relation {
TableFactor::Derived { subquery, .. } => { 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); assert_eq!(subquery.fetch, fetch_first_two_rows_only);
} }
_ => panic!("Test broke"), _ => panic!("Test broke"),