diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 235c1f1df..07893d98e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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()?), @@ -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), } @@ -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() { @@ -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) @@ -1654,9 +1654,9 @@ impl<'a> Parser<'a> { // Parses an array constructed from a subquery pub fn parse_array_subquery(&mut self) -> Result { - 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 ...)`. @@ -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 { @@ -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) @@ -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 { @@ -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, ), @@ -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 }; @@ -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, @@ -5686,7 +5686,7 @@ impl<'a> Parser<'a> { pub fn parse_copy(&mut self) -> Result { 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)?; @@ -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`) + /// is used. + fn parse_boxed_query(&mut self) -> Result, 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 @@ -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![], @@ -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![], @@ -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)? @@ -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, @@ -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 { @@ -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`) + /// is used. + fn parse_boxed_query_body(&mut self, precedence: u8) -> Result, ParserError> { + self.parse_query_body(precedence).map(Box::new) + } + /// Parse a "query body", which is an expression with roughly the /// following grammar: /// ```sql @@ -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` 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 { // 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)?) @@ -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)?, }; } @@ -8147,7 +8164,7 @@ impl<'a> Parser<'a> { &mut self, lateral: IsLateral, ) -> Result { - 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 { @@ -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, ParserError> { + Ok(Box::new(SetExpr::Insert(self.parse_insert()?))) + } + /// Parse an INSERT statement pub fn parse_insert(&mut self) -> Result { let or = if !dialect_of!(self is SQLiteDialect) { @@ -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, @@ -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) }; @@ -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, ParserError> { + Ok(Box::new(SetExpr::Update(self.parse_update()?))) + } + pub fn parse_update(&mut self) -> Result { let table = self.parse_table_and_joins()?; self.expect_keyword(Keyword::SET)?; @@ -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![], )); @@ -9202,7 +9233,7 @@ impl<'a> Parser<'a> { pub fn parse_unload(&mut self) -> Result { 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)?; @@ -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, })