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::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};

View file

@ -24,8 +24,8 @@ pub struct Query {
pub order_by: Vec<OrderByExpr>,
/// `LIMIT { <N> | ALL }`
pub limit: Option<Expr>,
/// `OFFSET <N> { ROW | ROWS }`
pub offset: Option<Expr>,
/// `OFFSET <N> [ { ROW | ROWS } ]`
pub offset: Option<Offset>,
/// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }`
pub fetch: Option<Fetch>,
}
@ -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,

View file

@ -1970,10 +1970,16 @@ impl Parser {
}
/// 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()?);
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

View file

@ -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"),