mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-14 09:55:01 +00:00
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:
commit
af852e78f6
4 changed files with 96 additions and 25 deletions
|
@ -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};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue