Skip to content

Commit 7c4eac3

Browse files
authored
recursive protection for parse_subexpr (#2282)
1 parent 1097a0d commit 7c4eac3

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

src/parser/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,7 @@ impl<'a> Parser<'a> {
13861386
}
13871387

13881388
/// Parse tokens until the precedence changes.
1389+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
13891390
pub fn parse_subexpr(&mut self, precedence: u8) -> Result<Expr, ParserError> {
13901391
let _guard = self.recursion_counter.try_decrease()?;
13911392
debug!("parsing expr");

tests/sqlparser_common.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15687,6 +15687,34 @@ fn overflow() {
1568715687
let statement = statements.pop().unwrap();
1568815688
assert_eq!(statement.to_string(), sql);
1568915689
}
15690+
15691+
#[test]
15692+
fn parse_deeply_nested_boolean_expr_does_not_stackoverflow() {
15693+
fn build_nested_expr(depth: usize) -> String {
15694+
if depth == 0 {
15695+
return "x = 1".to_string();
15696+
}
15697+
format!(
15698+
"({} OR {} AND ({}))",
15699+
build_nested_expr(0),
15700+
build_nested_expr(0),
15701+
build_nested_expr(depth - 1)
15702+
)
15703+
}
15704+
15705+
let depth = 200;
15706+
let where_clause = build_nested_expr(depth);
15707+
let sql = format!("SELECT pk FROM tab0 WHERE {where_clause}");
15708+
15709+
let mut statements = Parser::new(&GenericDialect {})
15710+
.try_with_sql(&sql)
15711+
.expect("tokenize to work")
15712+
.with_recursion_limit(depth * 10)
15713+
.parse_statements()
15714+
.unwrap();
15715+
let statement = statements.pop().unwrap();
15716+
assert_eq!(statement.to_string(), sql);
15717+
}
1569015718
#[test]
1569115719
fn parse_select_without_projection() {
1569215720
let dialects = all_dialects_where(|d| d.supports_empty_projections());

0 commit comments

Comments
 (0)