Skip to content

Commit 4acb645

Browse files
Merge main into PR apache#2559
2 parents e5b587c + 1ba145b commit 4acb645

5 files changed

Lines changed: 200 additions & 16 deletions

File tree

‎src/dialect/mod.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,6 +1976,13 @@ pub trait Dialect: Debug + Any {
19761976
false
19771977
}
19781978

1979+
/// Returns true if the dialect accepts `==` as an alternative to `=` in `UPDATE SET` assignments.
1980+
///
1981+
/// See <https://www.sqlite.org/lang_update.html>
1982+
fn supports_double_eq_assignment(&self) -> bool {
1983+
false
1984+
}
1985+
19791986
/// Returns true if the dialect supports `CAST(expr AS)` with an empty type name.
19801987
///
19811988
/// Example:

‎src/dialect/sqlite.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ impl Dialect for SQLiteDialect {
142142
true
143143
}
144144

145+
fn supports_double_eq_assignment(&self) -> bool {
146+
true
147+
}
148+
145149
fn supports_string_literal_column_names(&self) -> bool {
146150
true
147151
}

‎src/parser/mod.rs‎

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ pub struct Parser<'a> {
355355
/// `parse_table_factor`. See [`Parser::parse_table_factor`] for the 2^N
356356
/// pattern this guards.
357357
failed_derived_table_factor_positions: BTreeSet<usize>,
358+
/// Cached failures from the speculative subquery arm of [`Parser::parse_in`],
359+
/// which `IN` lists nested in a leading subquery would otherwise retry at
360+
/// every level, taking 2^N time. Maps the arm's start to where a parsed
361+
/// query lacked its `)`.
362+
failed_in_subquery_positions: BTreeMap<usize, Option<usize>>,
358363
}
359364

360365
/// Copy marker for a [`ParserError`] cached by the `parse_prefix` failure
@@ -401,6 +406,7 @@ impl<'a> Parser<'a> {
401406
failed_prefix_positions: BTreeMap::new(),
402407
failed_reserved_word_prefix_positions: BTreeMap::new(),
403408
failed_derived_table_factor_positions: BTreeSet::new(),
409+
failed_in_subquery_positions: BTreeMap::new(),
404410
}
405411
}
406412

@@ -469,6 +475,7 @@ impl<'a> Parser<'a> {
469475
self.failed_prefix_positions.clear();
470476
self.failed_reserved_word_prefix_positions.clear();
471477
self.failed_derived_table_factor_positions.clear();
478+
self.failed_in_subquery_positions.clear();
472479
self
473480
}
474481

@@ -4464,24 +4471,51 @@ impl<'a> Parser<'a> {
44644471
});
44654472
}
44664473
self.expect_token(&Token::LParen)?;
4467-
let in_op = match self.maybe_parse(|p| p.parse_query())? {
4468-
Some(subquery) => Expr::InSubquery {
4469-
expr: Box::new(expr),
4470-
subquery,
4471-
negated,
4472-
},
4473-
None => Expr::InList {
4474+
// A leading query is the whole subquery only when `)` follows it,
4475+
// otherwise it is the first item of a list, as in `IN ((SELECT 1), 2)`.
4476+
let start = self.index;
4477+
// Where a parsed query lacked its `)`. Reported if the list fails no
4478+
// further in, since that error would sit inside the query.
4479+
let missing_rparen = match self.failed_in_subquery_positions.get(&start) {
4480+
Some(&cached) => cached,
4481+
None => {
4482+
let mut missing_rparen = None;
4483+
if let Some(subquery) = self.maybe_parse(|p| p.parse_query())? {
4484+
if self.consume_token(&Token::RParen) {
4485+
return Ok(Expr::InSubquery {
4486+
expr: Box::new(expr),
4487+
subquery,
4488+
negated,
4489+
});
4490+
}
4491+
missing_rparen = Some(self.index);
4492+
self.index = start;
4493+
}
4494+
self.failed_in_subquery_positions
4495+
.insert(start, missing_rparen);
4496+
missing_rparen
4497+
}
4498+
};
4499+
let list = if self.dialect.supports_in_empty_list() {
4500+
self.parse_comma_separated0(Parser::parse_expr, Token::RParen)
4501+
} else {
4502+
self.parse_comma_separated(Parser::parse_expr)
4503+
};
4504+
match list.and_then(|list| self.expect_token(&Token::RParen).map(|_| list)) {
4505+
Ok(list) => Ok(Expr::InList {
44744506
expr: Box::new(expr),
4475-
list: if self.dialect.supports_in_empty_list() {
4476-
self.parse_comma_separated0(Parser::parse_expr, Token::RParen)?
4477-
} else {
4478-
self.parse_comma_separated(Parser::parse_expr)?
4479-
},
4507+
list,
44804508
negated,
4509+
}),
4510+
Err(ParserError::RecursionLimitExceeded) => Err(ParserError::RecursionLimitExceeded),
4511+
Err(e) => match missing_rparen {
4512+
Some(index) if self.index <= index => {
4513+
self.index = index;
4514+
self.expected_ref(")", self.peek_token_ref())
4515+
}
4516+
_ => Err(e),
44814517
},
4482-
};
4483-
self.expect_token(&Token::RParen)?;
4484-
Ok(in_op)
4518+
}
44854519
}
44864520

44874521
/// Parses `BETWEEN <low> AND <high>`, assuming the `BETWEEN` keyword was already consumed.
@@ -18968,7 +19002,13 @@ impl<'a> Parser<'a> {
1896819002
/// Parse a `var = expr` assignment, used in an UPDATE statement
1896919003
pub fn parse_assignment(&mut self) -> Result<Assignment, ParserError> {
1897019004
let target = self.parse_assignment_target()?;
18971-
self.expect_token(&Token::Eq)?;
19005+
if self.dialect.supports_double_eq_assignment() {
19006+
if !self.consume_token(&Token::Eq) && !self.consume_token(&Token::DoubleEq) {
19007+
return self.expected("= or == (for assignment)", self.peek_token());
19008+
}
19009+
} else {
19010+
self.expect_token(&Token::Eq)?;
19011+
}
1897219012
let value = self.parse_expr()?;
1897319013
Ok(Assignment { target, value })
1897419014
}

‎tests/sqlparser_common.rs‎

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20304,6 +20304,122 @@ fn parse_placeholder_disallows_quoted_ident() {
2030420304
);
2030520305
}
2030620306

20307+
#[test]
20308+
fn parse_in_list_leading_subquery() {
20309+
let x = || Box::new(Expr::Identifier(Ident::new("x")));
20310+
let subquery = || Expr::Subquery(Box::new(verified_query("SELECT a FROM u")));
20311+
20312+
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u), 1)");
20313+
assert_eq!(
20314+
Expr::InList {
20315+
expr: x(),
20316+
list: vec![subquery(), Expr::value(number("1"))],
20317+
negated: false,
20318+
},
20319+
select.selection.unwrap()
20320+
);
20321+
20322+
let select = verified_only_select("SELECT * FROM t WHERE x NOT IN (((SELECT a FROM u)), 1)");
20323+
assert_eq!(
20324+
Expr::InList {
20325+
expr: x(),
20326+
list: vec![Expr::Nested(Box::new(subquery())), Expr::value(number("1"))],
20327+
negated: true,
20328+
},
20329+
select.selection.unwrap()
20330+
);
20331+
20332+
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u) + 1, 2)");
20333+
assert_eq!(
20334+
Expr::InList {
20335+
expr: x(),
20336+
list: vec![
20337+
Expr::BinaryOp {
20338+
left: Box::new(subquery()),
20339+
op: BinaryOperator::Plus,
20340+
right: Box::new(Expr::value(number("1"))),
20341+
},
20342+
Expr::value(number("2"))
20343+
],
20344+
negated: false,
20345+
},
20346+
select.selection.unwrap()
20347+
);
20348+
20349+
let select = pg_and_generic()
20350+
.verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u)[1], 2)");
20351+
assert_matches!(
20352+
select.selection,
20353+
Some(Expr::InList { ref list, .. })
20354+
if matches!(&list[0], Expr::CompoundFieldAccess { root, .. } if **root == subquery())
20355+
);
20356+
20357+
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u))");
20358+
assert_eq!(
20359+
Expr::InSubquery {
20360+
expr: x(),
20361+
subquery: Box::new(verified_query("(SELECT a FROM u)")),
20362+
negated: false,
20363+
},
20364+
select.selection.unwrap()
20365+
);
20366+
20367+
assert_eq!(
20368+
ParserError::ParserError("Expected: ), found: 1".to_string()),
20369+
parse_sql_statements("SELECT * FROM t WHERE x IN ((SELECT a FROM u) 1)").unwrap_err()
20370+
);
20371+
assert_eq!(
20372+
ParserError::ParserError("Expected: ), found: EOF".to_string()),
20373+
parse_sql_statements("SELECT * FROM t WHERE x IN (SELECT a FROM u").unwrap_err()
20374+
);
20375+
assert_eq!(
20376+
ParserError::ParserError("Expected: ), found: EOF".to_string()),
20377+
parse_sql_statements("SELECT * FROM t WHERE x IN ((SELECT a FROM u), 1").unwrap_err()
20378+
);
20379+
// The inner `IN` is read again for the outer list after its subquery
20380+
// arm was cached as failed.
20381+
assert_eq!(
20382+
ParserError::ParserError("Expected: ), found: EOF".to_string()),
20383+
parse_sql_statements("SELECT * FROM t WHERE x IN ((SELECT 1 FROM u WHERE y IN (SELECT 2")
20384+
.unwrap_err()
20385+
);
20386+
}
20387+
20388+
/// Hang guard for `IN` lists nested in the leading subquery of an enclosing
20389+
/// `IN` list, where every level is first tried as the whole subquery and then
20390+
/// read again as a list item.
20391+
#[test]
20392+
fn parse_in_list_nested_leading_subquery_no_exponential_blowup() {
20393+
use std::sync::mpsc;
20394+
use std::thread;
20395+
use std::time::Duration;
20396+
20397+
for level in [
20398+
"x IN ((SELECT {}), 1)",
20399+
"x IN ((SELECT {}) + 1, 2)",
20400+
"x IN (((SELECT {}) + 1), 2)",
20401+
] {
20402+
let sql = format!(
20403+
"SELECT {}",
20404+
(0..30).fold("1".to_string(), |inner, _| level.replace("{}", &inner))
20405+
);
20406+
20407+
let (tx, rx) = mpsc::channel();
20408+
thread::spawn(move || {
20409+
let result = Parser::new(&GenericDialect {})
20410+
.with_recursion_limit(1000)
20411+
.try_with_sql(&sql)
20412+
.and_then(|mut parser| parser.parse_statements());
20413+
let _ = tx.send(result.is_ok());
20414+
});
20415+
20416+
let parsed = rx
20417+
.recv_timeout(Duration::from_secs(5))
20418+
.expect("parser should handle this quickly, not loop exponentially");
20419+
assert!(parsed, "{level} nested 30 deep should parse");
20420+
}
20421+
}
20422+
2030720423
#[test]
2030820424
fn parse_bang_not_renders_apart_from_operand() {
2030920425
let dialects = all_dialects_where(|d| d.supports_bang_not_operator());

‎tests/sqlparser_sqlite.rs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,23 @@ fn parse_pattern_operators_bind_at_like_precedence() {
957957
}
958958
}
959959

960+
#[test]
961+
fn parse_update_set_double_eq() {
962+
// SQLite treats `==` as `=` in all positions, including SET assignments.
963+
sqlite().one_statement_parses_to("UPDATE t SET a == 1", "UPDATE t SET a = 1");
964+
sqlite().one_statement_parses_to("UPDATE t SET a == 1, b == 2", "UPDATE t SET a = 1, b = 2");
965+
// `=` still works
966+
sqlite().verified_stmt("UPDATE t SET a = 1");
967+
// Other dialects reject `==` in SET
968+
let res = ParserError::ParserError("Expected: =, found: ==".to_string());
969+
assert_eq!(
970+
all_dialects_except(|d| d.supports_double_eq_assignment())
971+
.parse_sql_statements("UPDATE t SET a == 1")
972+
.unwrap_err(),
973+
res,
974+
);
975+
}
976+
960977
#[test]
961978
fn parse_create_table_string_column_names() {
962979
sqlite().verified_stmt("CREATE TABLE t ('a')");

0 commit comments

Comments
 (0)