mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-04 06:18:17 +00:00
Preserve MySQL-style LIMIT <offset>, <limit>
syntax (#1765)
This commit is contained in:
parent
85f855150f
commit
fb578bb419
10 changed files with 327 additions and 266 deletions
|
@ -66,17 +66,18 @@ pub use self::query::{
|
||||||
FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem,
|
FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem,
|
||||||
InputFormatClause, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator,
|
InputFormatClause, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator,
|
||||||
JsonTableColumn, JsonTableColumnErrorHandling, JsonTableNamedColumn, JsonTableNestedColumn,
|
JsonTableColumn, JsonTableColumnErrorHandling, JsonTableNamedColumn, JsonTableNestedColumn,
|
||||||
LateralView, LockClause, LockType, MatchRecognizePattern, MatchRecognizeSymbol, Measure,
|
LateralView, LimitClause, LockClause, LockType, MatchRecognizePattern, MatchRecognizeSymbol,
|
||||||
NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset, OffsetRows, OpenJsonTableColumn,
|
Measure, NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset, OffsetRows,
|
||||||
OrderBy, OrderByExpr, OrderByKind, OrderByOptions, PivotValueSource, ProjectionSelect, Query,
|
OpenJsonTableColumn, OrderBy, OrderByExpr, OrderByKind, OrderByOptions, PivotValueSource,
|
||||||
RenameSelectItem, RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch,
|
ProjectionSelect, Query, RenameSelectItem, RepetitionQuantifier, ReplaceSelectElement,
|
||||||
Select, SelectFlavor, SelectInto, SelectItem, SelectItemQualifiedWildcardKind, SetExpr,
|
ReplaceSelectItem, RowsPerMatch, Select, SelectFlavor, SelectInto, SelectItem,
|
||||||
SetOperator, SetQuantifier, Setting, SymbolDefinition, Table, TableAlias, TableAliasColumnDef,
|
SelectItemQualifiedWildcardKind, SetExpr, SetOperator, SetQuantifier, Setting,
|
||||||
TableFactor, TableFunctionArgs, TableIndexHintForClause, TableIndexHintType, TableIndexHints,
|
SymbolDefinition, Table, TableAlias, TableAliasColumnDef, TableFactor, TableFunctionArgs,
|
||||||
TableIndexType, TableSample, TableSampleBucket, TableSampleKind, TableSampleMethod,
|
TableIndexHintForClause, TableIndexHintType, TableIndexHints, TableIndexType, TableSample,
|
||||||
TableSampleModifier, TableSampleQuantity, TableSampleSeed, TableSampleSeedModifier,
|
TableSampleBucket, TableSampleKind, TableSampleMethod, TableSampleModifier,
|
||||||
TableSampleUnit, TableVersion, TableWithJoins, Top, TopQuantity, UpdateTableFromKind,
|
TableSampleQuantity, TableSampleSeed, TableSampleSeedModifier, TableSampleUnit, TableVersion,
|
||||||
ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
|
TableWithJoins, Top, TopQuantity, UpdateTableFromKind, ValueTableMode, Values,
|
||||||
|
WildcardAdditionalOptions, With, WithFill,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::trigger::{
|
pub use self::trigger::{
|
||||||
|
|
|
@ -43,14 +43,8 @@ pub struct Query {
|
||||||
pub body: Box<SetExpr>,
|
pub body: Box<SetExpr>,
|
||||||
/// ORDER BY
|
/// ORDER BY
|
||||||
pub order_by: Option<OrderBy>,
|
pub order_by: Option<OrderBy>,
|
||||||
/// `LIMIT { <N> | ALL }`
|
/// `LIMIT ... OFFSET ... | LIMIT <offset>, <limit>`
|
||||||
pub limit: Option<Expr>,
|
pub limit_clause: Option<LimitClause>,
|
||||||
|
|
||||||
/// `LIMIT { <N> } BY { <expr>,<expr>,... } }`
|
|
||||||
pub limit_by: Vec<Expr>,
|
|
||||||
|
|
||||||
/// `OFFSET <N> [ { ROW | ROWS } ]`
|
|
||||||
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>,
|
||||||
/// `FOR { UPDATE | SHARE } [ OF table_name ] [ SKIP LOCKED | NOWAIT ]`
|
/// `FOR { UPDATE | SHARE } [ OF table_name ] [ SKIP LOCKED | NOWAIT ]`
|
||||||
|
@ -79,14 +73,9 @@ impl fmt::Display for Query {
|
||||||
if let Some(ref order_by) = self.order_by {
|
if let Some(ref order_by) = self.order_by {
|
||||||
write!(f, " {order_by}")?;
|
write!(f, " {order_by}")?;
|
||||||
}
|
}
|
||||||
if let Some(ref limit) = self.limit {
|
|
||||||
write!(f, " LIMIT {limit}")?;
|
if let Some(ref limit_clause) = self.limit_clause {
|
||||||
}
|
limit_clause.fmt(f)?;
|
||||||
if let Some(ref offset) = self.offset {
|
|
||||||
write!(f, " {offset}")?;
|
|
||||||
}
|
|
||||||
if !self.limit_by.is_empty() {
|
|
||||||
write!(f, " BY {}", display_separated(&self.limit_by, ", "))?;
|
|
||||||
}
|
}
|
||||||
if let Some(ref settings) = self.settings {
|
if let Some(ref settings) = self.settings {
|
||||||
write!(f, " SETTINGS {}", display_comma_separated(settings))?;
|
write!(f, " SETTINGS {}", display_comma_separated(settings))?;
|
||||||
|
@ -2374,6 +2363,58 @@ impl fmt::Display for OrderByOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub enum LimitClause {
|
||||||
|
/// Standard SQL syntax
|
||||||
|
///
|
||||||
|
/// `LIMIT <limit> [BY <expr>,<expr>,...] [OFFSET <offset>]`
|
||||||
|
LimitOffset {
|
||||||
|
/// `LIMIT { <N> | ALL }`
|
||||||
|
limit: Option<Expr>,
|
||||||
|
/// `OFFSET <N> [ { ROW | ROWS } ]`
|
||||||
|
offset: Option<Offset>,
|
||||||
|
/// `BY { <expr>,<expr>,... } }`
|
||||||
|
///
|
||||||
|
/// [ClickHouse](https://clickhouse.com/docs/sql-reference/statements/select/limit-by)
|
||||||
|
limit_by: Vec<Expr>,
|
||||||
|
},
|
||||||
|
/// [MySQL]-specific syntax; the order of expressions is reversed.
|
||||||
|
///
|
||||||
|
/// `LIMIT <offset>, <limit>`
|
||||||
|
///
|
||||||
|
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/select.html
|
||||||
|
OffsetCommaLimit { offset: Expr, limit: Expr },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LimitClause {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
LimitClause::LimitOffset {
|
||||||
|
limit,
|
||||||
|
limit_by,
|
||||||
|
offset,
|
||||||
|
} => {
|
||||||
|
if let Some(ref limit) = limit {
|
||||||
|
write!(f, " LIMIT {limit}")?;
|
||||||
|
}
|
||||||
|
if let Some(ref offset) = offset {
|
||||||
|
write!(f, " {offset}")?;
|
||||||
|
}
|
||||||
|
if !limit_by.is_empty() {
|
||||||
|
debug_assert!(limit.is_some());
|
||||||
|
write!(f, " BY {}", display_separated(limit_by, ", "))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
LimitClause::OffsetCommaLimit { offset, limit } => {
|
||||||
|
write!(f, " LIMIT {}, {}", offset, limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
|
|
@ -29,8 +29,8 @@ use super::{
|
||||||
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
|
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
|
||||||
FunctionArguments, GroupByExpr, HavingBound, IlikeSelectItem, Insert, Interpolate,
|
FunctionArguments, GroupByExpr, HavingBound, IlikeSelectItem, Insert, Interpolate,
|
||||||
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
|
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
|
||||||
MatchRecognizePattern, Measure, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset,
|
LimitClause, MatchRecognizePattern, Measure, NamedWindowDefinition, ObjectName, ObjectNamePart,
|
||||||
OnConflict, OnConflictAction, OnInsert, OrderBy, OrderByExpr, OrderByKind, Partition,
|
Offset, OnConflict, OnConflictAction, OnInsert, OrderBy, OrderByExpr, OrderByKind, Partition,
|
||||||
PivotValueSource, ProjectionSelect, Query, ReferentialAction, RenameSelectItem,
|
PivotValueSource, ProjectionSelect, Query, ReferentialAction, RenameSelectItem,
|
||||||
ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption,
|
ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption,
|
||||||
Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint,
|
Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint,
|
||||||
|
@ -94,9 +94,7 @@ impl Spanned for Query {
|
||||||
with,
|
with,
|
||||||
body,
|
body,
|
||||||
order_by,
|
order_by,
|
||||||
limit,
|
limit_clause,
|
||||||
limit_by,
|
|
||||||
offset,
|
|
||||||
fetch,
|
fetch,
|
||||||
locks: _, // todo
|
locks: _, // todo
|
||||||
for_clause: _, // todo, mssql specific
|
for_clause: _, // todo, mssql specific
|
||||||
|
@ -109,14 +107,31 @@ impl Spanned for Query {
|
||||||
.map(|i| i.span())
|
.map(|i| i.span())
|
||||||
.chain(core::iter::once(body.span()))
|
.chain(core::iter::once(body.span()))
|
||||||
.chain(order_by.as_ref().map(|i| i.span()))
|
.chain(order_by.as_ref().map(|i| i.span()))
|
||||||
.chain(limit.as_ref().map(|i| i.span()))
|
.chain(limit_clause.as_ref().map(|i| i.span()))
|
||||||
.chain(limit_by.iter().map(|i| i.span()))
|
|
||||||
.chain(offset.as_ref().map(|i| i.span()))
|
|
||||||
.chain(fetch.as_ref().map(|i| i.span())),
|
.chain(fetch.as_ref().map(|i| i.span())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Spanned for LimitClause {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
LimitClause::LimitOffset {
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
limit_by,
|
||||||
|
} => union_spans(
|
||||||
|
limit
|
||||||
|
.iter()
|
||||||
|
.map(|i| i.span())
|
||||||
|
.chain(offset.as_ref().map(|i| i.span()))
|
||||||
|
.chain(limit_by.iter().map(|i| i.span())),
|
||||||
|
),
|
||||||
|
LimitClause::OffsetCommaLimit { offset, limit } => offset.span().union(&limit.span()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Spanned for Offset {
|
impl Spanned for Offset {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
let Offset {
|
let Offset {
|
||||||
|
|
|
@ -523,7 +523,7 @@ where
|
||||||
/// // Remove all select limits in sub-queries
|
/// // Remove all select limits in sub-queries
|
||||||
/// visit_expressions_mut(&mut statements, |expr| {
|
/// visit_expressions_mut(&mut statements, |expr| {
|
||||||
/// if let Expr::Subquery(q) = expr {
|
/// if let Expr::Subquery(q) = expr {
|
||||||
/// q.limit = None
|
/// q.limit_clause = None;
|
||||||
/// }
|
/// }
|
||||||
/// ControlFlow::<()>::Continue(())
|
/// ControlFlow::<()>::Continue(())
|
||||||
/// });
|
/// });
|
||||||
|
@ -647,7 +647,7 @@ where
|
||||||
/// // Remove all select limits in outer statements (not in sub-queries)
|
/// // Remove all select limits in outer statements (not in sub-queries)
|
||||||
/// visit_statements_mut(&mut statements, |stmt| {
|
/// visit_statements_mut(&mut statements, |stmt| {
|
||||||
/// if let Statement::Query(q) = stmt {
|
/// if let Statement::Query(q) = stmt {
|
||||||
/// q.limit = None
|
/// q.limit_clause = None;
|
||||||
/// }
|
/// }
|
||||||
/// ControlFlow::<()>::Continue(())
|
/// ControlFlow::<()>::Continue(())
|
||||||
/// });
|
/// });
|
||||||
|
|
|
@ -9491,6 +9491,60 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_optional_limit_clause(&mut self) -> Result<Option<LimitClause>, ParserError> {
|
||||||
|
let mut offset = if self.parse_keyword(Keyword::OFFSET) {
|
||||||
|
Some(self.parse_offset()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let (limit, limit_by) = if self.parse_keyword(Keyword::LIMIT) {
|
||||||
|
let expr = self.parse_limit()?;
|
||||||
|
|
||||||
|
if self.dialect.supports_limit_comma()
|
||||||
|
&& offset.is_none()
|
||||||
|
&& expr.is_some() // ALL not supported with comma
|
||||||
|
&& self.consume_token(&Token::Comma)
|
||||||
|
{
|
||||||
|
let offset = expr.ok_or_else(|| {
|
||||||
|
ParserError::ParserError(
|
||||||
|
"Missing offset for LIMIT <offset>, <limit>".to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
return Ok(Some(LimitClause::OffsetCommaLimit {
|
||||||
|
offset,
|
||||||
|
limit: self.parse_expr()?,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let limit_by = if dialect_of!(self is ClickHouseDialect | GenericDialect)
|
||||||
|
&& self.parse_keyword(Keyword::BY)
|
||||||
|
{
|
||||||
|
Some(self.parse_comma_separated(Parser::parse_expr)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
(Some(expr), limit_by)
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
if offset.is_none() && limit.is_some() && self.parse_keyword(Keyword::OFFSET) {
|
||||||
|
offset = Some(self.parse_offset()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset.is_some() || (limit.is_some() && limit != Some(None)) || limit_by.is_some() {
|
||||||
|
Ok(Some(LimitClause::LimitOffset {
|
||||||
|
limit: limit.unwrap_or_default(),
|
||||||
|
offset,
|
||||||
|
limit_by: limit_by.unwrap_or_default(),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a table object for insertion
|
/// Parse a table object for insertion
|
||||||
/// e.g. `some_database.some_table` or `FUNCTION some_table_func(...)`
|
/// e.g. `some_database.some_table` or `FUNCTION some_table_func(...)`
|
||||||
pub fn parse_table_object(&mut self) -> Result<TableObject, ParserError> {
|
pub fn parse_table_object(&mut self) -> Result<TableObject, ParserError> {
|
||||||
|
@ -10231,10 +10285,8 @@ impl<'a> Parser<'a> {
|
||||||
Ok(Query {
|
Ok(Query {
|
||||||
with,
|
with,
|
||||||
body: self.parse_insert_setexpr_boxed()?,
|
body: self.parse_insert_setexpr_boxed()?,
|
||||||
limit: None,
|
|
||||||
limit_by: vec![],
|
|
||||||
order_by: None,
|
order_by: None,
|
||||||
offset: None,
|
limit_clause: None,
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -10246,10 +10298,8 @@ impl<'a> Parser<'a> {
|
||||||
Ok(Query {
|
Ok(Query {
|
||||||
with,
|
with,
|
||||||
body: self.parse_update_setexpr_boxed()?,
|
body: self.parse_update_setexpr_boxed()?,
|
||||||
limit: None,
|
|
||||||
limit_by: vec![],
|
|
||||||
order_by: None,
|
order_by: None,
|
||||||
offset: None,
|
limit_clause: None,
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -10261,10 +10311,8 @@ impl<'a> Parser<'a> {
|
||||||
Ok(Query {
|
Ok(Query {
|
||||||
with,
|
with,
|
||||||
body: self.parse_delete_setexpr_boxed()?,
|
body: self.parse_delete_setexpr_boxed()?,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
order_by: None,
|
order_by: None,
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -10277,40 +10325,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
let order_by = self.parse_optional_order_by()?;
|
let order_by = self.parse_optional_order_by()?;
|
||||||
|
|
||||||
let mut limit = None;
|
let limit_clause = self.parse_optional_limit_clause()?;
|
||||||
let mut offset = None;
|
|
||||||
|
|
||||||
for _x in 0..2 {
|
|
||||||
if limit.is_none() && self.parse_keyword(Keyword::LIMIT) {
|
|
||||||
limit = self.parse_limit()?
|
|
||||||
}
|
|
||||||
|
|
||||||
if offset.is_none() && self.parse_keyword(Keyword::OFFSET) {
|
|
||||||
offset = Some(self.parse_offset()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.dialect.supports_limit_comma()
|
|
||||||
&& limit.is_some()
|
|
||||||
&& offset.is_none()
|
|
||||||
&& self.consume_token(&Token::Comma)
|
|
||||||
{
|
|
||||||
// MySQL style LIMIT x,y => LIMIT y OFFSET x.
|
|
||||||
// Check <https://dev.mysql.com/doc/refman/8.0/en/select.html> for more details.
|
|
||||||
offset = Some(Offset {
|
|
||||||
value: limit.unwrap(),
|
|
||||||
rows: OffsetRows::None,
|
|
||||||
});
|
|
||||||
limit = Some(self.parse_expr()?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let limit_by = if dialect_of!(self is ClickHouseDialect | GenericDialect)
|
|
||||||
&& self.parse_keyword(Keyword::BY)
|
|
||||||
{
|
|
||||||
self.parse_comma_separated(Parser::parse_expr)?
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
};
|
|
||||||
|
|
||||||
let settings = self.parse_settings()?;
|
let settings = self.parse_settings()?;
|
||||||
|
|
||||||
|
@ -10347,9 +10362,7 @@ impl<'a> Parser<'a> {
|
||||||
with,
|
with,
|
||||||
body,
|
body,
|
||||||
order_by,
|
order_by,
|
||||||
limit,
|
limit_clause,
|
||||||
limit_by,
|
|
||||||
offset,
|
|
||||||
fetch,
|
fetch,
|
||||||
locks,
|
locks,
|
||||||
for_clause,
|
for_clause,
|
||||||
|
@ -11809,9 +11822,7 @@ impl<'a> Parser<'a> {
|
||||||
with: None,
|
with: None,
|
||||||
body: Box::new(values),
|
body: Box::new(values),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
|
|
@ -944,6 +944,12 @@ fn parse_limit_by() {
|
||||||
clickhouse_and_generic().verified_stmt(
|
clickhouse_and_generic().verified_stmt(
|
||||||
r#"SELECT * FROM default.last_asset_runs_mv ORDER BY created_at DESC LIMIT 1 BY asset, toStartOfDay(created_at)"#,
|
r#"SELECT * FROM default.last_asset_runs_mv ORDER BY created_at DESC LIMIT 1 BY asset, toStartOfDay(created_at)"#,
|
||||||
);
|
);
|
||||||
|
clickhouse_and_generic().parse_sql_statements(
|
||||||
|
r#"SELECT * FROM default.last_asset_runs_mv ORDER BY created_at DESC BY asset, toStartOfDay(created_at)"#,
|
||||||
|
).expect_err("BY without LIMIT");
|
||||||
|
clickhouse_and_generic()
|
||||||
|
.parse_sql_statements("SELECT * FROM T OFFSET 5 BY foo")
|
||||||
|
.expect_err("BY with OFFSET but without LIMIT");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1107,7 +1113,14 @@ fn parse_select_order_by_with_fill_interpolate() {
|
||||||
},
|
},
|
||||||
select.order_by.expect("ORDER BY expected")
|
select.order_by.expect("ORDER BY expected")
|
||||||
);
|
);
|
||||||
assert_eq!(Some(Expr::value(number("2"))), select.limit);
|
assert_eq!(
|
||||||
|
select.limit_clause,
|
||||||
|
Some(LimitClause::LimitOffset {
|
||||||
|
limit: Some(Expr::value(number("2"))),
|
||||||
|
offset: None,
|
||||||
|
limit_by: vec![]
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -483,9 +483,7 @@ fn parse_update_set_from() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -900,7 +898,12 @@ fn parse_simple_select() {
|
||||||
assert!(select.distinct.is_none());
|
assert!(select.distinct.is_none());
|
||||||
assert_eq!(3, select.projection.len());
|
assert_eq!(3, select.projection.len());
|
||||||
let select = verified_query(sql);
|
let select = verified_query(sql);
|
||||||
assert_eq!(Some(Expr::value(number("5"))), select.limit);
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
|
limit: Some(Expr::value(number("5"))),
|
||||||
|
offset: None,
|
||||||
|
limit_by: vec![],
|
||||||
|
};
|
||||||
|
assert_eq!(Some(expected_limit_clause), select.limit_clause);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -908,14 +911,31 @@ fn parse_limit() {
|
||||||
verified_stmt("SELECT * FROM user LIMIT 1");
|
verified_stmt("SELECT * FROM user LIMIT 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_invalid_limit_by() {
|
||||||
|
all_dialects()
|
||||||
|
.parse_sql_statements("SELECT * FROM user BY name")
|
||||||
|
.expect_err("BY without LIMIT");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_limit_is_not_an_alias() {
|
fn parse_limit_is_not_an_alias() {
|
||||||
// In dialects supporting LIMIT it shouldn't be parsed as a table alias
|
// In dialects supporting LIMIT it shouldn't be parsed as a table alias
|
||||||
let ast = verified_query("SELECT id FROM customer LIMIT 1");
|
let ast = verified_query("SELECT id FROM customer LIMIT 1");
|
||||||
assert_eq!(Some(Expr::value(number("1"))), ast.limit);
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
|
limit: Some(Expr::value(number("1"))),
|
||||||
|
offset: None,
|
||||||
|
limit_by: vec![],
|
||||||
|
};
|
||||||
|
assert_eq!(Some(expected_limit_clause), ast.limit_clause);
|
||||||
|
|
||||||
let ast = verified_query("SELECT 1 LIMIT 5");
|
let ast = verified_query("SELECT 1 LIMIT 5");
|
||||||
assert_eq!(Some(Expr::value(number("5"))), ast.limit);
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
|
limit: Some(Expr::value(number("5"))),
|
||||||
|
offset: None,
|
||||||
|
limit_by: vec![],
|
||||||
|
};
|
||||||
|
assert_eq!(Some(expected_limit_clause), ast.limit_clause);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2493,7 +2513,12 @@ fn parse_select_order_by_limit() {
|
||||||
]),
|
]),
|
||||||
select.order_by.expect("ORDER BY expected").kind
|
select.order_by.expect("ORDER BY expected").kind
|
||||||
);
|
);
|
||||||
assert_eq!(Some(Expr::value(number("2"))), select.limit);
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
|
limit: Some(Expr::value(number("2"))),
|
||||||
|
offset: None,
|
||||||
|
limit_by: vec![],
|
||||||
|
};
|
||||||
|
assert_eq!(Some(expected_limit_clause), select.limit_clause);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2654,7 +2679,12 @@ fn parse_select_order_by_nulls_order() {
|
||||||
]),
|
]),
|
||||||
select.order_by.expect("ORDER BY expeccted").kind
|
select.order_by.expect("ORDER BY expeccted").kind
|
||||||
);
|
);
|
||||||
assert_eq!(Some(Expr::value(number("2"))), select.limit);
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
|
limit: Some(Expr::value(number("2"))),
|
||||||
|
offset: None,
|
||||||
|
limit_by: vec![],
|
||||||
|
};
|
||||||
|
assert_eq!(Some(expected_limit_clause), select.limit_clause);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2864,6 +2894,14 @@ fn parse_limit_accepts_all() {
|
||||||
"SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT ALL",
|
"SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT ALL",
|
||||||
"SELECT id, fname, lname FROM customer WHERE id = 1",
|
"SELECT id, fname, lname FROM customer WHERE id = 1",
|
||||||
);
|
);
|
||||||
|
one_statement_parses_to(
|
||||||
|
"SELECT id, fname, lname FROM customer WHERE id = 1 LIMIT ALL OFFSET 1",
|
||||||
|
"SELECT id, fname, lname FROM customer WHERE id = 1 OFFSET 1",
|
||||||
|
);
|
||||||
|
one_statement_parses_to(
|
||||||
|
"SELECT id, fname, lname FROM customer WHERE id = 1 OFFSET 1 LIMIT ALL",
|
||||||
|
"SELECT id, fname, lname FROM customer WHERE id = 1 OFFSET 1",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -4247,9 +4285,7 @@ fn parse_create_table_as_table() {
|
||||||
schema_name: None,
|
schema_name: None,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -4274,9 +4310,7 @@ fn parse_create_table_as_table() {
|
||||||
schema_name: Some("schema_name".to_string()),
|
schema_name: Some("schema_name".to_string()),
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -6273,9 +6307,7 @@ fn parse_interval_and_or_xor() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -8175,55 +8207,65 @@ fn parse_offset() {
|
||||||
let dialects =
|
let dialects =
|
||||||
all_dialects_where(|d| !d.is_column_alias(&Keyword::OFFSET, &mut Parser::new(d)));
|
all_dialects_where(|d| !d.is_column_alias(&Keyword::OFFSET, &mut Parser::new(d)));
|
||||||
|
|
||||||
let expect = Some(Offset {
|
let expected_limit_clause = &Some(LimitClause::LimitOffset {
|
||||||
value: Expr::value(number("2")),
|
limit: None,
|
||||||
rows: OffsetRows::Rows,
|
offset: Some(Offset {
|
||||||
|
value: Expr::value(number("2")),
|
||||||
|
rows: OffsetRows::Rows,
|
||||||
|
}),
|
||||||
|
limit_by: vec![],
|
||||||
});
|
});
|
||||||
let ast = dialects.verified_query("SELECT foo FROM bar OFFSET 2 ROWS");
|
let ast = dialects.verified_query("SELECT foo FROM bar OFFSET 2 ROWS");
|
||||||
assert_eq!(ast.offset, expect);
|
assert_eq!(&ast.limit_clause, expected_limit_clause);
|
||||||
let ast = dialects.verified_query("SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS");
|
let ast = dialects.verified_query("SELECT foo FROM bar WHERE foo = 4 OFFSET 2 ROWS");
|
||||||
assert_eq!(ast.offset, expect);
|
assert_eq!(&ast.limit_clause, expected_limit_clause);
|
||||||
let ast = dialects.verified_query("SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS");
|
let ast = dialects.verified_query("SELECT foo FROM bar ORDER BY baz OFFSET 2 ROWS");
|
||||||
assert_eq!(ast.offset, expect);
|
assert_eq!(&ast.limit_clause, expected_limit_clause);
|
||||||
let ast =
|
let ast =
|
||||||
dialects.verified_query("SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS");
|
dialects.verified_query("SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS");
|
||||||
assert_eq!(ast.offset, expect);
|
assert_eq!(&ast.limit_clause, expected_limit_clause);
|
||||||
let ast =
|
let ast =
|
||||||
dialects.verified_query("SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS");
|
dialects.verified_query("SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS) OFFSET 2 ROWS");
|
||||||
assert_eq!(ast.offset, expect);
|
assert_eq!(&ast.limit_clause, expected_limit_clause);
|
||||||
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, expect);
|
assert_eq!(&subquery.limit_clause, expected_limit_clause);
|
||||||
}
|
}
|
||||||
_ => panic!("Test broke"),
|
_ => panic!("Test broke"),
|
||||||
},
|
},
|
||||||
_ => panic!("Test broke"),
|
_ => panic!("Test broke"),
|
||||||
}
|
}
|
||||||
let ast = dialects.verified_query("SELECT 'foo' OFFSET 0 ROWS");
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
assert_eq!(
|
limit: None,
|
||||||
ast.offset,
|
offset: Some(Offset {
|
||||||
Some(Offset {
|
|
||||||
value: Expr::value(number("0")),
|
value: Expr::value(number("0")),
|
||||||
rows: OffsetRows::Rows,
|
rows: OffsetRows::Rows,
|
||||||
})
|
}),
|
||||||
);
|
limit_by: vec![],
|
||||||
let ast = dialects.verified_query("SELECT 'foo' OFFSET 1 ROW");
|
};
|
||||||
assert_eq!(
|
let ast = dialects.verified_query("SELECT 'foo' OFFSET 0 ROWS");
|
||||||
ast.offset,
|
assert_eq!(ast.limit_clause, Some(expected_limit_clause));
|
||||||
Some(Offset {
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
|
limit: None,
|
||||||
|
offset: Some(Offset {
|
||||||
value: Expr::value(number("1")),
|
value: Expr::value(number("1")),
|
||||||
rows: OffsetRows::Row,
|
rows: OffsetRows::Row,
|
||||||
})
|
}),
|
||||||
);
|
limit_by: vec![],
|
||||||
let ast = dialects.verified_query("SELECT 'foo' OFFSET 1");
|
};
|
||||||
assert_eq!(
|
let ast = dialects.verified_query("SELECT 'foo' OFFSET 1 ROW");
|
||||||
ast.offset,
|
assert_eq!(ast.limit_clause, Some(expected_limit_clause));
|
||||||
Some(Offset {
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
value: Expr::value(number("1")),
|
limit: None,
|
||||||
|
offset: Some(Offset {
|
||||||
|
value: Expr::value(number("2")),
|
||||||
rows: OffsetRows::None,
|
rows: OffsetRows::None,
|
||||||
})
|
}),
|
||||||
);
|
limit_by: vec![],
|
||||||
|
};
|
||||||
|
let ast = dialects.verified_query("SELECT 'foo' OFFSET 2");
|
||||||
|
assert_eq!(ast.limit_clause, Some(expected_limit_clause));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -8273,13 +8315,15 @@ 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!(
|
let expected_limit_clause = Some(LimitClause::LimitOffset {
|
||||||
ast.offset,
|
limit: None,
|
||||||
Some(Offset {
|
offset: Some(Offset {
|
||||||
value: Expr::value(number("2")),
|
value: Expr::value(number("2")),
|
||||||
rows: OffsetRows::Rows,
|
rows: OffsetRows::Rows,
|
||||||
})
|
}),
|
||||||
);
|
limit_by: vec![],
|
||||||
|
});
|
||||||
|
assert_eq!(ast.limit_clause, expected_limit_clause);
|
||||||
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",
|
||||||
|
@ -8295,24 +8339,20 @@ 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!(
|
let expected_limit_clause = &Some(LimitClause::LimitOffset {
|
||||||
ast.offset,
|
limit: None,
|
||||||
Some(Offset {
|
offset: Some(Offset {
|
||||||
value: Expr::value(number("2")),
|
value: Expr::value(number("2")),
|
||||||
rows: OffsetRows::Rows,
|
rows: OffsetRows::Rows,
|
||||||
})
|
}),
|
||||||
);
|
limit_by: vec![],
|
||||||
|
});
|
||||||
|
assert_eq!(&ast.limit_clause, expected_limit_clause);
|
||||||
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!(
|
assert_eq!(&subquery.limit_clause, expected_limit_clause);
|
||||||
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"),
|
||||||
|
@ -9358,9 +9398,7 @@ fn parse_merge() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -9678,21 +9716,18 @@ fn test_placeholder() {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "SELECT * FROM student LIMIT $1 OFFSET $2";
|
let ast = dialects.verified_query("SELECT * FROM student LIMIT $1 OFFSET $2");
|
||||||
let ast = dialects.verified_query(sql);
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
assert_eq!(
|
limit: Some(Expr::Value(
|
||||||
ast.limit,
|
(Value::Placeholder("$1".into())).with_empty_span(),
|
||||||
Some(Expr::Value(
|
)),
|
||||||
(Value::Placeholder("$1".into())).with_empty_span()
|
offset: Some(Offset {
|
||||||
))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
ast.offset,
|
|
||||||
Some(Offset {
|
|
||||||
value: Expr::Value((Value::Placeholder("$2".into())).with_empty_span()),
|
value: Expr::Value((Value::Placeholder("$2".into())).with_empty_span()),
|
||||||
rows: OffsetRows::None,
|
rows: OffsetRows::None,
|
||||||
}),
|
}),
|
||||||
);
|
limit_by: vec![],
|
||||||
|
};
|
||||||
|
assert_eq!(ast.limit_clause, Some(expected_limit_clause));
|
||||||
|
|
||||||
let dialects = TestedDialects::new(vec![
|
let dialects = TestedDialects::new(vec![
|
||||||
Box::new(GenericDialect {}),
|
Box::new(GenericDialect {}),
|
||||||
|
@ -9772,40 +9807,34 @@ fn verified_expr(query: &str) -> Expr {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_offset_and_limit() {
|
fn parse_offset_and_limit() {
|
||||||
let sql = "SELECT foo FROM bar LIMIT 1 OFFSET 2";
|
let sql = "SELECT foo FROM bar LIMIT 1 OFFSET 2";
|
||||||
let expect = Some(Offset {
|
let expected_limit_clause = Some(LimitClause::LimitOffset {
|
||||||
value: Expr::value(number("2")),
|
limit: Some(Expr::value(number("1"))),
|
||||||
rows: OffsetRows::None,
|
offset: Some(Offset {
|
||||||
|
value: Expr::value(number("2")),
|
||||||
|
rows: OffsetRows::None,
|
||||||
|
}),
|
||||||
|
limit_by: vec![],
|
||||||
});
|
});
|
||||||
let ast = verified_query(sql);
|
let ast = verified_query(sql);
|
||||||
assert_eq!(ast.offset, expect);
|
assert_eq!(ast.limit_clause, expected_limit_clause);
|
||||||
assert_eq!(ast.limit, Some(Expr::value(number("1"))));
|
|
||||||
|
|
||||||
// different order is OK
|
// different order is OK
|
||||||
one_statement_parses_to("SELECT foo FROM bar OFFSET 2 LIMIT 1", sql);
|
one_statement_parses_to("SELECT foo FROM bar OFFSET 2 LIMIT 1", sql);
|
||||||
|
|
||||||
// mysql syntax is ok for some dialects
|
// mysql syntax is ok for some dialects
|
||||||
TestedDialects::new(vec![
|
all_dialects_where(|d| d.supports_limit_comma())
|
||||||
Box::new(GenericDialect {}),
|
.verified_query("SELECT foo FROM bar LIMIT 2, 1");
|
||||||
Box::new(MySqlDialect {}),
|
|
||||||
Box::new(SQLiteDialect {}),
|
|
||||||
Box::new(ClickHouseDialect {}),
|
|
||||||
])
|
|
||||||
.one_statement_parses_to("SELECT foo FROM bar LIMIT 2, 1", sql);
|
|
||||||
|
|
||||||
// expressions are allowed
|
// expressions are allowed
|
||||||
let sql = "SELECT foo FROM bar LIMIT 1 + 2 OFFSET 3 * 4";
|
let sql = "SELECT foo FROM bar LIMIT 1 + 2 OFFSET 3 * 4";
|
||||||
let ast = verified_query(sql);
|
let ast = verified_query(sql);
|
||||||
assert_eq!(
|
let expected_limit_clause = LimitClause::LimitOffset {
|
||||||
ast.limit,
|
limit: Some(Expr::BinaryOp {
|
||||||
Some(Expr::BinaryOp {
|
|
||||||
left: Box::new(Expr::value(number("1"))),
|
left: Box::new(Expr::value(number("1"))),
|
||||||
op: BinaryOperator::Plus,
|
op: BinaryOperator::Plus,
|
||||||
right: Box::new(Expr::value(number("2"))),
|
right: Box::new(Expr::value(number("2"))),
|
||||||
}),
|
}),
|
||||||
);
|
offset: Some(Offset {
|
||||||
assert_eq!(
|
|
||||||
ast.offset,
|
|
||||||
Some(Offset {
|
|
||||||
value: Expr::BinaryOp {
|
value: Expr::BinaryOp {
|
||||||
left: Box::new(Expr::value(number("3"))),
|
left: Box::new(Expr::value(number("3"))),
|
||||||
op: BinaryOperator::Multiply,
|
op: BinaryOperator::Multiply,
|
||||||
|
@ -9813,7 +9842,12 @@ fn parse_offset_and_limit() {
|
||||||
},
|
},
|
||||||
rows: OffsetRows::None,
|
rows: OffsetRows::None,
|
||||||
}),
|
}),
|
||||||
);
|
limit_by: vec![],
|
||||||
|
};
|
||||||
|
assert_eq!(ast.limit_clause, Some(expected_limit_clause),);
|
||||||
|
|
||||||
|
// OFFSET without LIMIT
|
||||||
|
verified_stmt("SELECT foo FROM bar OFFSET 2");
|
||||||
|
|
||||||
// Can't repeat OFFSET / LIMIT
|
// Can't repeat OFFSET / LIMIT
|
||||||
let res = parse_sql_statements("SELECT foo FROM bar OFFSET 2 OFFSET 2");
|
let res = parse_sql_statements("SELECT foo FROM bar OFFSET 2 OFFSET 2");
|
||||||
|
@ -11227,9 +11261,7 @@ fn parse_unload() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
with: None,
|
with: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -12400,9 +12432,7 @@ fn test_extract_seconds_ok() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -14265,11 +14295,9 @@ fn test_select_from_first() {
|
||||||
flavor,
|
flavor,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
limit_by: vec![],
|
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
settings: None,
|
settings: None,
|
||||||
format_clause: None,
|
format_clause: None,
|
||||||
|
|
|
@ -107,9 +107,7 @@ fn parse_create_procedure() {
|
||||||
or_alter: true,
|
or_alter: true,
|
||||||
body: vec![Statement::Query(Box::new(Query {
|
body: vec![Statement::Query(Box::new(Query {
|
||||||
with: None,
|
with: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1161,9 +1159,7 @@ fn parse_substring_in_select() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1264,9 +1260,7 @@ fn parse_mssql_declare() {
|
||||||
}),
|
}),
|
||||||
Statement::Query(Box::new(Query {
|
Statement::Query(Box::new(Query {
|
||||||
with: None,
|
with: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
|
|
@ -1107,9 +1107,7 @@ fn parse_escaped_quote_identifiers_with_escape() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1161,9 +1159,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1209,9 +1205,7 @@ fn parse_escaped_backticks_with_escape() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1261,9 +1255,7 @@ fn parse_escaped_backticks_with_no_escape() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1438,9 +1430,7 @@ fn parse_simple_insert() {
|
||||||
]
|
]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1488,9 +1478,7 @@ fn parse_ignore_insert() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1538,9 +1526,7 @@ fn parse_priority_insert() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1585,9 +1571,7 @@ fn parse_priority_insert() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1634,9 +1618,7 @@ fn parse_insert_as() {
|
||||||
)]]
|
)]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1698,9 +1680,7 @@ fn parse_insert_as() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1749,9 +1729,7 @@ fn parse_replace_insert() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1791,9 +1769,7 @@ fn parse_empty_row_insert() {
|
||||||
rows: vec![vec![], vec![]]
|
rows: vec![vec![], vec![]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -1857,9 +1833,7 @@ fn parse_insert_with_on_duplicate_update() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -2596,9 +2570,7 @@ fn parse_substring_in_select() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -2737,10 +2709,8 @@ fn parse_set_names() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_limit_my_sql_syntax() {
|
fn parse_limit_my_sql_syntax() {
|
||||||
mysql_and_generic().one_statement_parses_to(
|
mysql_and_generic().verified_stmt("SELECT id, fname, lname FROM customer LIMIT 10 OFFSET 5");
|
||||||
"SELECT id, fname, lname FROM customer LIMIT 5, 10",
|
mysql_and_generic().verified_stmt("SELECT id, fname, lname FROM customer LIMIT 5, 10");
|
||||||
"SELECT id, fname, lname FROM customer LIMIT 10 OFFSET 5",
|
|
||||||
);
|
|
||||||
mysql_and_generic().verified_stmt("SELECT * FROM user LIMIT ? OFFSET ?");
|
mysql_and_generic().verified_stmt("SELECT * FROM user LIMIT ? OFFSET ?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2903,9 +2873,7 @@ fn parse_hex_string_introducer() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
|
|
@ -1319,9 +1319,7 @@ fn parse_copy_to() {
|
||||||
flavor: SelectFlavor::Standard,
|
flavor: SelectFlavor::Standard,
|
||||||
}))),
|
}))),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -2955,9 +2953,7 @@ fn parse_array_subquery_expr() {
|
||||||
}))),
|
}))),
|
||||||
}),
|
}),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -4747,9 +4743,7 @@ fn test_simple_postgres_insert_with_alias() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -4820,9 +4814,7 @@ fn test_simple_postgres_insert_with_alias() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
@ -4891,9 +4883,7 @@ fn test_simple_insert_with_quoted_alias() {
|
||||||
]]
|
]]
|
||||||
})),
|
})),
|
||||||
order_by: None,
|
order_by: None,
|
||||||
limit: None,
|
limit_clause: None,
|
||||||
limit_by: vec![],
|
|
||||||
offset: None,
|
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue