Add support for OFFSET with the ROWS keyword

MySQL doesn't support the ROWS part of OFFSET. Teach the parser to
remember which variant it saw, including just ROW.
This commit is contained in:
Matt Jibson 2020-04-13 17:59:10 -06:00
parent 05a29212ff
commit c0b0b5924d
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"),