Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solve stack overflow on RecursionLimitExceeded during debug building #1171

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 64 additions & 33 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ impl<'a> Parser<'a> {
Keyword::ANALYZE => Ok(self.parse_analyze()?),
Keyword::SELECT | Keyword::WITH | Keyword::VALUES => {
self.prev_token();
Ok(Statement::Query(Box::new(self.parse_query()?)))
Ok(Statement::Query(self.parse_boxed_query()?))
}
Keyword::TRUNCATE => Ok(self.parse_truncate()?),
Keyword::ATTACH => Ok(self.parse_attach_database()?),
Expand Down Expand Up @@ -530,7 +530,7 @@ impl<'a> Parser<'a> {
},
Token::LParen => {
self.prev_token();
Ok(Statement::Query(Box::new(self.parse_query()?)))
Ok(Statement::Query(self.parse_boxed_query()?))
}
_ => self.expected("an SQL statement", next_token),
}
Expand Down Expand Up @@ -1084,7 +1084,7 @@ impl<'a> Parser<'a> {
let expr =
if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) {
self.prev_token();
Expr::Subquery(Box::new(self.parse_query()?))
Expr::Subquery(self.parse_boxed_query()?)
} else {
let exprs = self.parse_comma_separated(Parser::parse_expr)?;
match exprs.len() {
Expand Down Expand Up @@ -1465,7 +1465,7 @@ impl<'a> Parser<'a> {
self.expect_token(&Token::LParen)?;
let exists_node = Expr::Exists {
negated,
subquery: Box::new(self.parse_query()?),
subquery: self.parse_boxed_query()?,
};
self.expect_token(&Token::RParen)?;
Ok(exists_node)
Expand Down Expand Up @@ -1654,9 +1654,9 @@ impl<'a> Parser<'a> {

// Parses an array constructed from a subquery
pub fn parse_array_subquery(&mut self) -> Result<Expr, ParserError> {
let query = self.parse_query()?;
let query = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
Ok(Expr::ArraySubquery(Box::new(query)))
Ok(Expr::ArraySubquery(query))
}

/// Parse a SQL LISTAGG expression, e.g. `LISTAGG(...) WITHIN GROUP (ORDER BY ...)`.
Expand Down Expand Up @@ -2554,7 +2554,7 @@ impl<'a> Parser<'a> {
self.prev_token();
Expr::InSubquery {
expr: Box::new(expr),
subquery: Box::new(self.parse_query()?),
subquery: self.parse_boxed_query()?,
negated,
}
} else {
Expand Down Expand Up @@ -3637,7 +3637,7 @@ impl<'a> Parser<'a> {
}

self.expect_keyword(Keyword::AS)?;
let query = Box::new(self.parse_query()?);
let query = self.parse_boxed_query()?;
// Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here.

let with_no_schema_binding = dialect_of!(self is RedshiftSqlDialect | GenericDialect)
Expand Down Expand Up @@ -4032,7 +4032,7 @@ impl<'a> Parser<'a> {

self.expect_keyword(Keyword::FOR)?;

let query = Some(Box::new(self.parse_query()?));
let query = Some(self.parse_boxed_query()?);

Ok(Statement::Declare {
stmts: vec![Declare {
Expand Down Expand Up @@ -4126,7 +4126,7 @@ impl<'a> Parser<'a> {
match self.peek_token().token {
Token::Word(w) if w.keyword == Keyword::SELECT => (
Some(DeclareType::Cursor),
Some(Box::new(self.parse_query()?)),
Some(self.parse_boxed_query()?),
None,
None,
),
Expand Down Expand Up @@ -4650,7 +4650,7 @@ impl<'a> Parser<'a> {

// Parse optional `AS ( query )`
let query = if self.parse_keyword(Keyword::AS) {
Some(Box::new(self.parse_query()?))
Some(self.parse_boxed_query()?)
} else {
None
};
Expand Down Expand Up @@ -5646,7 +5646,7 @@ impl<'a> Parser<'a> {
let with_options = self.parse_options(Keyword::WITH)?;

self.expect_keyword(Keyword::AS)?;
let query = Box::new(self.parse_query()?);
let query = self.parse_boxed_query()?;

Ok(Statement::AlterView {
name,
Expand Down Expand Up @@ -5686,7 +5686,7 @@ impl<'a> Parser<'a> {
pub fn parse_copy(&mut self) -> Result<Statement, ParserError> {
let source;
if self.consume_token(&Token::LParen) {
source = CopySource::Query(Box::new(self.parse_query()?));
source = CopySource::Query(self.parse_boxed_query()?);
self.expect_token(&Token::RParen)?;
} else {
let table_name = self.parse_object_name(false)?;
Expand Down Expand Up @@ -6910,6 +6910,15 @@ impl<'a> Parser<'a> {
}
}

/// Call's [`Self::parse_query`] returning a `Box`'ed result.
///
/// This function can be used to reduce the stack size required in debug
/// builds. Instead of `sizeof(Query)` only a pointer (`Box<Query>`)
/// is used.
fn parse_boxed_query(&mut self) -> Result<Box<Query>, ParserError> {
self.parse_query().map(Box::new)
}

/// Parse a query expression, i.e. a `SELECT` statement optionally
/// preceded with some `WITH` CTE declarations and optionally followed
/// by `ORDER BY`. Unlike some other parse_... methods, this one doesn't
Expand All @@ -6924,13 +6933,10 @@ impl<'a> Parser<'a> {
} else {
None
};

if self.parse_keyword(Keyword::INSERT) {
let insert = self.parse_insert()?;

Ok(Query {
with,
body: Box::new(SetExpr::Insert(insert)),
body: self.parse_insert_setexpr_boxed()?,
limit: None,
limit_by: vec![],
order_by: vec![],
Expand All @@ -6940,10 +6946,9 @@ impl<'a> Parser<'a> {
for_clause: None,
})
} else if self.parse_keyword(Keyword::UPDATE) {
let update = self.parse_update()?;
Ok(Query {
with,
body: Box::new(SetExpr::Update(update)),
body: self.parse_update_setexpr_boxed()?,
limit: None,
limit_by: vec![],
order_by: vec![],
Expand All @@ -6953,7 +6958,7 @@ impl<'a> Parser<'a> {
for_clause: None,
})
} else {
let body = Box::new(self.parse_query_body(0)?);
let body = self.parse_boxed_query_body(0)?;

let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
self.parse_comma_separated(Parser::parse_order_by_expr)?
Expand Down Expand Up @@ -7143,7 +7148,7 @@ impl<'a> Parser<'a> {
}
}
self.expect_token(&Token::LParen)?;
let query = Box::new(self.parse_query()?);
let query = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
let alias = TableAlias {
name,
Expand All @@ -7167,7 +7172,7 @@ impl<'a> Parser<'a> {
}
}
self.expect_token(&Token::LParen)?;
let query = Box::new(self.parse_query()?);
let query = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
let alias = TableAlias { name, columns };
Cte {
Expand All @@ -7183,6 +7188,15 @@ impl<'a> Parser<'a> {
Ok(cte)
}

/// Call's [`Self::parse_query_body`] returning a `Box`'ed result.
///
/// This function can be used to reduce the stack size required in debug
/// builds. Instead of `sizeof(QueryBody)` only a pointer (`Box<QueryBody>`)
/// is used.
fn parse_boxed_query_body(&mut self, precedence: u8) -> Result<Box<SetExpr>, ParserError> {
self.parse_query_body(precedence).map(Box::new)
}

/// Parse a "query body", which is an expression with roughly the
/// following grammar:
/// ```sql
Expand All @@ -7191,16 +7205,19 @@ impl<'a> Parser<'a> {
/// subquery ::= query_body [ order_by_limit ]
/// set_operation ::= query_body { 'UNION' | 'EXCEPT' | 'INTERSECT' } [ 'ALL' ] query_body
/// ```
///
/// If you need `Box<SetExpr>` then maybe there is sense to use `parse_boxed_query_body`
/// due to prevent stack overflow in debug building(to reserve less memory on stack).
pub fn parse_query_body(&mut self, precedence: u8) -> Result<SetExpr, ParserError> {
// We parse the expression using a Pratt parser, as in `parse_expr()`.
// Start by parsing a restricted SELECT or a `(subquery)`:
let mut expr = if self.parse_keyword(Keyword::SELECT) {
SetExpr::Select(Box::new(self.parse_select()?))
SetExpr::Select(self.parse_select().map(Box::new)?)
} else if self.consume_token(&Token::LParen) {
// CTEs are not allowed here, but the parser currently accepts them
let subquery = self.parse_query()?;
let subquery = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
SetExpr::Query(Box::new(subquery))
SetExpr::Query(subquery)
} else if self.parse_keyword(Keyword::VALUES) {
let is_mysql = dialect_of!(self is MySqlDialect);
SetExpr::Values(self.parse_values(is_mysql)?)
Expand Down Expand Up @@ -7233,7 +7250,7 @@ impl<'a> Parser<'a> {
left: Box::new(expr),
op: op.unwrap(),
set_quantifier,
right: Box::new(self.parse_query_body(next_precedence)?),
right: self.parse_boxed_query_body(next_precedence)?,
};
}

Expand Down Expand Up @@ -8147,7 +8164,7 @@ impl<'a> Parser<'a> {
&mut self,
lateral: IsLateral,
) -> Result<TableFactor, ParserError> {
let subquery = Box::new(self.parse_query()?);
let subquery = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
Ok(TableFactor::Derived {
Expand Down Expand Up @@ -8395,6 +8412,13 @@ impl<'a> Parser<'a> {
Ok(insert.clone())
}

/// Parse an INSERT statement, returning a `Box`ed SetExpr
///
/// This is used to reduce the size of the stack frames in debug builds
fn parse_insert_setexpr_boxed(&mut self) -> Result<Box<SetExpr>, ParserError> {
Ok(Box::new(SetExpr::Insert(self.parse_insert()?)))
}

/// Parse an INSERT statement
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
let or = if !dialect_of!(self is SQLiteDialect) {
Expand Down Expand Up @@ -8445,7 +8469,7 @@ impl<'a> Parser<'a> {
} else {
None
};
let source = Box::new(self.parse_query()?);
let source = self.parse_boxed_query()?;
Ok(Statement::Directory {
local,
path,
Expand Down Expand Up @@ -8478,7 +8502,7 @@ impl<'a> Parser<'a> {
// Hive allows you to specify columns after partitions as well if you want.
let after_columns = self.parse_parenthesized_column_list(Optional, false)?;

let source = Some(Box::new(self.parse_query()?));
let source = Some(self.parse_boxed_query()?);

(columns, partitioned, after_columns, source)
};
Expand Down Expand Up @@ -8581,6 +8605,13 @@ impl<'a> Parser<'a> {
}
}

/// Parse an UPDATE statement, returning a `Box`ed SetExpr
///
/// This is used to reduce the size of the stack frames in debug builds
fn parse_update_setexpr_boxed(&mut self) -> Result<Box<SetExpr>, ParserError> {
Ok(Box::new(SetExpr::Update(self.parse_update()?)))
}

pub fn parse_update(&mut self) -> Result<Statement, ParserError> {
let table = self.parse_table_and_joins()?;
self.expect_keyword(Keyword::SET)?;
Expand Down Expand Up @@ -8684,11 +8715,11 @@ impl<'a> Parser<'a> {
.is_some()
{
self.prev_token();
let subquery = self.parse_query()?;
let subquery = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
return Ok((
vec![FunctionArg::Unnamed(FunctionArgExpr::from(Expr::Subquery(
Box::new(subquery),
subquery,
)))],
vec![],
));
Expand Down Expand Up @@ -9202,7 +9233,7 @@ impl<'a> Parser<'a> {

pub fn parse_unload(&mut self) -> Result<Statement, ParserError> {
self.expect_token(&Token::LParen)?;
let query = self.parse_query()?;
let query = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;

self.expect_keyword(Keyword::TO)?;
Expand All @@ -9211,7 +9242,7 @@ impl<'a> Parser<'a> {
let with_options = self.parse_options(Keyword::WITH)?;

Ok(Statement::Unload {
query: Box::new(query),
query,
to,
with: with_options,
})
Expand Down
Loading